From 4062dc70fb196e6b85c4ada990d541c01aab6875 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Wed, 16 Mar 2022 15:03:55 +0200 Subject: [PATCH 01/39] Initial export/import API for devices, device profiles, assets, rule chains, dashboards and customers --- .../main/data/upgrade/3.4/schema_update.sql | 28 +++ .../EntitiesExportImportController.java | 99 +++++++++ .../DefaultEntitiesExportImportService.java | 86 ++++++++ .../expimp/EntitiesExportImportService.java | 29 +++ .../expimp/exp/EntityExportService.java | 32 +++ .../expimp/exp/impl/AssetExportService.java | 50 +++++ .../exp/impl/CustomerExportService.java | 50 +++++ .../exp/impl/DashboardExportService.java | 50 +++++ .../expimp/exp/impl/DeviceExportService.java | 53 +++++ .../exp/impl/DeviceProfileExportService.java | 50 +++++ .../exp/impl/RuleChainExportService.java | 51 +++++ .../expimp/imp/EntityImportService.java | 30 +++ .../imp/impl/AbstractEntityImportService.java | 73 +++++++ .../expimp/imp/impl/AssetImportService.java | 62 ++++++ .../imp/impl/CustomerImportService.java | 60 ++++++ .../imp/impl/DashboardImportService.java | 62 ++++++ .../expimp/imp/impl/DeviceImportService.java | 81 ++++++++ .../imp/impl/DeviceProfileImportService.java | 65 ++++++ .../imp/impl/RuleChainImportService.java | 80 ++++++++ .../server/dao/rule/RuleChainService.java | 3 +- .../server/common/data/Customer.java | 6 + .../server/common/data/Dashboard.java | 10 +- .../server/common/data/Device.java | 7 + .../server/common/data/DeviceProfile.java | 3 + .../server/common/data/asset/Asset.java | 7 + .../data/export/EntitiesExportRequest.java | 29 +++ .../data/export/EntitiesExportResponse.java | 29 +++ .../common/data/export/EntityExportData.java | 48 +++++ .../data/export/impl/AssetExportData.java | 33 +++ .../data/export/impl/CustomerExportData.java | 33 +++ .../data/export/impl/DashboardExportData.java | 33 +++ .../data/export/impl/DeviceExportData.java | 35 ++++ .../export/impl/DeviceProfileExportData.java | 33 +++ .../data/export/impl/RuleChainExportData.java | 35 ++++ .../server/common/data/rule/RuleChain.java | 3 + .../server/dao/ExportableEntityDao.java | 36 ++++ .../dao/ExportableEntityRepository.java | 26 +++ .../server/dao/asset/AssetDao.java | 4 +- .../server/dao/customer/CustomerDao.java | 5 +- .../server/dao/dashboard/DashboardDao.java | 3 +- .../server/dao/device/DeviceDao.java | 3 +- .../server/dao/device/DeviceProfileDao.java | 3 +- .../server/dao/model/ModelConstants.java | 2 + .../dao/model/sql/AbstractAssetEntity.java | 11 + .../dao/model/sql/AbstractDeviceEntity.java | 10 + .../server/dao/model/sql/CustomerEntity.java | 9 + .../server/dao/model/sql/DashboardEntity.java | 9 + .../dao/model/sql/DeviceProfileEntity.java | 9 + .../server/dao/model/sql/RuleChainEntity.java | 9 + .../server/dao/rule/BaseRuleChainService.java | 5 + .../server/dao/rule/RuleChainDao.java | 7 +- .../server/dao/sql/asset/AssetRepository.java | 3 +- .../server/dao/sql/asset/JpaAssetDao.java | 16 ++ .../dao/sql/customer/CustomerRepository.java | 3 +- .../dao/sql/customer/JpaCustomerDao.java | 17 ++ .../sql/dashboard/DashboardRepository.java | 4 +- .../dao/sql/dashboard/JpaDashboardDao.java | 18 ++ .../sql/device/DeviceProfileRepository.java | 4 +- .../dao/sql/device/DeviceRepository.java | 3 +- .../server/dao/sql/device/JpaDeviceDao.java | 16 ++ .../dao/sql/device/JpaDeviceProfileDao.java | 17 ++ .../server/dao/sql/rule/JpaRuleChainDao.java | 16 ++ .../dao/sql/rule/RuleChainRepository.java | 3 +- .../main/resources/sql/schema-entities.sql | 188 +++++++++--------- 64 files changed, 1788 insertions(+), 109 deletions(-) create mode 100644 application/src/main/data/upgrade/3.4/schema_update.sql create mode 100644 application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AssetExportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/CustomerExportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DashboardExportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceExportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceProfileExportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/RuleChainExportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/CustomerImportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DashboardImportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceImportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceProfileImportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/RuleChainImportService.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportRequest.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportResponse.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/export/impl/AssetExportData.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/export/impl/CustomerExportData.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DashboardExportData.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceExportData.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceProfileExportData.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/export/impl/RuleChainExportData.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/ExportableEntityRepository.java diff --git a/application/src/main/data/upgrade/3.4/schema_update.sql b/application/src/main/data/upgrade/3.4/schema_update.sql new file mode 100644 index 0000000000..ad307a6576 --- /dev/null +++ b/application/src/main/data/upgrade/3.4/schema_update.sql @@ -0,0 +1,28 @@ +-- +-- 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. +-- + +ALTER TABLE device + ADD COLUMN IF NOT EXISTS external_id UUID; +ALTER TABLE device_profile + ADD COLUMN IF NOT EXISTS external_id UUID; +ALTER TABLE asset + ADD COLUMN IF NOT EXISTS external_id UUID; +ALTER TABLE rule_chain + ADD COLUMN IF NOT EXISTS external_id UUID; +ALTER TABLE dashboard + ADD COLUMN IF NOT EXISTS external_id UUID; +ALTER TABLE customer + ADD COLUMN IF NOT EXISTS external_id UUID; diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java new file mode 100644 index 0000000000..a11a9189f4 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -0,0 +1,99 @@ +/** + * 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.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.EntityType; +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.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +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.service.expimp.EntitiesExportImportService; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@RestController +@RequestMapping("/api/entities") +@TbCoreComponent +@RequiredArgsConstructor +public class EntitiesExportImportController extends BaseController { + + private final EntitiesExportImportService exportImportService; + + + @PostMapping("/export/{entityType}/{entityId}") + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + public EntityExportData exportEntity(@PathVariable EntityType entityType, + @PathVariable("entityId") UUID entityUuid) throws ThingsboardException { + EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); + try { + return exportImportService.exportEntity(getTenantId(), entityId); + } catch (Exception 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(); + + Map>>> result = new HashMap<>(); + exportRequest.getEntities().forEach((entityType, entityIds) -> { + List>> exportDataForEntityType = new LinkedList<>(); + entityIds.forEach(entityId -> { + EntityExportData> exportData = exportImportService.exportEntity(tenantId, entityId); + exportDataForEntityType.add(exportData); + }); + result.put(entityType, exportDataForEntityType); + }); + + exportResponse.setExportData(result); + return exportResponse; + } + // TODO: api to export and import whole customer, whole tenant + + + @PostMapping("/import") + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + public , I extends EntityId, D extends EntityExportData> E importEntity(@RequestBody D exportData) throws ThingsboardException { + try { + return exportImportService.importEntity(getTenantId(), exportData); + } catch (Exception e) { + throw handleException(e); + } + } + +// public void importEntities(@RequestBody ) + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java new file mode 100644 index 0000000000..9b384b34dd --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java @@ -0,0 +1,86 @@ +/** + * 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; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.id.EntityId; +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.service.expimp.exp.EntityExportService; +import org.thingsboard.server.service.expimp.imp.EntityImportService; + +import java.util.Collection; +import java.util.EnumMap; +import java.util.Map; + +// FIXME: review packages and classes naming +@Service +@TbCoreComponent +public class DefaultEntitiesExportImportService implements EntitiesExportImportService { + + private final Map> exportServices = new EnumMap<>(EntityType.class); + private final Map> importServices = new EnumMap<>(EntityType.class); + + + // TODO: export and import of the whole tenant + // TODO: export and import of the whole customer ? + // TODO: relations export and import + @Override + public , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId) { + EntityType entityType = entityId.getEntityType(); + EntityExportService exportService = getExportService(entityType); + + return exportService.getExportData(tenantId, entityId); + } + + // FIXME: somehow validate export data + // FIXME: validate permissions for create or update + // FIXME: send entity lifecycle event + @Override + public , I extends EntityId, D extends EntityExportData> E importEntity(TenantId tenantId, D exportData) { + EntityType entityType = exportData.getEntityType(); + EntityImportService importService = getImportService(entityType); + + return importService.importEntity(tenantId, exportData); + } + + + @SuppressWarnings("unchecked") + private > EntityExportService getExportService(EntityType entityType) { + return (EntityExportService) exportServices.get(entityType); + } + + @SuppressWarnings("unchecked") + private , D extends EntityExportData> EntityImportService getImportService(EntityType entityType) { + return (EntityImportService) importServices.get(entityType); + } + + @Autowired + private void setExportImportServices(Collection> exportServices, + Collection> importServices) { + exportServices.forEach(entityExportService -> { + this.exportServices.put(entityExportService.getEntityType(), entityExportService); + }); + importServices.forEach(entityImportService -> { + this.importServices.put(entityImportService.getEntityType(), entityImportService); + }); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java new file mode 100644 index 0000000000..5d25cb9320 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java @@ -0,0 +1,29 @@ +/** + * 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; + +import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.TenantId; + +public interface EntitiesExportImportService { + + , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId); + + , I extends EntityId, D extends EntityExportData> E importEntity(TenantId tenantId, D exportData); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportService.java new file mode 100644 index 0000000000..9afa01ff8e --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportService.java @@ -0,0 +1,32 @@ +/** + * 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.exp; + +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.TenantId; + +public interface EntityExportService> { + + // FIXME: export relations + // FIXME: get rid of boilerplate + EntityExportData getExportData(TenantId tenantId, I entityId); + + EntityType getEntityType(); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AssetExportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AssetExportService.java new file mode 100644 index 0000000000..bb4b68bb29 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AssetExportService.java @@ -0,0 +1,50 @@ +/** + * 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.exp.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.export.impl.AssetExportData; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.expimp.exp.EntityExportService; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class AssetExportService implements EntityExportService { + + private final AssetService assetService; + + + @Override + public EntityExportData getExportData(TenantId tenantId, AssetId assetId) { + AssetExportData exportData = new AssetExportData(); + exportData.setAsset(assetService.findAssetById(tenantId, assetId)); + return exportData; + } + + @Override + public EntityType getEntityType() { + return EntityType.ASSET; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/CustomerExportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/CustomerExportService.java new file mode 100644 index 0000000000..7430f9ddd3 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/CustomerExportService.java @@ -0,0 +1,50 @@ +/** + * 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.exp.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.export.impl.CustomerExportData; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.customer.CustomerService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.expimp.exp.EntityExportService; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class CustomerExportService implements EntityExportService { + + private final CustomerService customerService; + + + @Override + public EntityExportData getExportData(TenantId tenantId, CustomerId customerId) { + CustomerExportData exportData = new CustomerExportData(); + exportData.setCustomer(customerService.findCustomerById(tenantId, customerId)); + return exportData; + } + + @Override + public EntityType getEntityType() { + return EntityType.CUSTOMER; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DashboardExportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DashboardExportService.java new file mode 100644 index 0000000000..87959429d9 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DashboardExportService.java @@ -0,0 +1,50 @@ +/** + * 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.exp.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.export.impl.DashboardExportData; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.dashboard.DashboardService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.expimp.exp.EntityExportService; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class DashboardExportService implements EntityExportService { + + private final DashboardService dashboardService; + + + @Override + public EntityExportData getExportData(TenantId tenantId, DashboardId dashboardId) { + DashboardExportData exportData = new DashboardExportData(); + exportData.setDashboard(dashboardService.findDashboardById(tenantId, dashboardId)); + return exportData; + } + + @Override + public EntityType getEntityType() { + return EntityType.DASHBOARD; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceExportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceExportService.java new file mode 100644 index 0000000000..860aaa9b1d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceExportService.java @@ -0,0 +1,53 @@ +/** + * 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.exp.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.export.impl.DeviceExportData; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.device.DeviceCredentialsService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.expimp.exp.EntityExportService; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class DeviceExportService implements EntityExportService { + + private final DeviceService deviceService; + private final DeviceCredentialsService deviceCredentialsService; + + + @Override + public EntityExportData getExportData(TenantId tenantId, DeviceId deviceId) { + DeviceExportData exportData = new DeviceExportData(); + exportData.setDevice(deviceService.findDeviceById(tenantId, deviceId)); + exportData.setCredentials(deviceCredentialsService.findDeviceCredentialsByDeviceId(TenantId.SYS_TENANT_ID, deviceId)); + return exportData; + } + + @Override + public EntityType getEntityType() { + return EntityType.DEVICE; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceProfileExportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceProfileExportService.java new file mode 100644 index 0000000000..425dbfd5be --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceProfileExportService.java @@ -0,0 +1,50 @@ +/** + * 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.exp.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.export.impl.DeviceProfileExportData; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.device.DeviceProfileService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.expimp.exp.EntityExportService; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class DeviceProfileExportService implements EntityExportService { + + private final DeviceProfileService deviceProfileService; + + + @Override + public EntityExportData getExportData(TenantId tenantId, DeviceProfileId deviceProfileId) { + DeviceProfileExportData exportData = new DeviceProfileExportData(); + exportData.setDeviceProfile(deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId)); + return exportData; + } + + @Override + public EntityType getEntityType() { + return EntityType.DEVICE_PROFILE; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/RuleChainExportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/RuleChainExportService.java new file mode 100644 index 0000000000..7a85281d63 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/RuleChainExportService.java @@ -0,0 +1,51 @@ +/** + * 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.exp.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.export.impl.RuleChainExportData; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.expimp.exp.EntityExportService; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class RuleChainExportService implements EntityExportService { + + private final RuleChainService ruleChainService; + + + @Override + public EntityExportData getExportData(TenantId tenantId, RuleChainId ruleChainId) { + RuleChainExportData exportData = new RuleChainExportData(); + exportData.setRuleChain(ruleChainService.findRuleChainById(tenantId, ruleChainId)); + exportData.setMetaData(ruleChainService.loadRuleChainMetaData(tenantId, ruleChainId)); + return exportData; + } + + @Override + public EntityType getEntityType() { + return EntityType.RULE_CHAIN; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java new file mode 100644 index 0000000000..195ace0b66 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java @@ -0,0 +1,30 @@ +/** + * 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 org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.TenantId; + +public interface EntityImportService, D extends EntityExportData> { + + E importEntity(TenantId tenantId, D exportData); + + EntityType getEntityType(); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java new file mode 100644 index 0000000000..3f33ba04f0 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java @@ -0,0 +1,73 @@ +/** + * 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.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.ExportableEntityDao; +import org.thingsboard.server.service.expimp.imp.EntityImportService; + +import java.util.Collection; +import java.util.EnumMap; +import java.util.Map; +import java.util.Optional; + +public abstract class AbstractEntityImportService, D extends EntityExportData> implements EntityImportService { + + private final Map> daos = new EnumMap<>(EntityType.class); + + + public final E findByExternalId(TenantId tenantId, I externalId) { + return findByExternalOrInternalId(tenantId, externalId); + } + + + protected final ID getInternalId(TenantId tenantId, ID externalId) { + if (externalId == null) { + return null; + } + HasId entity = findByExternalOrInternalId(tenantId, externalId); + if (entity == null) { + throw new IllegalStateException("Cannot find " + externalId.getEntityType() + " by external id " + externalId); + } + return entity.getId(); + } + + private , ID extends EntityId> T findByExternalOrInternalId(TenantId tenantId, ID externalOrInternalId) { + ExportableEntityDao dao = getDao(externalOrInternalId.getEntityType()); + return Optional.ofNullable(dao.findByTenantIdAndExternalId(tenantId.getId(), externalOrInternalId.getId())) + .orElseGet(() -> dao.findByTenantIdAndId(tenantId.getId(), externalOrInternalId.getId())); + } + + + @SuppressWarnings("unchecked") + private ExportableEntityDao getDao(EntityType entityType) { + return (ExportableEntityDao) daos.get(entityType); + } + + @Autowired + private void setDaos(Collection> daos) { + daos.forEach(dao -> this.daos.put(dao.getEntityType(), dao)); + if (!this.daos.containsKey(getEntityType())) { + throw new IllegalStateException(getClass().getSimpleName() + " requires ExportableEntityDao for entity type " + getEntityType()); + } + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java new file mode 100644 index 0000000000..5e563762d6 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java @@ -0,0 +1,62 @@ +/** + * 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.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.export.impl.AssetExportData; +import org.thingsboard.server.common.data.id.AssetId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class AssetImportService extends AbstractEntityImportService { + + private final AssetService assetService; + + + @Override + public Asset importEntity(TenantId tenantId, AssetExportData exportData) { + Asset asset = exportData.getAsset(); + Asset existingAsset = findByExternalId(tenantId, asset.getId()); // TODO: extract boiler plate to abstract class ... + + asset.setExternalId(asset.getId()); + asset.setTenantId(tenantId); + + if (existingAsset == null) { + asset.setId(null); + } else { + asset.setId(existingAsset.getId()); + } + + asset.setCustomerId(getInternalId(tenantId, asset.getCustomerId())); + + Asset savedAsset = assetService.saveAsset(asset); + + return savedAsset; + } + + @Override + public EntityType getEntityType() { + return EntityType.ASSET; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/CustomerImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/CustomerImportService.java new file mode 100644 index 0000000000..9510cc0700 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/CustomerImportService.java @@ -0,0 +1,60 @@ +/** + * 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.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.impl.CustomerExportData; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.customer.CustomerService; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class CustomerImportService extends AbstractEntityImportService { + + private final CustomerService customerService; + + + @Override + public Customer importEntity(TenantId tenantId, CustomerExportData exportData) { + Customer customer = exportData.getCustomer(); + Customer existingCustomer = findByExternalId(tenantId, customer.getId()); + + customer.setExternalId(customer.getId()); + customer.setTenantId(tenantId); + + if (existingCustomer == null) { + customer.setId(null); + } else { + customer.setId(existingCustomer.getId()); + } + + Customer savedCustomer = customerService.saveCustomer(customer); + + return savedCustomer; + } + + @Override + public EntityType getEntityType() { + return EntityType.CUSTOMER; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DashboardImportService.java new file mode 100644 index 0000000000..eab90bb294 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DashboardImportService.java @@ -0,0 +1,62 @@ +/** + * 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.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.impl.DashboardExportData; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.dashboard.DashboardService; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class DashboardImportService extends AbstractEntityImportService { + + private final DashboardService dashboardService; + + + @Override + public Dashboard importEntity(TenantId tenantId, DashboardExportData exportData) { + Dashboard dashboard = exportData.getDashboard(); + Dashboard existingDashboard = findByExternalId(tenantId, dashboard.getId()); + + dashboard.setExternalId(dashboard.getId()); + dashboard.setTenantId(tenantId); + + if (existingDashboard == null) { + dashboard.setId(null); + dashboard.setAssignedCustomers(null); // FIXME: need to assign dashboard to customers ? + } else { + dashboard.setId(existingDashboard.getId()); + dashboard.setAssignedCustomers(existingDashboard.getAssignedCustomers()); // we left them untouched (FIXME) + } + + Dashboard savedDashboard = dashboardService.saveDashboard(dashboard); + + return savedDashboard; + } + + @Override + public EntityType getEntityType() { + return EntityType.DASHBOARD; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceImportService.java new file mode 100644 index 0000000000..052fa3e1b3 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceImportService.java @@ -0,0 +1,81 @@ +/** + * 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.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +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.EntityType; +import org.thingsboard.server.common.data.export.impl.DeviceExportData; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.action.EntityActionService; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class DeviceImportService extends AbstractEntityImportService { + + private final DeviceService deviceService; + + + @Transactional + @Override + public Device importEntity(TenantId tenantId, DeviceExportData exportData) { + Device device = exportData.getDevice(); + Device existingDevice = findByExternalId(tenantId, device.getId()); // FIXME: !!! + // what if exporting and importing back already exported entity ? (save version and then load it back) + /* + * export entity -> id from env1 -> import this entity -> ... + * + * maybe find not only by external id but by internal too ? but then what if we will try + * */ + + device.setExternalId(device.getId()); + device.setTenantId(tenantId); + + if (existingDevice == null) { + device.setId(null); + device.setCustomerId(null); // FIXME: find and set customer + } else { + device.setId(existingDevice.getId()); + device.setCustomerId(existingDevice.getCustomerId()); + } + + // TODO or maybe set as additional config whether to update related entities when device already exists ? + // TODO: or also whether to ignore not found internal ids + + // FIXME: review use cases for version controlling: in the same tenant, between tenants, between environments and different tenants + + device.setDeviceProfileId(getInternalId(tenantId, device.getDeviceProfileId())); + device.setFirmwareId(getInternalId(tenantId, device.getFirmwareId())); + device.setSoftwareId(getInternalId(tenantId, device.getSoftwareId())); + + Device savedDevice = deviceService.saveDeviceWithCredentials(device, exportData.getCredentials()); + + return savedDevice; + } + + @Override + public EntityType getEntityType() { + return EntityType.DEVICE; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceProfileImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceProfileImportService.java new file mode 100644 index 0000000000..615f62d389 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceProfileImportService.java @@ -0,0 +1,65 @@ +/** + * 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.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.impl.DeviceProfileExportData; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.device.DeviceProfileService; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class DeviceProfileImportService extends AbstractEntityImportService { + + private final DeviceProfileService deviceProfileService; + + + @Override + public DeviceProfile importEntity(TenantId tenantId, DeviceProfileExportData exportData) { + DeviceProfile deviceProfile = exportData.getDeviceProfile(); + DeviceProfile existingDeviceProfile = findByExternalId(tenantId, deviceProfile.getId()); + + deviceProfile.setExternalId(deviceProfile.getId()); + deviceProfile.setTenantId(tenantId); + + if (existingDeviceProfile == null) { + deviceProfile.setId(null); + } else { + deviceProfile.setId(existingDeviceProfile.getId()); + } + + deviceProfile.setDefaultRuleChainId(getInternalId(tenantId, deviceProfile.getDefaultRuleChainId())); + deviceProfile.setDefaultDashboardId(getInternalId(tenantId, deviceProfile.getDefaultDashboardId())); + deviceProfile.setFirmwareId(getInternalId(tenantId, deviceProfile.getFirmwareId())); + deviceProfile.setSoftwareId(getInternalId(tenantId, deviceProfile.getSoftwareId())); + + DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); + + return savedDeviceProfile; + } + + @Override + public EntityType getEntityType() { + return EntityType.DEVICE_PROFILE; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/RuleChainImportService.java new file mode 100644 index 0000000000..eb72f58a7f --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/RuleChainImportService.java @@ -0,0 +1,80 @@ +/** + * 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.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.impl.RuleChainExportData; +import org.thingsboard.server.common.data.id.RuleChainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +public class RuleChainImportService extends AbstractEntityImportService { + + private final RuleChainService ruleChainService; + + + @Transactional + @Override + public RuleChain importEntity(TenantId tenantId, RuleChainExportData exportData) { + RuleChain ruleChain = exportData.getRuleChain(); + RuleChain existingRuleChain = findByExternalId(tenantId, ruleChain.getId()); + + ruleChain.setExternalId(ruleChain.getId()); + ruleChain.setTenantId(tenantId); + ruleChain.setFirstRuleNodeId(null); // will be set during metadata persisting + + if (existingRuleChain == null) { + ruleChain.setId(null); + } else { + ruleChain.setId(existingRuleChain.getId()); + } + + RuleChain savedRuleChain = ruleChainService.saveRuleChain(ruleChain); + + if (ruleChain.getId() != null) { + ruleChainService.deleteRuleNodes(tenantId, ruleChain.getId()); + } + RuleChainMetaData metaData = exportData.getMetaData(); + metaData.setRuleChainId(savedRuleChain.getId()); + metaData.getNodes().forEach(ruleNode -> { + ruleNode.setId(null); + ruleNode.setRuleChainId(null); + }); + metaData.getRuleChainConnections().forEach(ruleChainConnectionInfo -> { +// ruleChainConnectionInfo.setTargetRuleChainId(); + // TODO: check if this thing is needed for "Other Rule Chain Node" + // TODO: and check import of tenant rule chains + }); + ruleChainService.saveRuleChainMetaData(tenantId, metaData); + + return savedRuleChain; + } + + @Override + public EntityType getEntityType() { + return EntityType.RULE_CHAIN; + } + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java index 5089a6286e..0c42b8aec8 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java @@ -28,7 +28,6 @@ import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainData; import org.thingsboard.server.common.data.rule.RuleChainImportResult; import org.thingsboard.server.common.data.rule.RuleChainMetaData; -import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleChainUpdateResult; import org.thingsboard.server.common.data.rule.RuleNode; @@ -93,4 +92,6 @@ public interface RuleChainService { List findRuleNodesByTenantIdAndType(TenantId tenantId, String name, String toString); RuleNode saveRuleNode(TenantId tenantId, RuleNode ruleNode); + + void deleteRuleNodes(TenantId tenantId, RuleChainId ruleChainId); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java index ab21d70795..065e0c5db5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java @@ -20,6 +20,8 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty.Access; import com.fasterxml.jackson.databind.JsonNode; import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.Length; @@ -36,6 +38,9 @@ public class Customer extends ContactBased implements HasTenantId { @ApiModelProperty(position = 5, required = true, value = "JSON object with Tenant Id") private TenantId tenantId; + @Getter @Setter + private CustomerId externalId; // FIXME: add to hashcode, equals, etc + public Customer() { super(); } @@ -48,6 +53,7 @@ public class Customer extends ContactBased implements HasTenantId { super(customer); this.tenantId = customer.getTenantId(); this.title = customer.getTitle(); + this.externalId = customer.getExternalId(); } public TenantId getTenantId() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java b/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java index 450c88cbb5..4b69534bc0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java @@ -17,14 +17,19 @@ package org.thingsboard.server.common.data; import com.fasterxml.jackson.databind.JsonNode; import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; import org.thingsboard.server.common.data.id.DashboardId; public class Dashboard extends DashboardInfo { private static final long serialVersionUID = 872682138346187503L; - + private transient JsonNode configuration; - + + @Getter @Setter + private DashboardId externalId; + public Dashboard() { super(); } @@ -40,6 +45,7 @@ public class Dashboard extends DashboardInfo { public Dashboard(Dashboard dashboard) { super(dashboard); this.configuration = dashboard.getConfiguration(); + this.externalId = dashboard.getExternalId(); } @ApiModelProperty(position = 9, value = "JSON object with main configuration of the dashboard: layouts, widgets, aliases, etc. " + diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java index 4d7bf5e822..519cffc39a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java @@ -21,6 +21,8 @@ import com.fasterxml.jackson.databind.JsonNode; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.device.data.DeviceData; import org.thingsboard.server.common.data.id.CustomerId; @@ -61,6 +63,9 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen private OtaPackageId firmwareId; private OtaPackageId softwareId; + @Getter @Setter + private DeviceId externalId; + public Device() { super(); } @@ -80,6 +85,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen this.setDeviceData(device.getDeviceData()); this.firmwareId = device.getFirmwareId(); this.softwareId = device.getSoftwareId(); + this.externalId = device.getExternalId(); } public Device updateDevice(Device device) { @@ -93,6 +99,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen this.setFirmwareId(device.getFirmwareId()); this.setSoftwareId(device.getSoftwareId()); Optional.ofNullable(device.getAdditionalInfo()).ifPresent(this::setAdditionalInfo); + this.setExternalId(device.getExternalId()); return this; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java index 1dd6a2ad19..6161cd6a5c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java @@ -90,6 +90,8 @@ public class DeviceProfile extends SearchTextBased implements H @ApiModelProperty(position = 10, value = "Reference to the software OTA package. If present, the specified package will be used as default device software. ") private OtaPackageId softwareId; + private DeviceProfileId externalId; + public DeviceProfile() { super(); } @@ -112,6 +114,7 @@ public class DeviceProfile extends SearchTextBased implements H this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey(); this.firmwareId = deviceProfile.getFirmwareId(); this.softwareId = deviceProfile.getSoftwareId(); + this.externalId = deviceProfile.getExternalId(); } @ApiModelProperty(position = 1, value = "JSON object with the device profile Id. " + diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java index db68df4901..e0924c63ee 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java @@ -19,6 +19,8 @@ import com.fasterxml.jackson.databind.JsonNode; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; import org.thingsboard.server.common.data.HasCustomerId; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; @@ -49,6 +51,9 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements @Length(fieldName = "label") private String label; + @Getter @Setter + private AssetId externalId; + public Asset() { super(); } @@ -64,6 +69,7 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements this.name = asset.getName(); this.type = asset.getType(); this.label = asset.getLabel(); + this.externalId = asset.getExternalId(); } public void update(Asset asset) { @@ -73,6 +79,7 @@ public class Asset extends SearchTextBasedWithAdditionalInfo implements this.type = asset.getType(); this.label = asset.getLabel(); Optional.ofNullable(asset.getAdditionalInfo()).ifPresent(this::setAdditionalInfo); + this.externalId = asset.getExternalId(); } @ApiModelProperty(position = 1, value = "JSON object with the asset Id. " + diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportRequest.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportRequest.java new file mode 100644 index 0000000000..d3502695c0 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportRequest.java @@ -0,0 +1,29 @@ +/** + * 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.common.data.export; + +import lombok.Data; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; + +import java.util.List; +import java.util.Map; + +@Data +public class EntitiesExportRequest { + // todo: should be processed in a specific order, e.g. device profiles, then devices, then relations, etc. + private Map> entities; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportResponse.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportResponse.java new file mode 100644 index 0000000000..cab75313d4 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportResponse.java @@ -0,0 +1,29 @@ +/** + * 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.common.data.export; + +import lombok.Data; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; + +import java.util.List; +import java.util.Map; + +@Data +public class EntitiesExportResponse { + private Map>>> exportData; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java new file mode 100644 index 0000000000..36d3cb4b0c --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java @@ -0,0 +1,48 @@ +/** + * 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.common.data.export; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.impl.AssetExportData; +import org.thingsboard.server.common.data.export.impl.CustomerExportData; +import org.thingsboard.server.common.data.export.impl.DashboardExportData; +import org.thingsboard.server.common.data.export.impl.DeviceExportData; +import org.thingsboard.server.common.data.export.impl.DeviceProfileExportData; +import org.thingsboard.server.common.data.export.impl.RuleChainExportData; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(property = "entityType", use = JsonTypeInfo.Id.NAME) +@JsonSubTypes({ + @Type(name = "DEVICE", value = DeviceExportData.class), + @Type(name = "DEVICE_PROFILE", value = DeviceProfileExportData.class), + @Type(name = "ASSET", value = AssetExportData.class), + @Type(name = "RULE_CHAIN", value = RuleChainExportData.class), + @Type(name = "DASHBOARD", value = DashboardExportData.class), + @Type(name = "CUSTOMER", value = CustomerExportData.class) +}) +public interface EntityExportData> { + + @JsonIgnore + EntityType getEntityType(); // fixme: maybe remove if not needed, as well as generic + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/AssetExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/AssetExportData.java new file mode 100644 index 0000000000..c0f417bd76 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/AssetExportData.java @@ -0,0 +1,33 @@ +/** + * 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.common.data.export.impl; + +import lombok.Data; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.export.EntityExportData; + +@Data +public class AssetExportData implements EntityExportData { + + private Asset asset; + + @Override + public EntityType getEntityType() { + return EntityType.ASSET; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/CustomerExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/CustomerExportData.java new file mode 100644 index 0000000000..31e53db223 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/CustomerExportData.java @@ -0,0 +1,33 @@ +/** + * 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.common.data.export.impl; + +import lombok.Data; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.EntityExportData; + +@Data +public class CustomerExportData implements EntityExportData { + + private Customer customer; + + @Override + public EntityType getEntityType() { + return EntityType.CUSTOMER; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DashboardExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DashboardExportData.java new file mode 100644 index 0000000000..7860c6c553 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DashboardExportData.java @@ -0,0 +1,33 @@ +/** + * 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.common.data.export.impl; + +import lombok.Data; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.EntityExportData; + +@Data +public class DashboardExportData implements EntityExportData { + + private Dashboard dashboard; + + @Override + public EntityType getEntityType() { + return EntityType.DASHBOARD; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceExportData.java new file mode 100644 index 0000000000..05f3299d02 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceExportData.java @@ -0,0 +1,35 @@ +/** + * 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.common.data.export.impl; + +import lombok.Data; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.security.DeviceCredentials; + +@Data +public class DeviceExportData implements EntityExportData { + + private Device device; + private DeviceCredentials credentials; + + @Override + public EntityType getEntityType() { + return EntityType.DEVICE; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceProfileExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceProfileExportData.java new file mode 100644 index 0000000000..c030d0b86e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceProfileExportData.java @@ -0,0 +1,33 @@ +/** + * 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.common.data.export.impl; + +import lombok.Data; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.EntityExportData; + +@Data +public class DeviceProfileExportData implements EntityExportData { + + private DeviceProfile deviceProfile; + + @Override + public EntityType getEntityType() { + return EntityType.DEVICE_PROFILE; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/RuleChainExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/RuleChainExportData.java new file mode 100644 index 0000000000..11fbace911 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/RuleChainExportData.java @@ -0,0 +1,35 @@ +/** + * 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.common.data.export.impl; + +import lombok.Data; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; + +@Data +public class RuleChainExportData implements EntityExportData { + + private RuleChain ruleChain; + private RuleChainMetaData metaData; + + @Override + public EntityType getEntityType() { + return EntityType.RULE_CHAIN; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java index 17d7ffef0b..7ff2b0dc2b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java @@ -59,6 +59,8 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo im @JsonIgnore private byte[] configurationBytes; + private RuleChainId externalId; + public RuleChain() { super(); } @@ -75,6 +77,7 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo im this.firstRuleNodeId = ruleChain.getFirstRuleNodeId(); this.root = ruleChain.isRoot(); this.setConfiguration(ruleChain.getConfiguration()); + this.setExternalId(ruleChain.getExternalId()); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java new file mode 100644 index 0000000000..c7741ec013 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java @@ -0,0 +1,36 @@ +/** + * 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.dao; + +import org.thingsboard.server.common.data.EntityType; + +import java.util.UUID; + +public interface ExportableEntityDao { + + T findByTenantIdAndExternalId(UUID tenantId, UUID externalId); + + T findByTenantIdAndId(UUID tenantId, UUID id); + // fixme: get rid of boilerplate ? + + EntityType getEntityType(); + + /* + * default > ExportableEntityRepository getExportableEntityRepository() { + * ((ExportableEntityRepository) getJpaRepository).find... + * */ + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityRepository.java b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityRepository.java new file mode 100644 index 0000000000..b6a303a941 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityRepository.java @@ -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.dao; + +import java.util.UUID; + +public interface ExportableEntityRepository { + + D findByTenantIdAndExternalId(UUID tenantId, UUID externalId); + + D findByTenantIdAndId(UUID tenantId, UUID id); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java index e7533cc736..495886bcc4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java @@ -22,8 +22,8 @@ import org.thingsboard.server.common.data.asset.AssetInfo; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.dao.TenantEntityDao; import java.util.List; @@ -34,7 +34,7 @@ import java.util.UUID; * The Interface AssetDao. * */ -public interface AssetDao extends Dao, TenantEntityDao { +public interface AssetDao extends Dao, TenantEntityDao, ExportableEntityDao { /** * Find asset info by id. diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java index d79537e564..89672d2a73 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.dao.TenantEntityDao; import java.util.Optional; @@ -28,7 +29,7 @@ import java.util.UUID; /** * The Interface CustomerDao. */ -public interface CustomerDao extends Dao, TenantEntityDao { +public interface CustomerDao extends Dao, TenantEntityDao, ExportableEntityDao { /** * Save or update customer object @@ -37,7 +38,7 @@ public interface CustomerDao extends Dao, TenantEntityDao { * @return saved customer object */ Customer save(TenantId tenantId, Customer customer); - + /** * Find customers by tenant id and page link. * diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java index dfe9152484..90353f49b3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java @@ -18,12 +18,13 @@ package org.thingsboard.server.dao.dashboard; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.dao.TenantEntityDao; /** * The Interface DashboardDao. */ -public interface DashboardDao extends Dao, TenantEntityDao { +public interface DashboardDao extends Dao, TenantEntityDao, ExportableEntityDao { /** * Save or update dashboard object diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java index 85f66e4b67..90f2b218a7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.dao.TenantEntityDao; import java.util.List; @@ -35,7 +36,7 @@ import java.util.UUID; * The Interface DeviceDao. * */ -public interface DeviceDao extends Dao, TenantEntityDao { +public interface DeviceDao extends Dao, TenantEntityDao, ExportableEntityDao { /** * Find device info by id. diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java index bf61cbdce5..b79cbb05ed 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java @@ -21,10 +21,11 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.ExportableEntityDao; import java.util.UUID; -public interface DeviceProfileDao extends Dao { +public interface DeviceProfileDao extends Dao, ExportableEntityDao { DeviceProfileInfo findDeviceProfileInfoById(TenantId tenantId, UUID deviceProfileId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 8f692d5a12..0b74e79f2b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -559,6 +559,8 @@ public class ModelConstants { public static final String EDGE_EVENT_BY_ID_VIEW_NAME = "edge_event_by_id"; + public static final String EXTERNAL_ID_PROPERTY = "external_id"; + /** * Cassandra attributes and timeseries constants. */ diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAssetEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAssetEntity.java index 4405768f7c..0936ca2855 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAssetEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractAssetEntity.java @@ -38,6 +38,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.ASSET_LABEL_PROPER import static org.thingsboard.server.dao.model.ModelConstants.ASSET_NAME_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TENANT_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.ASSET_TYPE_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.EXTERNAL_ID_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @Data @@ -68,6 +69,9 @@ public abstract class AbstractAssetEntity extends BaseSqlEntity @Column(name = ModelConstants.ASSET_ADDITIONAL_INFO_PROPERTY) private JsonNode additionalInfo; + @Column(name = EXTERNAL_ID_PROPERTY) + private UUID externalId; + public AbstractAssetEntity() { super(); } @@ -87,6 +91,9 @@ public abstract class AbstractAssetEntity extends BaseSqlEntity this.type = asset.getType(); this.label = asset.getLabel(); this.additionalInfo = asset.getAdditionalInfo(); + if (asset.getExternalId() != null) { + this.externalId = asset.getExternalId().getId(); + } } public AbstractAssetEntity(AssetEntity assetEntity) { @@ -99,6 +106,7 @@ public abstract class AbstractAssetEntity extends BaseSqlEntity this.label = assetEntity.getLabel(); this.searchText = assetEntity.getSearchText(); this.additionalInfo = assetEntity.getAdditionalInfo(); + this.externalId = assetEntity.getExternalId(); } @Override @@ -128,6 +136,9 @@ public abstract class AbstractAssetEntity extends BaseSqlEntity asset.setType(type); asset.setLabel(label); asset.setAdditionalInfo(additionalInfo); + if (externalId != null) { + asset.setExternalId(new AssetId(externalId)); + } return asset; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java index 717ba1f9e3..dbc0a057ed 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java @@ -84,6 +84,9 @@ public abstract class AbstractDeviceEntity extends BaseSqlEnti @Column(name = ModelConstants.DEVICE_DEVICE_DATA_PROPERTY, columnDefinition = "jsonb") private JsonNode deviceData; + @Column(name = ModelConstants.EXTERNAL_ID_PROPERTY, columnDefinition = "uuid") + private UUID externalId; + public AbstractDeviceEntity() { super(); } @@ -113,6 +116,9 @@ public abstract class AbstractDeviceEntity extends BaseSqlEnti this.type = device.getType(); this.label = device.getLabel(); this.additionalInfo = device.getAdditionalInfo(); + if (device.getExternalId() != null) { + this.externalId = device.getExternalId().getId(); + } } public AbstractDeviceEntity(DeviceEntity deviceEntity) { @@ -129,6 +135,7 @@ public abstract class AbstractDeviceEntity extends BaseSqlEnti this.additionalInfo = deviceEntity.getAdditionalInfo(); this.firmwareId = deviceEntity.getFirmwareId(); this.softwareId = deviceEntity.getSoftwareId(); + this.externalId = deviceEntity.getExternalId(); } @Override @@ -164,6 +171,9 @@ public abstract class AbstractDeviceEntity extends BaseSqlEnti device.setType(type); device.setLabel(label); device.setAdditionalInfo(additionalInfo); + if (externalId != null) { + device.setExternalId(new DeviceId(externalId)); + } return device; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/CustomerEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/CustomerEntity.java index 61b21ecc4f..3dec26afd9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/CustomerEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/CustomerEntity.java @@ -77,6 +77,9 @@ public final class CustomerEntity extends BaseSqlEntity implements Sea @Column(name = ModelConstants.CUSTOMER_ADDITIONAL_INFO_PROPERTY) private JsonNode additionalInfo; + @Column(name = ModelConstants.EXTERNAL_ID_PROPERTY) + private UUID externalId; + public CustomerEntity() { super(); } @@ -97,6 +100,9 @@ public final class CustomerEntity extends BaseSqlEntity implements Sea this.phone = customer.getPhone(); this.email = customer.getEmail(); this.additionalInfo = customer.getAdditionalInfo(); + if (customer.getExternalId() != null) { + this.externalId = customer.getExternalId().getId(); + } } @Override @@ -124,6 +130,9 @@ public final class CustomerEntity extends BaseSqlEntity implements Sea customer.setPhone(phone); customer.setEmail(email); customer.setAdditionalInfo(additionalInfo); + if (externalId != null) { + customer.setExternalId(new CustomerId(externalId)); + } return customer; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java index 45b4bf9210..ccaea5524e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DashboardEntity.java @@ -78,6 +78,9 @@ public final class DashboardEntity extends BaseSqlEntity implements S @Column(name = ModelConstants.DASHBOARD_CONFIGURATION_PROPERTY) private JsonNode configuration; + @Column(name = ModelConstants.EXTERNAL_ID_PROPERTY) + private UUID externalId; + public DashboardEntity() { super(); } @@ -102,6 +105,9 @@ public final class DashboardEntity extends BaseSqlEntity implements S this.mobileHide = dashboard.isMobileHide(); this.mobileOrder = dashboard.getMobileOrder(); this.configuration = dashboard.getConfiguration(); + if (dashboard.getExternalId() != null) { + this.externalId = dashboard.getExternalId().getId(); + } } @Override @@ -133,6 +139,9 @@ public final class DashboardEntity extends BaseSqlEntity implements S dashboard.setMobileHide(mobileHide); dashboard.setMobileOrder(mobileOrder); dashboard.setConfiguration(configuration); + if (externalId != null) { + dashboard.setExternalId(new DashboardId(externalId)); + } return dashboard; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java index bd214a6cbc..6894bd08d7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java @@ -103,6 +103,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl @Column(name = ModelConstants.DEVICE_PROFILE_SOFTWARE_ID_PROPERTY) private UUID softwareId; + @Column(name = ModelConstants.EXTERNAL_ID_PROPERTY) + private UUID externalId; + public DeviceProfileEntity() { super(); } @@ -137,6 +140,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl if (deviceProfile.getSoftwareId() != null) { this.softwareId = deviceProfile.getSoftwareId().getId(); } + if (deviceProfile.getExternalId() != null) { + this.externalId = deviceProfile.getExternalId().getId(); + } } @Override @@ -184,6 +190,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl if (softwareId != null) { deviceProfile.setSoftwareId(new OtaPackageId(softwareId)); } + if (externalId != null) { + deviceProfile.setExternalId(new DeviceProfileId(externalId)); + } return deviceProfile; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java index 09ed75b5e5..88c8c79fdc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/RuleChainEntity.java @@ -75,6 +75,9 @@ public class RuleChainEntity extends BaseSqlEntity implements SearchT @Column(name = ModelConstants.ADDITIONAL_INFO_PROPERTY) private JsonNode additionalInfo; + @Column(name = ModelConstants.EXTERNAL_ID_PROPERTY) + private UUID externalId; + public RuleChainEntity() { } @@ -94,6 +97,9 @@ public class RuleChainEntity extends BaseSqlEntity implements SearchT this.debugMode = ruleChain.isDebugMode(); this.configuration = ruleChain.getConfiguration(); this.additionalInfo = ruleChain.getAdditionalInfo(); + if (ruleChain.getExternalId() != null) { + this.externalId = ruleChain.getExternalId().getId(); + } } @Override @@ -120,6 +126,9 @@ public class RuleChainEntity extends BaseSqlEntity implements SearchT ruleChain.setDebugMode(debugMode); ruleChain.setConfiguration(configuration); ruleChain.setAdditionalInfo(additionalInfo); + if (externalId != null) { + ruleChain.setExternalId(new RuleChainId(externalId)); + } return ruleChain; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index 097779f33e..fd84c12a1b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -684,6 +684,11 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC throw t; } } + deleteRuleNodes(tenantId, ruleChainId); + } + + @Override + public void deleteRuleNodes(TenantId tenantId, RuleChainId ruleChainId) { List nodeRelations = getRuleChainToNodeRelations(tenantId, ruleChainId); for (EntityRelation relation : nodeRelations) { deleteRuleNode(tenantId, relation.getTo()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java index 542a487949..59f287eadd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java @@ -19,19 +19,18 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.rule.RuleChain; -import org.thingsboard.server.common.data.rule.RuleChainOutputLabelsUsage; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.dao.TenantEntityDao; import java.util.Collection; -import java.util.List; import java.util.UUID; /** * Created by igor on 3/12/18. */ -public interface RuleChainDao extends Dao, TenantEntityDao { +public interface RuleChainDao extends Dao, TenantEntityDao, ExportableEntityDao { /** * Find rule chains by tenantId and page link. @@ -81,4 +80,6 @@ public interface RuleChainDao extends Dao, TenantEntityDao { Collection findByTenantIdAndTypeAndName(TenantId tenantId, RuleChainType type, String name); + RuleChain findByTenantIdAndExternalId(UUID tenantId, UUID externalId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java index 833fcc2de8..140ae3af86 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/AssetRepository.java @@ -20,6 +20,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.AssetEntity; import org.thingsboard.server.dao.model.sql.AssetInfoEntity; @@ -29,7 +30,7 @@ import java.util.UUID; /** * Created by Valerii Sosliuk on 5/21/2017. */ -public interface AssetRepository extends JpaRepository { +public interface AssetRepository extends JpaRepository, ExportableEntityRepository { @Query("SELECT new org.thingsboard.server.dao.model.sql.AssetInfoEntity(a, c.title, c.additionalInfo) " + "FROM AssetEntity a " + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java index c9752c5390..cd4fefac48 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java @@ -208,4 +208,20 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao im public Long countByTenantId(TenantId tenantId) { return assetRepository.countByTenantIdAndTypeIsNot(tenantId.getId(), TB_SERVICE_QUEUE); } + + @Override + public Asset findByTenantIdAndExternalId(UUID tenantId, UUID externalId) { + return DaoUtil.getData(assetRepository.findByTenantIdAndExternalId(tenantId, externalId)); + } + + @Override + public Asset findByTenantIdAndId(UUID tenantId, UUID id) { + return DaoUtil.getData(assetRepository.findByTenantIdAndId(tenantId, id)); + } + + @Override + public EntityType getEntityType() { + return EntityType.ASSET; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java index 61cd9afac7..1300fcdfa5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java @@ -20,6 +20,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.CustomerEntity; import java.util.UUID; @@ -27,7 +28,7 @@ import java.util.UUID; /** * Created by Valerii Sosliuk on 5/6/2017. */ -public interface CustomerRepository extends JpaRepository { +public interface CustomerRepository extends JpaRepository, ExportableEntityRepository { @Query("SELECT c FROM CustomerEntity c WHERE c.tenantId = :tenantId " + "AND LOWER(c.searchText) LIKE LOWER(CONCAT('%', :textSearch, '%'))") diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java index 02311df61c..9cf74f1f33 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java @@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -68,4 +69,20 @@ public class JpaCustomerDao extends JpaAbstractSearchTextDao { +public interface DashboardRepository extends JpaRepository, ExportableEntityRepository { Long countByTenantId(UUID tenantId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java index 98edcaa1fd..b65e9997a9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java @@ -19,7 +19,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.dashboard.DashboardDao; import org.thingsboard.server.dao.model.sql.DashboardEntity; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; @@ -49,4 +51,20 @@ public class JpaDashboardDao extends JpaAbstractSearchTextDao { +public interface DeviceProfileRepository extends JpaRepository, ExportableEntityRepository { @Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.name, d.image, d.defaultDashboardId, d.type, d.transportType) " + "FROM DeviceProfileEntity d " + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java index b4ae9270e3..46d755195e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java @@ -21,6 +21,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.DeviceEntity; import org.thingsboard.server.dao.model.sql.DeviceInfoEntity; @@ -30,7 +31,7 @@ import java.util.UUID; /** * Created by Valerii Sosliuk on 5/6/2017. */ -public interface DeviceRepository extends JpaRepository { +public interface DeviceRepository extends JpaRepository, ExportableEntityRepository { @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + "FROM DeviceEntity d " + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java index 1d75690e50..1a5d44900c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java @@ -302,4 +302,20 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao Objects.toString(pageLink.getTextSearch(), ""), DaoUtil.toPageable(pageLink))); } + + @Override + public Device findByTenantIdAndExternalId(UUID tenantId, UUID externalId) { + return DaoUtil.getData(deviceRepository.findByTenantIdAndExternalId(tenantId, externalId)); + } + + @Override + public Device findByTenantIdAndId(UUID tenantId, UUID id) { + return findDeviceByTenantIdAndId(TenantId.fromUUID(tenantId), id); + } + + @Override + public EntityType getEntityType() { + return EntityType.DEVICE; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java index 80513d77c6..539377a647 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java @@ -23,6 +23,7 @@ import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -109,4 +110,20 @@ public class JpaDeviceProfileDao extends JpaAbstractSearchTextDao { +public interface RuleChainRepository extends JpaRepository, ExportableEntityRepository { @Query("SELECT rc FROM RuleChainEntity rc WHERE rc.tenantId = :tenantId " + "AND LOWER(rc.searchText) LIKE LOWER(CONCAT('%', :searchText, '%'))") diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 4e2596485b..e18f73a065 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -72,17 +72,19 @@ CREATE TABLE IF NOT EXISTS entity_alarm ( CONSTRAINT fk_entity_alarm_id FOREIGN KEY (alarm_id) REFERENCES alarm(id) ON DELETE CASCADE ); -CREATE TABLE IF NOT EXISTS asset ( - id uuid NOT NULL CONSTRAINT asset_pkey PRIMARY KEY, - created_time bigint NOT NULL, - additional_info varchar, - customer_id uuid, - name varchar(255), - label varchar(255), - search_text varchar(255), - tenant_id uuid, - type varchar(255), - CONSTRAINT asset_name_unq_key UNIQUE (tenant_id, name) +CREATE TABLE IF NOT EXISTS asset( + id uuid NOT NULL + CONSTRAINT asset_pkey PRIMARY KEY, + created_time bigint NOT NULL, + additional_info varchar, + customer_id uuid, + name varchar(255), + label varchar(255), + search_text varchar(255), + tenant_id uuid, + type varchar(255), + external_id uuid, + CONSTRAINT asset_name_unq_key UNIQUE (tenant_id, name) ); CREATE TABLE IF NOT EXISTS audit_log ( @@ -128,47 +130,53 @@ CREATE TABLE IF NOT EXISTS component_descriptor ( ); CREATE TABLE IF NOT EXISTS customer ( - id uuid NOT NULL CONSTRAINT customer_pkey PRIMARY KEY, - created_time bigint NOT NULL, - additional_info varchar, - address varchar, - address2 varchar, - city varchar(255), - country varchar(255), - email varchar(255), - phone varchar(255), - search_text varchar(255), - state varchar(255), - tenant_id uuid, - title varchar(255), - zip varchar(255) -); - -CREATE TABLE IF NOT EXISTS dashboard ( - id uuid NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY, - created_time bigint NOT NULL, - configuration varchar, + id uuid NOT NULL CONSTRAINT customer_pkey PRIMARY KEY, + created_time bigint NOT NULL, + additional_info varchar, + address varchar, + address2 varchar, + city varchar(255), + country varchar(255), + email varchar(255), + phone varchar(255), + search_text varchar(255), + state varchar(255), + tenant_id uuid, + title varchar(255), + zip varchar(255), + external_id uuid +); + +CREATE TABLE IF NOT EXISTS dashboard +( + id uuid NOT NULL + CONSTRAINT dashboard_pkey PRIMARY KEY, + created_time bigint NOT NULL, + configuration varchar, assigned_customers varchar(1000000), - search_text varchar(255), - tenant_id uuid, - title varchar(255), - mobile_hide boolean DEFAULT false, - mobile_order int, - image varchar(1000000) + search_text varchar(255), + tenant_id uuid, + title varchar(255), + mobile_hide boolean DEFAULT false, + mobile_order int, + image varchar(1000000), + external_id uuid ); CREATE TABLE IF NOT EXISTS rule_chain ( - id uuid NOT NULL CONSTRAINT rule_chain_pkey PRIMARY KEY, - created_time bigint NOT NULL, - additional_info varchar, - configuration varchar(10000000), - name varchar(255), - type varchar(255), - first_rule_node_id uuid, - root boolean, - debug_mode boolean, - search_text varchar(255), - tenant_id uuid + id uuid NOT NULL + CONSTRAINT rule_chain_pkey PRIMARY KEY, + created_time bigint NOT NULL, + additional_info varchar, + configuration varchar(10000000), + name varchar(255), + type varchar(255), + first_rule_node_id uuid, + root boolean, + debug_mode boolean, + search_text varchar(255), + tenant_id uuid, + external_id uuid ); CREATE TABLE IF NOT EXISTS rule_node ( @@ -216,30 +224,31 @@ CREATE TABLE IF NOT EXISTS ota_package ( ); CREATE TABLE IF NOT EXISTS device_profile ( - id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY, - created_time bigint NOT NULL, - name varchar(255), - type varchar(255), - image varchar(1000000), - transport_type varchar(255), - provision_type varchar(255), - profile_data jsonb, - description varchar, - search_text varchar(255), - is_default boolean, - tenant_id uuid, - firmware_id uuid, - software_id uuid, - default_rule_chain_id uuid, - default_dashboard_id uuid, - default_queue_name varchar(255), - provision_device_key varchar, - CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), - CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), - CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), - CONSTRAINT fk_default_dashboard_device_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id), - CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES ota_package(id), - CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES ota_package(id) + id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY, + created_time bigint NOT NULL, + name varchar(255), + type varchar(255), + image varchar(1000000), + transport_type varchar(255), + provision_type varchar(255), + profile_data jsonb, + description varchar, + search_text varchar(255), + is_default boolean, + tenant_id uuid, + firmware_id uuid, + software_id uuid, + default_rule_chain_id uuid, + default_dashboard_id uuid, + default_queue_name varchar(255), + provision_device_key varchar, + external_id uuid, + CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), + CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), + CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain (id), + CONSTRAINT fk_default_dashboard_device_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard (id), + CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES ota_package (id), + CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES ota_package (id) ); ALTER TABLE ota_package @@ -257,23 +266,24 @@ ALTER TABLE ota_package -- ); CREATE TABLE IF NOT EXISTS device ( - id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY, - created_time bigint NOT NULL, - additional_info varchar, - customer_id uuid, - device_profile_id uuid NOT NULL, - device_data jsonb, - type varchar(255), - name varchar(255), - label varchar(255), - search_text varchar(255), - tenant_id uuid, - firmware_id uuid, - software_id uuid, - CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), - CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id), - CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES ota_package(id), - CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES ota_package(id) + id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY, + created_time bigint NOT NULL, + additional_info varchar, + customer_id uuid, + device_profile_id uuid NOT NULL, + device_data jsonb, + type varchar(255), + name varchar(255), + label varchar(255), + search_text varchar(255), + tenant_id uuid, + firmware_id uuid, + software_id uuid, + external_id uuid, + CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), + CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile (id), + CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES ota_package (id), + CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES ota_package (id) ); CREATE TABLE IF NOT EXISTS device_credentials ( From 849513541ecb7cab4e09a1bdf3cf29480bd28ea0 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Wed, 16 Mar 2022 17:35:34 +0200 Subject: [PATCH 02/39] Entities export/import API refactoring: permission checks and entity lifecycle events --- .../server/controller/AssetController.java | 18 +--- .../server/controller/BaseController.java | 62 ++++++++++++++ .../server/controller/CustomerController.java | 10 +-- .../controller/DashboardController.java | 8 +- .../server/controller/DeviceController.java | 22 +---- .../controller/DeviceProfileController.java | 25 +----- .../EntitiesExportImportController.java | 84 ++++++++++++------- .../controller/RuleChainController.java | 18 +--- .../DefaultEntitiesExportImportService.java | 13 ++- .../expimp/EntitiesExportImportService.java | 5 +- .../expimp/imp/EntityImportResult.java | 26 ++++++ .../expimp/imp/EntityImportService.java | 3 +- .../imp/impl/AbstractEntityImportService.java | 3 +- .../expimp/imp/impl/AssetImportService.java | 8 +- .../imp/impl/CustomerImportService.java | 8 +- .../imp/impl/DashboardImportService.java | 8 +- .../expimp/imp/impl/DeviceImportService.java | 10 ++- .../imp/impl/DeviceProfileImportService.java | 8 +- .../imp/impl/RuleChainImportService.java | 8 +- .../common/data/export/EntityExportData.java | 5 +- .../data/export/impl/AssetExportData.java | 5 ++ .../data/export/impl/CustomerExportData.java | 5 ++ .../data/export/impl/DashboardExportData.java | 5 ++ .../data/export/impl/DeviceExportData.java | 5 ++ .../export/impl/DeviceProfileExportData.java | 5 ++ .../data/export/impl/RuleChainExportData.java | 5 ++ 26 files changed, 240 insertions(+), 142 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportResult.java diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index 7bd9cb065d..94c714e24f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -156,7 +156,7 @@ public class AssetController extends BaseController { Asset savedAsset = checkNotNull(assetService.saveAsset(asset)); - onAssetCreatedOrUpdated(savedAsset, asset.getId() != null, getCurrentUser()); + onEntityUpdatedOrCreated(getCurrentUser(), savedAsset, null, asset.getId() == null); return savedAsset; } 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)", 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')") @@ -681,7 +667,7 @@ public class AssetController extends BaseController { public BulkImportResult processAssetsBulkImport(@RequestBody BulkImportRequest request) throws Exception { SecurityUser user = getCurrentUser(); return assetBulkImportService.processBulkImport(request, user, importedAssetInfo -> { - onAssetCreatedOrUpdated(importedAssetInfo.getEntity(), importedAssetInfo.isUpdated(), user); + onEntityUpdatedOrCreated(user, importedAssetInfo.getEntity(), importedAssetInfo.getOldEntity(), !importedAssetInfo.isUpdated()); }); } diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index f945cc30b7..210a6bea2d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; 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.HasTenantId; 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.EntityIdFactory; 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.RpcId; 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.TimePageLink; 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.relation.EntityRelation; import org.thingsboard.server.common.data.rpc.Rpc; @@ -141,6 +144,7 @@ import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; @@ -919,4 +923,62 @@ public abstract class BaseController { return MediaType.APPLICATION_OCTET_STREAM; } } + + public & 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); + } + } + } diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java index b419121459..27037f36e7 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java @@ -33,7 +33,6 @@ import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; 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.id.CustomerId; import org.thingsboard.server.common.data.id.EdgeId; @@ -151,14 +150,7 @@ public class CustomerController extends BaseController { checkEntity(customer.getId(), customer, Resource.CUSTOMER); Customer savedCustomer = checkNotNull(customerService.saveCustomer(customer)); - - 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); - } + onEntityUpdatedOrCreated(getCurrentUser(), savedCustomer, null, customer.getId() == null); return savedCustomer; } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index 49c33045a5..4ea1ca83c7 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -187,13 +187,7 @@ public class DashboardController extends BaseController { Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard)); - logEntityAction(savedDashboard.getId(), savedDashboard, - null, - dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); - - if (dashboard.getId() != null) { - sendEntityNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), EdgeEventActionType.UPDATED); - } + onEntityUpdatedOrCreated(getCurrentUser(), savedDashboard, null, dashboard.getId() == null); return savedDashboard; } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 85a21e912c..d2ffafa73e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -195,7 +195,7 @@ public class DeviceController extends BaseController { Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken)); - onDeviceCreatedOrUpdated(savedDevice, oldDevice, !created, getCurrentUser()); + onEntityUpdatedOrCreated(getCurrentUser(), savedDevice, oldDevice, created); return savedDevice; } catch (Exception e) { @@ -224,10 +224,8 @@ public class DeviceController extends BaseController { checkEntity(device.getId(), device, Resource.DEVICE); Device savedDevice = deviceService.saveDeviceWithCredentials(device, credentials); checkNotNull(savedDevice); - tbClusterService.onDeviceUpdated(savedDevice, device); - logEntityAction(savedDevice.getId(), savedDevice, - savedDevice.getCustomerId(), - device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); + + onEntityUpdatedOrCreated(getCurrentUser(), savedDevice, device, device.getId() == null); return savedDevice; } 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)", 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')") @@ -1015,7 +1001,7 @@ public class DeviceController extends BaseController { public BulkImportResult processDevicesBulkImport(@RequestBody BulkImportRequest request) throws Exception { SecurityUser user = getCurrentUser(); return deviceBulkImportService.processBulkImport(request, user, importedDeviceInfo -> { - onDeviceCreatedOrUpdated(importedDeviceInfo.getEntity(), importedDeviceInfo.getOldEntity(), importedDeviceInfo.isUpdated(), user); + onEntityUpdatedOrCreated(user, importedDeviceInfo.getEntity(), importedDeviceInfo.getOldEntity(), !importedDeviceInfo.isUpdated()); }); } diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java index 25a35e77fa..240e60be2c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java @@ -46,7 +46,6 @@ import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import java.util.List; -import java.util.Objects; import java.util.UUID; 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); - boolean isFirmwareChanged = false; - boolean isSoftwareChanged = false; - + DeviceProfile oldDeviceProfile = null; if (!created) { - DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId()); - if (!Objects.equals(deviceProfile.getFirmwareId(), oldDeviceProfile.getFirmwareId())) { - isFirmwareChanged = true; - } - if (!Objects.equals(deviceProfile.getSoftwareId(), oldDeviceProfile.getSoftwareId())) { - isSoftwareChanged = true; - } + oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId()); } DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile)); - tbClusterService.onDeviceProfileChange(savedDeviceProfile, null); - 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); + onEntityUpdatedOrCreated(getCurrentUser(), deviceProfile, oldDeviceProfile, created); - sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(), - deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); return savedDeviceProfile; } catch (Exception e) { logEntityAction(emptyId(EntityType.DEVICE_PROFILE), deviceProfile, diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index a11a9189f4..07a033ab03 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.controller; +import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; import org.springframework.security.access.prepost.PreAuthorize; 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.RestController; 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.export.EntitiesExportRequest; -import org.thingsboard.server.common.data.export.EntitiesExportResponse; import org.thingsboard.server.common.data.export.EntityExportData; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; 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.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; @RestController @@ -51,49 +51,69 @@ public class EntitiesExportImportController extends BaseController { @PostMapping("/export/{entityType}/{entityId}") @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 { EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); try { - return exportImportService.exportEntity(getTenantId(), entityId); + return exportEntity(getCurrentUser(), entityId); } catch (Exception 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(); - - Map>>> result = new HashMap<>(); - exportRequest.getEntities().forEach((entityType, entityIds) -> { - List>> exportDataForEntityType = new LinkedList<>(); - entityIds.forEach(entityId -> { - EntityExportData> exportData = exportImportService.exportEntity(tenantId, entityId); - exportDataForEntityType.add(exportData); - }); - result.put(entityType, exportDataForEntityType); - }); - - exportResponse.setExportData(result); - return exportResponse; - } + +// @PostMapping("/export/batch") +// @PreAuthorize("hasAuthority('TENANT_ADMIN')") +// public EntitiesExportResponse exportEntities(@RequestBody EntitiesExportRequest exportRequest) throws ThingsboardException { +// TenantId tenantId = getTenantId(); +// +// EntitiesExportResponse exportResponse = new EntitiesExportResponse(); +// +// Map>>> result = new HashMap<>(); +// exportRequest.getEntities().forEach((entityType, entityIds) -> { +// List>> exportDataForEntityType = new LinkedList<>(); +// entityIds.forEach(entityId -> { +// EntityExportData> exportData = exportImportService.exportEntity(tenantId, entityId); +// exportDataForEntityType.add(exportData); +// }); +// 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 @PostMapping("/import") @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public , I extends EntityId, D extends EntityExportData> E importEntity(@RequestBody D exportData) throws ThingsboardException { + public & HasName & HasTenantId, I extends EntityId, D extends EntityExportData> EntityImportResult importEntity(@RequestBody D exportData) throws ThingsboardException { try { - return exportImportService.importEntity(getTenantId(), exportData); + return importEntity(getCurrentUser(), exportData); } catch (Exception e) { throw handleException(e); } } -// public void importEntities(@RequestBody ) + + private , I extends EntityId> EntityExportData> exportEntity(SecurityUser user, I entityId) throws ThingsboardException { + checkEntityId(entityId, Operation.READ); + return exportImportService.exportEntity(getTenantId(), entityId); + } + + private & HasName & HasTenantId, I extends EntityId, D extends EntityExportData> EntityImportResult 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 importResult = exportImportService.importEntity(getTenantId(), exportData); + onEntityUpdatedOrCreated(user, importResult.getSavedEntity(), importResult.getOldEntity(), importResult.getOldEntity() == null); + + return importResult; + } } diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index 6479b838b2..aba074ac60 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -78,11 +78,9 @@ import org.thingsboard.server.service.security.permission.Resource; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeSet; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -254,20 +252,7 @@ public class RuleChainController extends BaseController { RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain)); - if (RuleChainType.CORE.equals(savedRuleChain.getType())) { - 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); - } - } + onEntityUpdatedOrCreated(getCurrentUser(), savedRuleChain, null, created); return savedRuleChain; } catch (Exception e) { @@ -294,6 +279,7 @@ public class RuleChainController extends BaseController { RuleChain savedRuleChain = installScripts.createDefaultRuleChain(getCurrentUser().getTenantId(), request.getName()); + tbClusterService.broadcastEntityStateChangeEvent(savedRuleChain.getTenantId(), savedRuleChain.getId(), ComponentLifecycleEvent.CREATED); logEntityAction(savedRuleChain.getId(), savedRuleChain, null, ActionType.ADDED, null); diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java index 9b384b34dd..26d983a694 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java @@ -24,7 +24,9 @@ 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.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.impl.AbstractEntityImportService; import java.util.Collection; import java.util.EnumMap; @@ -51,16 +53,21 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS } // FIXME: somehow validate export data - // FIXME: validate permissions for create or update - // FIXME: send entity lifecycle event @Override - public , I extends EntityId, D extends EntityExportData> E importEntity(TenantId tenantId, D exportData) { + public , I extends EntityId, D extends EntityExportData> EntityImportResult importEntity(TenantId tenantId, D exportData) { EntityType entityType = exportData.getEntityType(); EntityImportService importService = getImportService(entityType); return importService.importEntity(tenantId, exportData); } + @Override + @SuppressWarnings("unchecked") + public , 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") private > EntityExportService getExportService(EntityType entityType) { diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java index 5d25cb9320..75503d2750 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java @@ -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.HasId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.service.expimp.imp.EntityImportResult; public interface EntitiesExportImportService { , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId); - , I extends EntityId, D extends EntityExportData> E importEntity(TenantId tenantId, D exportData); + , I extends EntityId, D extends EntityExportData> EntityImportResult importEntity(TenantId tenantId, D exportData); + + , I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId); } diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportResult.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportResult.java new file mode 100644 index 0000000000..9868670076 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportResult.java @@ -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> { + private E savedEntity; + private E oldEntity; +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java index 195ace0b66..e0f469e751 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java @@ -23,7 +23,8 @@ import org.thingsboard.server.common.data.id.TenantId; public interface EntityImportService, D extends EntityExportData> { - E importEntity(TenantId tenantId, D exportData); + // FIXME: get rid of boilerplate for import result creation and everything else + EntityImportResult importEntity(TenantId tenantId, D exportData); EntityType getEntityType(); diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java index 3f33ba04f0..81855ec8dd 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java @@ -38,7 +38,6 @@ public abstract class AbstractEntityImportService ID getInternalId(TenantId tenantId, ID externalId) { if (externalId == null) { return null; @@ -50,7 +49,7 @@ public abstract class AbstractEntityImportService, ID extends EntityId> T findByExternalOrInternalId(TenantId tenantId, ID externalOrInternalId) { + public final , ID extends EntityId> T findByExternalOrInternalId(TenantId tenantId, ID externalOrInternalId) { ExportableEntityDao dao = getDao(externalOrInternalId.getEntityType()); return Optional.ofNullable(dao.findByTenantIdAndExternalId(tenantId.getId(), externalOrInternalId.getId())) .orElseGet(() -> dao.findByTenantIdAndId(tenantId.getId(), externalOrInternalId.getId())); diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java index 5e563762d6..0e3c6f3004 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.expimp.imp.EntityImportResult; @Service @TbCoreComponent @@ -34,7 +35,7 @@ public class AssetImportService extends AbstractEntityImportService importEntity(TenantId tenantId, AssetExportData exportData) { Asset asset = exportData.getAsset(); Asset existingAsset = findByExternalId(tenantId, asset.getId()); // TODO: extract boiler plate to abstract class ... @@ -51,7 +52,10 @@ public class AssetImportService extends AbstractEntityImportService importResult = new EntityImportResult<>(); + importResult.setSavedEntity(savedAsset); + importResult.setOldEntity(existingAsset); + return importResult; } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/CustomerImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/CustomerImportService.java index 9510cc0700..5f98a7fb66 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/CustomerImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/CustomerImportService.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.expimp.imp.EntityImportResult; @Service @TbCoreComponent @@ -34,7 +35,7 @@ public class CustomerImportService extends AbstractEntityImportService importEntity(TenantId tenantId, CustomerExportData exportData) { Customer customer = exportData.getCustomer(); Customer existingCustomer = findByExternalId(tenantId, customer.getId()); @@ -49,7 +50,10 @@ public class CustomerImportService extends AbstractEntityImportService importResult = new EntityImportResult<>(); + importResult.setSavedEntity(savedCustomer); + importResult.setOldEntity(existingCustomer); + return importResult; } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DashboardImportService.java index eab90bb294..210e7ca841 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DashboardImportService.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.expimp.imp.EntityImportResult; @Service @TbCoreComponent @@ -34,7 +35,7 @@ public class DashboardImportService extends AbstractEntityImportService importEntity(TenantId tenantId, DashboardExportData exportData) { Dashboard dashboard = exportData.getDashboard(); Dashboard existingDashboard = findByExternalId(tenantId, dashboard.getId()); @@ -51,7 +52,10 @@ public class DashboardImportService extends AbstractEntityImportService importResult = new EntityImportResult<>(); + importResult.setSavedEntity(savedDashboard); + importResult.setOldEntity(existingDashboard); + return importResult; } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceImportService.java index 052fa3e1b3..c72ed48b2f 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceImportService.java @@ -18,7 +18,6 @@ package org.thingsboard.server.service.expimp.imp.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; 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.EntityType; 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.dao.device.DeviceService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.action.EntityActionService; +import org.thingsboard.server.service.expimp.imp.EntityImportResult; @Service @TbCoreComponent @@ -38,7 +37,7 @@ public class DeviceImportService extends AbstractEntityImportService importEntity(TenantId tenantId, DeviceExportData exportData) { Device device = exportData.getDevice(); Device existingDevice = findByExternalId(tenantId, device.getId()); // FIXME: !!! // 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 importResult = new EntityImportResult<>(); + importResult.setSavedEntity(savedDevice); + importResult.setOldEntity(existingDevice); + return importResult; } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceProfileImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceProfileImportService.java index 615f62d389..99efefe812 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceProfileImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceProfileImportService.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.expimp.imp.EntityImportResult; @Service @TbCoreComponent @@ -34,7 +35,7 @@ public class DeviceProfileImportService extends AbstractEntityImportService importEntity(TenantId tenantId, DeviceProfileExportData exportData) { DeviceProfile deviceProfile = exportData.getDeviceProfile(); DeviceProfile existingDeviceProfile = findByExternalId(tenantId, deviceProfile.getId()); @@ -54,7 +55,10 @@ public class DeviceProfileImportService extends AbstractEntityImportService importResult = new EntityImportResult<>(); + importResult.setSavedEntity(savedDeviceProfile); + importResult.setOldEntity(existingDeviceProfile); + return importResult; } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/RuleChainImportService.java index eb72f58a7f..92daa19a00 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/RuleChainImportService.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.expimp.imp.EntityImportResult; @Service @TbCoreComponent @@ -37,7 +38,7 @@ public class RuleChainImportService extends AbstractEntityImportService importEntity(TenantId tenantId, RuleChainExportData exportData) { RuleChain ruleChain = exportData.getRuleChain(); RuleChain existingRuleChain = findByExternalId(tenantId, ruleChain.getId()); @@ -69,7 +70,10 @@ public class RuleChainImportService extends AbstractEntityImportService importResult = new EntityImportResult<>(); + importResult.setSavedEntity(savedRuleChain); + importResult.setOldEntity(existingRuleChain); + return importResult; } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java index 36d3cb4b0c..4fd1a5c732 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java @@ -43,6 +43,9 @@ import org.thingsboard.server.common.data.id.HasId; public interface EntityExportData> { @JsonIgnore - EntityType getEntityType(); // fixme: maybe remove if not needed, as well as generic + E getMainEntity(); + + @JsonIgnore + EntityType getEntityType(); // fixme: maybe remove if not needed } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/AssetExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/AssetExportData.java index c0f417bd76..abb268eeb6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/AssetExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/AssetExportData.java @@ -25,6 +25,11 @@ public class AssetExportData implements EntityExportData { private Asset asset; + @Override + public Asset getMainEntity() { + return asset; + } + @Override public EntityType getEntityType() { return EntityType.ASSET; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/CustomerExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/CustomerExportData.java index 31e53db223..02ec9e7efa 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/CustomerExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/CustomerExportData.java @@ -25,6 +25,11 @@ public class CustomerExportData implements EntityExportData { private Customer customer; + @Override + public Customer getMainEntity() { + return customer; + } + @Override public EntityType getEntityType() { return EntityType.CUSTOMER; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DashboardExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DashboardExportData.java index 7860c6c553..f75f2479f6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DashboardExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DashboardExportData.java @@ -25,6 +25,11 @@ public class DashboardExportData implements EntityExportData { private Dashboard dashboard; + @Override + public Dashboard getMainEntity() { + return dashboard; + } + @Override public EntityType getEntityType() { return EntityType.DASHBOARD; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceExportData.java index 05f3299d02..ff3741dabc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceExportData.java @@ -27,6 +27,11 @@ public class DeviceExportData implements EntityExportData { private Device device; private DeviceCredentials credentials; + @Override + public Device getMainEntity() { + return device; + } + @Override public EntityType getEntityType() { return EntityType.DEVICE; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceProfileExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceProfileExportData.java index c030d0b86e..a5d7872cc6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceProfileExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceProfileExportData.java @@ -25,6 +25,11 @@ public class DeviceProfileExportData implements EntityExportData private DeviceProfile deviceProfile; + @Override + public DeviceProfile getMainEntity() { + return deviceProfile; + } + @Override public EntityType getEntityType() { return EntityType.DEVICE_PROFILE; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/RuleChainExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/RuleChainExportData.java index 11fbace911..101f516881 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/RuleChainExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/RuleChainExportData.java @@ -27,6 +27,11 @@ public class RuleChainExportData implements EntityExportData { private RuleChain ruleChain; private RuleChainMetaData metaData; + @Override + public RuleChain getMainEntity() { + return ruleChain; + } + @Override public EntityType getEntityType() { return EntityType.RULE_CHAIN; From 99d0197caa4dc0c31d3ac726b88cec33c96d2bff Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Wed, 16 Mar 2022 18:01:44 +0200 Subject: [PATCH 03/39] Refactor AbstractEntityImportService --- .../server/controller/BaseController.java | 10 ++--- .../DefaultEntitiesExportImportService.java | 32 +++++++++++----- .../expimp/EntitiesExportImportService.java | 4 +- .../expimp/ExportableEntitiesService.java | 26 +++++++++++++ .../imp/impl/AbstractEntityImportService.java | 38 ++++--------------- 5 files changed, 61 insertions(+), 49 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/ExportableEntitiesService.java diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 210a6bea2d..e244bcf680 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -925,7 +925,7 @@ public abstract class BaseController { } public & HasTenantId, I extends EntityId> void onEntityUpdatedOrCreated(User user, E savedEntity, E oldEntity, boolean isNewEntity) { - boolean notifyEdge = false; + boolean notifyEdgeService = false; EntityType entityType = savedEntity.getId().getEntityType(); switch (entityType) { @@ -949,7 +949,7 @@ public abstract class BaseController { tbClusterService.broadcastEntityStateChangeEvent(deviceProfile.getTenantId(), deviceProfile.getId(), isNewEntity ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); otaPackageStateService.update(deviceProfile, isFirmwareChanged, isSoftwareChanged); - notifyEdge = true; + notifyEdgeService = true; break; case RULE_CHAIN: // FIXME: events for rule chain metadata RuleChainType ruleChainType = ((RuleChain) savedEntity).getType(); @@ -959,7 +959,7 @@ public abstract class BaseController { } if (RuleChainType.EDGE.equals(ruleChainType)) { if (!isNewEntity) { - notifyEdge = true; + notifyEdgeService = true; } } break; @@ -967,7 +967,7 @@ public abstract class BaseController { case CUSTOMER: case DASHBOARD: if (!isNewEntity) { - notifyEdge = true; + notifyEdgeService = true; } break; default: @@ -976,7 +976,7 @@ public abstract class BaseController { entityActionService.logEntityAction(user, savedEntity.getId(), savedEntity, savedEntity instanceof HasCustomerId ? ((HasCustomerId) savedEntity).getCustomerId() : null, isNewEntity ? ActionType.ADDED : ActionType.UPDATED, null); - if (notifyEdge) { + if (notifyEdgeService) { sendEntityNotificationMsg(savedEntity.getTenantId(), savedEntity.getId(), isNewEntity ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); } } diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java index 26d983a694..352d26d177 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java @@ -22,23 +22,25 @@ import org.thingsboard.server.common.data.export.EntityExportData; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.queue.util.TbCoreComponent; 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.impl.AbstractEntityImportService; import java.util.Collection; -import java.util.EnumMap; +import java.util.HashMap; import java.util.Map; +import java.util.Optional; // FIXME: review packages and classes naming @Service @TbCoreComponent public class DefaultEntitiesExportImportService implements EntitiesExportImportService { - private final Map> exportServices = new EnumMap<>(EntityType.class); - private final Map> importServices = new EnumMap<>(EntityType.class); + private final Map> exportServices = new HashMap<>(); + private final Map> importServices = new HashMap<>(); + private final Map> daos = new HashMap<>(); // TODO: export and import of the whole tenant @@ -61,14 +63,15 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS return importService.importEntity(tenantId, exportData); } + @Override - @SuppressWarnings("unchecked") public , 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 !!! + ExportableEntityDao dao = getDao(externalId.getEntityType()); + return Optional.ofNullable(dao.findByTenantIdAndExternalId(tenantId.getId(), externalId.getId())) + .orElseGet(() -> dao.findByTenantIdAndId(tenantId.getId(), externalId.getId())); } + @SuppressWarnings("unchecked") private > EntityExportService getExportService(EntityType entityType) { return (EntityExportService) exportServices.get(entityType); @@ -79,15 +82,24 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS return (EntityImportService) importServices.get(entityType); } + @SuppressWarnings("unchecked") + private ExportableEntityDao getDao(EntityType entityType) { + return (ExportableEntityDao) daos.get(entityType); + } + @Autowired - private void setExportImportServices(Collection> exportServices, - Collection> importServices) { + private void setServices(Collection> exportServices, + Collection> importServices, + Collection> daos) { exportServices.forEach(entityExportService -> { this.exportServices.put(entityExportService.getEntityType(), entityExportService); }); importServices.forEach(entityImportService -> { this.importServices.put(entityImportService.getEntityType(), entityImportService); }); + daos.forEach(dao -> { + this.daos.put(dao.getEntityType(), dao); + }); } } diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java index 75503d2750..2a2ac3e1f5 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java @@ -21,12 +21,10 @@ import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.service.expimp.imp.EntityImportResult; -public interface EntitiesExportImportService { +public interface EntitiesExportImportService extends ExportableEntitiesService { , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId); , I extends EntityId, D extends EntityExportData> EntityImportResult importEntity(TenantId tenantId, D exportData); - , I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId); - } diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/ExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/expimp/ExportableEntitiesService.java new file mode 100644 index 0000000000..0af93dfeec --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/ExportableEntitiesService.java @@ -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; + +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.TenantId; + +public interface ExportableEntitiesService { + + , I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java index 81855ec8dd..795af18db3 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java @@ -16,57 +16,33 @@ package org.thingsboard.server.service.expimp.imp.impl; import org.springframework.beans.factory.annotation.Autowired; -import org.thingsboard.server.common.data.EntityType; +import org.springframework.context.annotation.Lazy; import org.thingsboard.server.common.data.export.EntityExportData; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.ExportableEntityDao; +import org.thingsboard.server.service.expimp.ExportableEntitiesService; import org.thingsboard.server.service.expimp.imp.EntityImportService; -import java.util.Collection; -import java.util.EnumMap; -import java.util.Map; -import java.util.Optional; - public abstract class AbstractEntityImportService, D extends EntityExportData> implements EntityImportService { - private final Map> daos = new EnumMap<>(EntityType.class); + @Autowired @Lazy + private ExportableEntitiesService exportableEntitiesService; - public final E findByExternalId(TenantId tenantId, I externalId) { - return findByExternalOrInternalId(tenantId, externalId); + protected final E findByExternalId(TenantId tenantId, I externalId) { + return exportableEntitiesService.findEntityByExternalId(tenantId, externalId); } protected final ID getInternalId(TenantId tenantId, ID externalId) { if (externalId == null) { return null; } - HasId entity = findByExternalOrInternalId(tenantId, externalId); + HasId entity = exportableEntitiesService.findEntityByExternalId(tenantId, externalId); if (entity == null) { throw new IllegalStateException("Cannot find " + externalId.getEntityType() + " by external id " + externalId); } return entity.getId(); } - public final , ID extends EntityId> T findByExternalOrInternalId(TenantId tenantId, ID externalOrInternalId) { - ExportableEntityDao dao = getDao(externalOrInternalId.getEntityType()); - return Optional.ofNullable(dao.findByTenantIdAndExternalId(tenantId.getId(), externalOrInternalId.getId())) - .orElseGet(() -> dao.findByTenantIdAndId(tenantId.getId(), externalOrInternalId.getId())); - } - - - @SuppressWarnings("unchecked") - private ExportableEntityDao getDao(EntityType entityType) { - return (ExportableEntityDao) daos.get(entityType); - } - - @Autowired - private void setDaos(Collection> daos) { - daos.forEach(dao -> this.daos.put(dao.getEntityType(), dao)); - if (!this.daos.containsKey(getEntityType())) { - throw new IllegalStateException(getClass().getSimpleName() + " requires ExportableEntityDao for entity type " + getEntityType()); - } - } - } From 01ad35d8645f51b763a1d3acf2c6cc72f8188bd1 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Wed, 16 Mar 2022 20:02:51 +0200 Subject: [PATCH 04/39] Get rid of boilerplate in import services --- .../EntitiesExportImportController.java | 14 +++---- .../DefaultEntitiesExportImportService.java | 14 +++---- .../expimp/EntitiesExportImportService.java | 6 +-- .../expimp/ExportableEntitiesService.java | 4 +- .../expimp/exp/EntityExportService.java | 4 +- .../expimp/imp/EntityImportResult.java | 4 +- .../expimp/imp/EntityImportService.java | 5 +-- .../imp/impl/AbstractEntityImportService.java | 40 ++++++++++++++++++- .../expimp/imp/impl/AssetImportService.java | 22 +--------- .../imp/impl/CustomerImportService.java | 22 +--------- .../imp/impl/DashboardImportService.java | 20 ++-------- .../expimp/imp/impl/DeviceImportService.java | 38 ++---------------- .../imp/impl/DeviceProfileImportService.java | 22 +--------- .../imp/impl/RuleChainImportService.java | 27 +++---------- .../server/common/data/Customer.java | 3 +- .../server/common/data/Dashboard.java | 3 +- .../server/common/data/Device.java | 3 +- .../server/common/data/DeviceProfile.java | 3 +- .../server/common/data/asset/Asset.java | 3 +- .../data/export/EntitiesExportResponse.java | 3 +- .../common/data/export/EntityExportData.java | 3 +- .../common/data/export/ExportableEntity.java | 39 ++++++++++++++++++ .../server/common/data/rule/RuleChain.java | 3 +- 23 files changed, 133 insertions(+), 172 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/export/ExportableEntity.java diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index 07a033ab03..a3f26bf7b0 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -24,13 +24,11 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; 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.export.EntityExportData; +import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; -import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.expimp.EntitiesExportImportService; import org.thingsboard.server.service.expimp.imp.EntityImportResult; @@ -88,7 +86,7 @@ public class EntitiesExportImportController extends BaseController { @PostMapping("/import") @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public & HasName & HasTenantId, I extends EntityId, D extends EntityExportData> EntityImportResult importEntity(@RequestBody D exportData) throws ThingsboardException { + public EntityImportResult> importEntity(@RequestBody EntityExportData> exportData) throws ThingsboardException { try { return importEntity(getCurrentUser(), exportData); } catch (Exception e) { @@ -97,20 +95,20 @@ public class EntitiesExportImportController extends BaseController { } - private , I extends EntityId> EntityExportData> exportEntity(SecurityUser user, I entityId) throws ThingsboardException { + private EntityExportData> exportEntity(SecurityUser user, EntityId entityId) throws ThingsboardException { checkEntityId(entityId, Operation.READ); return exportImportService.exportEntity(getTenantId(), entityId); } - private & HasName & HasTenantId, I extends EntityId, D extends EntityExportData> EntityImportResult importEntity(SecurityUser user, D exportData) throws ThingsboardException { - E existingEntity = exportImportService.findEntityByExternalId(user.getTenantId(), exportData.getMainEntity().getId()); + private EntityImportResult> importEntity(SecurityUser user, EntityExportData> exportData) throws ThingsboardException { + ExportableEntity 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 importResult = exportImportService.importEntity(getTenantId(), exportData); + EntityImportResult> importResult = exportImportService.importEntity(getTenantId(), exportData); onEntityUpdatedOrCreated(user, importResult.getSavedEntity(), importResult.getOldEntity(), importResult.getOldEntity() == null); return importResult; diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java index 352d26d177..c6fe03a750 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java @@ -19,8 +19,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -47,7 +47,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS // TODO: export and import of the whole customer ? // TODO: relations export and import @Override - public , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId) { + public , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId) { EntityType entityType = entityId.getEntityType(); EntityExportService exportService = getExportService(entityType); @@ -56,16 +56,16 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS // FIXME: somehow validate export data @Override - public , I extends EntityId, D extends EntityExportData> EntityImportResult importEntity(TenantId tenantId, D exportData) { + public , I extends EntityId> EntityImportResult importEntity(TenantId tenantId, EntityExportData exportData) { EntityType entityType = exportData.getEntityType(); - EntityImportService importService = getImportService(entityType); + EntityImportService> importService = getImportService(entityType); return importService.importEntity(tenantId, exportData); } @Override - public , I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId) { + public , I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId) { ExportableEntityDao dao = getDao(externalId.getEntityType()); return Optional.ofNullable(dao.findByTenantIdAndExternalId(tenantId.getId(), externalId.getId())) .orElseGet(() -> dao.findByTenantIdAndId(tenantId.getId(), externalId.getId())); @@ -73,12 +73,12 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS @SuppressWarnings("unchecked") - private > EntityExportService getExportService(EntityType entityType) { + private > EntityExportService getExportService(EntityType entityType) { return (EntityExportService) exportServices.get(entityType); } @SuppressWarnings("unchecked") - private , D extends EntityExportData> EntityImportService getImportService(EntityType entityType) { + private , D extends EntityExportData> EntityImportService getImportService(EntityType entityType) { return (EntityImportService) importServices.get(entityType); } diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java index 2a2ac3e1f5..10361865a2 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java @@ -16,15 +16,15 @@ package org.thingsboard.server.service.expimp; import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.service.expimp.imp.EntityImportResult; public interface EntitiesExportImportService extends ExportableEntitiesService { - , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId); + , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId); - , I extends EntityId, D extends EntityExportData> EntityImportResult importEntity(TenantId tenantId, D exportData); + , I extends EntityId> EntityImportResult importEntity(TenantId tenantId, EntityExportData exportData); } diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/ExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/expimp/ExportableEntitiesService.java index 0af93dfeec..ca401db64b 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/ExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/ExportableEntitiesService.java @@ -15,12 +15,12 @@ */ package org.thingsboard.server.service.expimp; +import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; public interface ExportableEntitiesService { - , I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId); + , I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId); } diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportService.java index 9afa01ff8e..bdd525d5d9 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportService.java @@ -17,11 +17,11 @@ package org.thingsboard.server.service.expimp.exp; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; -public interface EntityExportService> { +public interface EntityExportService> { // FIXME: export relations // FIXME: get rid of boilerplate diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportResult.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportResult.java index 9868670076..90f5d42bc9 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportResult.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportResult.java @@ -16,11 +16,11 @@ package org.thingsboard.server.service.expimp.imp; import lombok.Data; +import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.HasId; @Data -public class EntityImportResult> { +public class EntityImportResult> { private E savedEntity; private E oldEntity; } diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java index e0f469e751..96a0f732ed 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java @@ -17,13 +17,12 @@ package org.thingsboard.server.service.expimp.imp; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; -public interface EntityImportService, D extends EntityExportData> { +public interface EntityImportService, D extends EntityExportData> { - // FIXME: get rid of boilerplate for import result creation and everything else EntityImportResult importEntity(TenantId tenantId, D exportData); EntityType getEntityType(); diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java index 795af18db3..fd635e239c 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java @@ -18,22 +18,58 @@ package org.thingsboard.server.service.expimp.imp.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.service.expimp.ExportableEntitiesService; +import org.thingsboard.server.service.expimp.imp.EntityImportResult; import org.thingsboard.server.service.expimp.imp.EntityImportService; -public abstract class AbstractEntityImportService, D extends EntityExportData> implements EntityImportService { +public abstract class AbstractEntityImportService, D extends EntityExportData> implements EntityImportService { @Autowired @Lazy private ExportableEntitiesService exportableEntitiesService; + // FIXME what if exporting and importing back already exported entity ? (save version and then load it back) + /* + * export entity -> id from env1 -> import this entity -> ... + * + * maybe find not only by external id but by internal too ? but then what if we will try + * */ + @Override + public final EntityImportResult importEntity(TenantId tenantId, D exportData) { + E entity = exportData.getMainEntity(); + E existingEntity = findByExternalId(tenantId, entity.getId()); - protected final E findByExternalId(TenantId tenantId, I externalId) { + entity.setExternalId(entity.getId()); + entity.setTenantId(tenantId); + + if (existingEntity == null) { + entity.setId(null); + } else { + entity.setId(existingEntity.getId()); + } + + E savedEntity = prepareAndSaveEntity(tenantId, entity, existingEntity, exportData); + + EntityImportResult importResult = new EntityImportResult<>(); + importResult.setSavedEntity(savedEntity); + importResult.setOldEntity(existingEntity); + return importResult; + } + + protected abstract E prepareAndSaveEntity(TenantId tenantId, E entity, E existingEntity, D exportData); + + + private E findByExternalId(TenantId tenantId, I externalId) { return exportableEntitiesService.findEntityByExternalId(tenantId, externalId); } + // TODO or maybe set as additional config whether to update related entities when device already exists ? + // TODO: or also whether to ignore not found internal ids + + // FIXME: review use cases for version controlling: in the same tenant, between tenants, between environments and different tenants protected final ID getInternalId(TenantId tenantId, ID externalId) { if (externalId == null) { return null; diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java index 0e3c6f3004..4e6fa2b33e 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.imp.EntityImportResult; @Service @TbCoreComponent @@ -35,27 +34,10 @@ public class AssetImportService extends AbstractEntityImportService importEntity(TenantId tenantId, AssetExportData exportData) { - Asset asset = exportData.getAsset(); - Asset existingAsset = findByExternalId(tenantId, asset.getId()); // TODO: extract boiler plate to abstract class ... - - asset.setExternalId(asset.getId()); - asset.setTenantId(tenantId); - - if (existingAsset == null) { - asset.setId(null); - } else { - asset.setId(existingAsset.getId()); - } - + protected Asset prepareAndSaveEntity(TenantId tenantId, Asset asset, Asset existingAsset, AssetExportData exportData) { asset.setCustomerId(getInternalId(tenantId, asset.getCustomerId())); - Asset savedAsset = assetService.saveAsset(asset); - - EntityImportResult importResult = new EntityImportResult<>(); - importResult.setSavedEntity(savedAsset); - importResult.setOldEntity(existingAsset); - return importResult; + return assetService.saveAsset(asset); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/CustomerImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/CustomerImportService.java index 5f98a7fb66..559cc1cd18 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/CustomerImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/CustomerImportService.java @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.imp.EntityImportResult; @Service @TbCoreComponent @@ -35,25 +34,8 @@ public class CustomerImportService extends AbstractEntityImportService importEntity(TenantId tenantId, CustomerExportData exportData) { - Customer customer = exportData.getCustomer(); - Customer existingCustomer = findByExternalId(tenantId, customer.getId()); - - customer.setExternalId(customer.getId()); - customer.setTenantId(tenantId); - - if (existingCustomer == null) { - customer.setId(null); - } else { - customer.setId(existingCustomer.getId()); - } - - Customer savedCustomer = customerService.saveCustomer(customer); - - EntityImportResult importResult = new EntityImportResult<>(); - importResult.setSavedEntity(savedCustomer); - importResult.setOldEntity(existingCustomer); - return importResult; + protected Customer prepareAndSaveEntity(TenantId tenantId, Customer customer, Customer existingCustomer, CustomerExportData exportData) { + return customerService.saveCustomer(customer); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DashboardImportService.java index 210e7ca841..916d95ea1c 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DashboardImportService.java @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.imp.EntityImportResult; @Service @TbCoreComponent @@ -35,27 +34,14 @@ public class DashboardImportService extends AbstractEntityImportService importEntity(TenantId tenantId, DashboardExportData exportData) { - Dashboard dashboard = exportData.getDashboard(); - Dashboard existingDashboard = findByExternalId(tenantId, dashboard.getId()); - - dashboard.setExternalId(dashboard.getId()); - dashboard.setTenantId(tenantId); - + protected Dashboard prepareAndSaveEntity(TenantId tenantId, Dashboard dashboard, Dashboard existingDashboard, DashboardExportData exportData) { if (existingDashboard == null) { - dashboard.setId(null); dashboard.setAssignedCustomers(null); // FIXME: need to assign dashboard to customers ? } else { - dashboard.setId(existingDashboard.getId()); - dashboard.setAssignedCustomers(existingDashboard.getAssignedCustomers()); // we left them untouched (FIXME) + dashboard.setAssignedCustomers(existingDashboard.getAssignedCustomers()); } - Dashboard savedDashboard = dashboardService.saveDashboard(dashboard); - - EntityImportResult importResult = new EntityImportResult<>(); - importResult.setSavedEntity(savedDashboard); - importResult.setOldEntity(existingDashboard); - return importResult; + return dashboardService.saveDashboard(dashboard); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceImportService.java index c72ed48b2f..c41e586b83 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceImportService.java @@ -17,7 +17,6 @@ package org.thingsboard.server.service.expimp.imp.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.export.impl.DeviceExportData; @@ -25,7 +24,6 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.imp.EntityImportResult; @Service @TbCoreComponent @@ -35,44 +33,14 @@ public class DeviceImportService extends AbstractEntityImportService importEntity(TenantId tenantId, DeviceExportData exportData) { - Device device = exportData.getDevice(); - Device existingDevice = findByExternalId(tenantId, device.getId()); // FIXME: !!! - // what if exporting and importing back already exported entity ? (save version and then load it back) - /* - * export entity -> id from env1 -> import this entity -> ... - * - * maybe find not only by external id but by internal too ? but then what if we will try - * */ - - device.setExternalId(device.getId()); - device.setTenantId(tenantId); - - if (existingDevice == null) { - device.setId(null); - device.setCustomerId(null); // FIXME: find and set customer - } else { - device.setId(existingDevice.getId()); - device.setCustomerId(existingDevice.getCustomerId()); - } - - // TODO or maybe set as additional config whether to update related entities when device already exists ? - // TODO: or also whether to ignore not found internal ids - - // FIXME: review use cases for version controlling: in the same tenant, between tenants, between environments and different tenants - + protected Device prepareAndSaveEntity(TenantId tenantId, Device device, Device existingDevice, DeviceExportData exportData) { + device.setCustomerId(getInternalId(tenantId, device.getCustomerId())); device.setDeviceProfileId(getInternalId(tenantId, device.getDeviceProfileId())); device.setFirmwareId(getInternalId(tenantId, device.getFirmwareId())); device.setSoftwareId(getInternalId(tenantId, device.getSoftwareId())); - Device savedDevice = deviceService.saveDeviceWithCredentials(device, exportData.getCredentials()); - - EntityImportResult importResult = new EntityImportResult<>(); - importResult.setSavedEntity(savedDevice); - importResult.setOldEntity(existingDevice); - return importResult; + return deviceService.saveDeviceWithCredentials(device, exportData.getCredentials()); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceProfileImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceProfileImportService.java index 99efefe812..8be8bf6b11 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceProfileImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceProfileImportService.java @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.imp.EntityImportResult; @Service @TbCoreComponent @@ -35,30 +34,13 @@ public class DeviceProfileImportService extends AbstractEntityImportService importEntity(TenantId tenantId, DeviceProfileExportData exportData) { - DeviceProfile deviceProfile = exportData.getDeviceProfile(); - DeviceProfile existingDeviceProfile = findByExternalId(tenantId, deviceProfile.getId()); - - deviceProfile.setExternalId(deviceProfile.getId()); - deviceProfile.setTenantId(tenantId); - - if (existingDeviceProfile == null) { - deviceProfile.setId(null); - } else { - deviceProfile.setId(existingDeviceProfile.getId()); - } - + protected DeviceProfile prepareAndSaveEntity(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfile existingDeviceProfile, DeviceProfileExportData exportData) { deviceProfile.setDefaultRuleChainId(getInternalId(tenantId, deviceProfile.getDefaultRuleChainId())); deviceProfile.setDefaultDashboardId(getInternalId(tenantId, deviceProfile.getDefaultDashboardId())); deviceProfile.setFirmwareId(getInternalId(tenantId, deviceProfile.getFirmwareId())); deviceProfile.setSoftwareId(getInternalId(tenantId, deviceProfile.getSoftwareId())); - DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); - - EntityImportResult importResult = new EntityImportResult<>(); - importResult.setSavedEntity(savedDeviceProfile); - importResult.setOldEntity(existingDeviceProfile); - return importResult; + return deviceProfileService.saveDeviceProfile(deviceProfile); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/RuleChainImportService.java index 92daa19a00..dbc6b82545 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/RuleChainImportService.java @@ -26,7 +26,6 @@ import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.imp.EntityImportResult; @Service @TbCoreComponent @@ -38,27 +37,16 @@ public class RuleChainImportService extends AbstractEntityImportService importEntity(TenantId tenantId, RuleChainExportData exportData) { - RuleChain ruleChain = exportData.getRuleChain(); - RuleChain existingRuleChain = findByExternalId(tenantId, ruleChain.getId()); - - ruleChain.setExternalId(ruleChain.getId()); - ruleChain.setTenantId(tenantId); + protected RuleChain prepareAndSaveEntity(TenantId tenantId, RuleChain ruleChain, RuleChain existingRuleChain, RuleChainExportData exportData) { ruleChain.setFirstRuleNodeId(null); // will be set during metadata persisting - - if (existingRuleChain == null) { - ruleChain.setId(null); - } else { - ruleChain.setId(existingRuleChain.getId()); + if (existingRuleChain != null) { + ruleChainService.deleteRuleNodes(tenantId, existingRuleChain.getId()); } - RuleChain savedRuleChain = ruleChainService.saveRuleChain(ruleChain); + ruleChain = ruleChainService.saveRuleChain(ruleChain); - if (ruleChain.getId() != null) { - ruleChainService.deleteRuleNodes(tenantId, ruleChain.getId()); - } RuleChainMetaData metaData = exportData.getMetaData(); - metaData.setRuleChainId(savedRuleChain.getId()); + metaData.setRuleChainId(ruleChain.getId()); metaData.getNodes().forEach(ruleNode -> { ruleNode.setId(null); ruleNode.setRuleChainId(null); @@ -70,10 +58,7 @@ public class RuleChainImportService extends AbstractEntityImportService importResult = new EntityImportResult<>(); - importResult.setSavedEntity(savedRuleChain); - importResult.setOldEntity(existingRuleChain); - return importResult; + return ruleChain; } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java index 065e0c5db5..f87b44b0eb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java @@ -22,12 +22,13 @@ import com.fasterxml.jackson.databind.JsonNode; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; +import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; -public class Customer extends ContactBased implements HasTenantId { +public class Customer extends ContactBased implements HasTenantId, ExportableEntity { private static final long serialVersionUID = -1599722990298929275L; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java b/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java index 4b69534bc0..c8b0df98b4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java @@ -19,9 +19,10 @@ import com.fasterxml.jackson.databind.JsonNode; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; +import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.DashboardId; -public class Dashboard extends DashboardInfo { +public class Dashboard extends DashboardInfo implements ExportableEntity { private static final long serialVersionUID = 872682138346187503L; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java index 519cffc39a..3bd369591f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java @@ -25,6 +25,7 @@ import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.device.data.DeviceData; +import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -40,7 +41,7 @@ import java.util.Optional; @ApiModel @EqualsAndHashCode(callSuper = true) @Slf4j -public class Device extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId, HasOtaPackage { +public class Device extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId, HasOtaPackage, ExportableEntity { private static final long serialVersionUID = 2807343040519543363L; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java index 6161cd6a5c..0bd770d02d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java @@ -24,6 +24,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; +import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.OtaPackageId; @@ -43,7 +44,7 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn @ToString(exclude = {"image", "profileDataBytes"}) @EqualsAndHashCode(callSuper = true) @Slf4j -public class DeviceProfile extends SearchTextBased implements HasName, HasTenantId, HasOtaPackage { +public class DeviceProfile extends SearchTextBased implements HasName, HasTenantId, HasOtaPackage, ExportableEntity { private static final long serialVersionUID = 6998485460273302018L; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java index e0924c63ee..694a0162bc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.HasCustomerId; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; +import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -35,7 +36,7 @@ import java.util.Optional; @ApiModel @EqualsAndHashCode(callSuper = true) -public class Asset extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId { +public class Asset extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId, ExportableEntity { private static final long serialVersionUID = 2807343040519543363L; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportResponse.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportResponse.java index cab75313d4..fdf08b2cdb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportResponse.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportResponse.java @@ -18,12 +18,11 @@ package org.thingsboard.server.common.data.export; import lombok.Data; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.HasId; import java.util.List; import java.util.Map; @Data public class EntitiesExportResponse { - private Map>>> exportData; + private Map>>> exportData; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java index 4fd1a5c732..b428c3e6b5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java @@ -28,7 +28,6 @@ import org.thingsboard.server.common.data.export.impl.DeviceExportData; import org.thingsboard.server.common.data.export.impl.DeviceProfileExportData; import org.thingsboard.server.common.data.export.impl.RuleChainExportData; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.HasId; @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(property = "entityType", use = JsonTypeInfo.Id.NAME) @@ -40,7 +39,7 @@ import org.thingsboard.server.common.data.id.HasId; @Type(name = "DASHBOARD", value = DashboardExportData.class), @Type(name = "CUSTOMER", value = CustomerExportData.class) }) -public interface EntityExportData> { +public interface EntityExportData> { @JsonIgnore E getMainEntity(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/ExportableEntity.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/ExportableEntity.java new file mode 100644 index 0000000000..89b0cc3de7 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/ExportableEntity.java @@ -0,0 +1,39 @@ +/** + * 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.common.data.export; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.springframework.data.annotation.Transient; +import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.TenantId; + +public interface ExportableEntity extends HasId, HasTenantId, HasName { + + @Transient @JsonIgnore + I getId(); + @Transient @JsonIgnore + void setId(I id); + + I getExternalId(); + void setExternalId(I externalId); + + TenantId getTenantId(); + void setTenantId(TenantId tenantId); + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java index 7ff2b0dc2b..370087deb3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java @@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; +import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; @@ -35,7 +36,7 @@ import org.thingsboard.server.common.data.validation.NoXss; @Data @EqualsAndHashCode(callSuper = true) @Slf4j -public class RuleChain extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId { +public class RuleChain extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, ExportableEntity { private static final long serialVersionUID = -5656679015121935465L; From 5ee056ff8da694659a5ceb98f3cf92209a40126a Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Fri, 25 Mar 2022 15:32:54 +0200 Subject: [PATCH 05/39] Export/import API refactoring --- .../EntitiesExportImportController.java | 70 +++++++++---------- .../DefaultEntitiesExportImportService.java | 37 ++++++---- .../expimp/EntitiesExportImportService.java | 6 +- .../expimp/ExportableEntitiesService.java | 2 + .../expimp/exp/EntityExportService.java | 6 +- .../expimp/exp/EntityExportSettings.java | 17 ++--- .../exp/impl/AbstractEntityExportService.java | 63 +++++++++++++++++ .../expimp/exp/impl/AssetExportService.java | 17 +---- .../exp/impl/CustomerExportService.java | 17 +---- .../exp/impl/DashboardExportService.java | 17 +---- .../expimp/exp/impl/DeviceExportService.java | 18 +++-- .../exp/impl/DeviceProfileExportService.java | 17 +---- .../exp/impl/RuleChainExportService.java | 16 ++--- .../expimp/imp/EntityImportService.java | 8 ++- .../expimp/imp/EntityImportSettings.java | 18 ++--- .../imp/impl/AbstractEntityImportService.java | 34 +++++---- .../expimp/imp/impl/AssetImportService.java | 3 +- .../imp/impl/CustomerImportService.java | 3 +- .../imp/impl/DashboardImportService.java | 5 +- .../expimp/imp/impl/DeviceImportService.java | 3 +- .../imp/impl/DeviceProfileImportService.java | 3 +- .../imp/impl/RuleChainImportService.java | 7 +- .../server/common/data/Customer.java | 2 +- .../common/data/export/EntityExportData.java | 13 ++-- .../common/data/export/ExportableEntity.java | 4 -- .../data/export/impl/AssetExportData.java | 11 +-- .../data/export/impl/CustomerExportData.java | 11 +-- .../data/export/impl/DashboardExportData.java | 11 +-- .../data/export/impl/DeviceExportData.java | 10 +-- .../export/impl/DeviceProfileExportData.java | 11 +-- .../data/export/impl/RuleChainExportData.java | 10 +-- .../server/common/data/id/IdBased.java | 37 +++++----- .../server/dao/ExportableEntityDao.java | 2 +- 33 files changed, 265 insertions(+), 244 deletions(-) rename common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportResponse.java => application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportSettings.java (65%) create mode 100644 application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AbstractEntityExportService.java rename common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportRequest.java => application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportSettings.java (61%) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index a3f26bf7b0..96bc70656f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -17,11 +17,13 @@ package org.thingsboard.server.controller; import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; +import org.apache.commons.collections.CollectionUtils; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -29,14 +31,18 @@ import org.thingsboard.server.common.data.export.EntityExportData; import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.expimp.EntitiesExportImportService; +import org.thingsboard.server.service.expimp.exp.EntityExportSettings; import org.thingsboard.server.service.expimp.imp.EntityImportResult; +import org.thingsboard.server.service.expimp.imp.EntityImportSettings; 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.UUID; +import java.util.stream.Collectors; @RestController @RequestMapping("/api/entities") @@ -49,66 +55,60 @@ public class EntitiesExportImportController extends BaseController { @PostMapping("/export/{entityType}/{entityId}") @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public EntityExportData exportEntity(@ApiParam(allowableValues = "DEVICE, DEVICE_PROFILE, ASSET, RULE_CHAIN, DASHBOARD, CUSTOMER") @PathVariable EntityType entityType, - @PathVariable("entityId") UUID entityUuid) throws ThingsboardException { + public EntityExportData exportEntity(@ApiParam(allowableValues = "DEVICE, DEVICE_PROFILE, ASSET, RULE_CHAIN, DASHBOARD, CUSTOMER") + @PathVariable EntityType entityType, + @PathVariable("entityId") UUID entityUuid, + @RequestParam(defaultValue = "false") boolean exportInboundRelations) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); + EntityExportSettings exportSettings = EntityExportSettings.builder() + .exportInboundRelations(exportInboundRelations) + .build(); try { - return exportEntity(getCurrentUser(), entityId); + return exportEntity(getCurrentUser(), entityId, exportSettings); } catch (Exception 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(); -// -// Map>>> result = new HashMap<>(); -// exportRequest.getEntities().forEach((entityType, entityIds) -> { -// List>> exportDataForEntityType = new LinkedList<>(); -// entityIds.forEach(entityId -> { -// EntityExportData> exportData = exportImportService.exportEntity(tenantId, entityId); -// exportDataForEntityType.add(exportData); -// }); -// 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 - - @PostMapping("/import") @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public EntityImportResult> importEntity(@RequestBody EntityExportData> exportData) throws ThingsboardException { + public EntityImportResult> importEntity(@RequestBody EntityExportData> exportData, + @RequestParam(defaultValue = "false") boolean importInboundRelations) throws ThingsboardException { + EntityImportSettings importSettings = EntityImportSettings.builder() + .importInboundRelations(importInboundRelations) + .build(); try { - return importEntity(getCurrentUser(), exportData); + return importEntity(getCurrentUser(), exportData, importSettings); } catch (Exception e) { throw handleException(e); } } - private EntityExportData> exportEntity(SecurityUser user, EntityId entityId) throws ThingsboardException { + // TODO [viacheslav]: export and import of batches + // TODO [viacheslav]: api to export and import whole customer, whole tenant + + private EntityExportData> exportEntity(SecurityUser user, EntityId entityId, EntityExportSettings exportSettings) throws ThingsboardException { checkEntityId(entityId, Operation.READ); - return exportImportService.exportEntity(getTenantId(), entityId); + return exportImportService.exportEntity(user.getTenantId(), entityId, exportSettings); } - private EntityImportResult> importEntity(SecurityUser user, EntityExportData> exportData) throws ThingsboardException { + private EntityImportResult> importEntity(SecurityUser user, EntityExportData> exportData, EntityImportSettings importSettings) throws ThingsboardException { ExportableEntity 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 + if (existingEntity != null) {// todo [viacheslav] maybe need to extract permission check to BaseController and put there permission checks from other controllers + checkEntityId(existingEntity.getId(), Operation.WRITE); + if (importSettings.isImportInboundRelations() && CollectionUtils.isNotEmpty(exportData.getInboundRelations())) { + for (EntityId fromId : exportData.getInboundRelations().stream().map(EntityRelation::getFrom).collect(Collectors.toSet())) { + // FIXME [viacheslav]: fromId is external +// checkEntityId(fromId, Operation.WRITE); + } + } } else { checkEntity(null, exportData.getMainEntity(), Resource.of(exportData.getEntityType())); } - EntityImportResult> importResult = exportImportService.importEntity(getTenantId(), exportData); + EntityImportResult> importResult = exportImportService.importEntity(getTenantId(), exportData, importSettings); onEntityUpdatedOrCreated(user, importResult.getSavedEntity(), importResult.getOldEntity(), importResult.getOldEntity() == null); return importResult; diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java index c6fe03a750..0136bd1d52 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java @@ -25,56 +25,63 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.expimp.exp.EntityExportService; +import org.thingsboard.server.service.expimp.exp.EntityExportSettings; import org.thingsboard.server.service.expimp.imp.EntityImportResult; import org.thingsboard.server.service.expimp.imp.EntityImportService; +import org.thingsboard.server.service.expimp.imp.EntityImportSettings; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Optional; -// FIXME: review packages and classes naming +// FIXME [viacheslav]: review packages and classes naming @Service @TbCoreComponent public class DefaultEntitiesExportImportService implements EntitiesExportImportService { - private final Map> exportServices = new HashMap<>(); + private final Map> exportServices = new HashMap<>(); private final Map> importServices = new HashMap<>(); private final Map> daos = new HashMap<>(); - // TODO: export and import of the whole tenant - // TODO: export and import of the whole customer ? - // TODO: relations export and import + // TODO [viacheslav]: export and import of the whole tenant + // TODO [viacheslav]: export and import of the whole customer ? @Override - public , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId) { + public , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId, EntityExportSettings exportSettings) { EntityType entityType = entityId.getEntityType(); - EntityExportService exportService = getExportService(entityType); + EntityExportService> exportService = getExportService(entityType); - return exportService.getExportData(tenantId, entityId); + return exportService.getExportData(tenantId, entityId, exportSettings); } - // FIXME: somehow validate export data + // FIXME [viacheslav]: somehow validate export data @Override - public , I extends EntityId> EntityImportResult importEntity(TenantId tenantId, EntityExportData exportData) { + public , I extends EntityId> EntityImportResult importEntity(TenantId tenantId, EntityExportData exportData, EntityImportSettings importSettings) { EntityType entityType = exportData.getEntityType(); EntityImportService> importService = getImportService(entityType); - return importService.importEntity(tenantId, exportData); + return importService.importEntity(tenantId, exportData, importSettings); } + @Override + public , I extends EntityId> E findEntityById(TenantId tenantId, I id) { + ExportableEntityDao dao = getDao(id.getEntityType()); + return dao.findByTenantIdAndId(tenantId.getId(), id.getId()); + } + @Override public , I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId) { ExportableEntityDao dao = getDao(externalId.getEntityType()); return Optional.ofNullable(dao.findByTenantIdAndExternalId(tenantId.getId(), externalId.getId())) - .orElseGet(() -> dao.findByTenantIdAndId(tenantId.getId(), externalId.getId())); + .orElseGet(() -> findEntityById(tenantId, externalId)); } @SuppressWarnings("unchecked") - private > EntityExportService getExportService(EntityType entityType) { - return (EntityExportService) exportServices.get(entityType); + private , D extends EntityExportData> EntityExportService getExportService(EntityType entityType) { + return (EntityExportService) exportServices.get(entityType); } @SuppressWarnings("unchecked") @@ -88,7 +95,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS } @Autowired - private void setServices(Collection> exportServices, + private void setServices(Collection> exportServices, Collection> importServices, Collection> daos) { exportServices.forEach(entityExportService -> { diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java index 10361865a2..752d4b6315 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java @@ -19,12 +19,14 @@ import org.thingsboard.server.common.data.export.EntityExportData; import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.service.expimp.exp.EntityExportSettings; import org.thingsboard.server.service.expimp.imp.EntityImportResult; +import org.thingsboard.server.service.expimp.imp.EntityImportSettings; public interface EntitiesExportImportService extends ExportableEntitiesService { - , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId); + , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId, EntityExportSettings exportSettings); - , I extends EntityId> EntityImportResult importEntity(TenantId tenantId, EntityExportData exportData); + , I extends EntityId> EntityImportResult importEntity(TenantId tenantId, EntityExportData exportData, EntityImportSettings importSettings); } diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/ExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/expimp/ExportableEntitiesService.java index ca401db64b..17151e3889 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/ExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/ExportableEntitiesService.java @@ -21,6 +21,8 @@ import org.thingsboard.server.common.data.id.TenantId; public interface ExportableEntitiesService { + , I extends EntityId> E findEntityById(TenantId tenantId, I id); + , I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId); } diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportService.java index bdd525d5d9..7c7fc49240 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportService.java @@ -21,11 +21,9 @@ import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -public interface EntityExportService> { +public interface EntityExportService, D extends EntityExportData> { - // FIXME: export relations - // FIXME: get rid of boilerplate - EntityExportData getExportData(TenantId tenantId, I entityId); + D getExportData(TenantId tenantId, I entityId, EntityExportSettings exportSettings); EntityType getEntityType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportResponse.java b/application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportSettings.java similarity index 65% rename from common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportResponse.java rename to application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportSettings.java index fdf08b2cdb..c70b0b2216 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportResponse.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportSettings.java @@ -13,16 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.export; +package org.thingsboard.server.service.expimp.exp; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.id.EntityId; - -import java.util.List; -import java.util.Map; +import lombok.NoArgsConstructor; @Data -public class EntitiesExportResponse { - private Map>>> exportData; +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class EntityExportSettings { + private boolean exportInboundRelations; } diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AbstractEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AbstractEntityExportService.java new file mode 100644 index 0000000000..f6c2f5d5f7 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AbstractEntityExportService.java @@ -0,0 +1,63 @@ +/** + * 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.exp.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.thingsboard.server.common.data.export.EntityExportData; +import org.thingsboard.server.common.data.export.ExportableEntity; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.service.expimp.ExportableEntitiesService; +import org.thingsboard.server.service.expimp.exp.EntityExportService; +import org.thingsboard.server.service.expimp.exp.EntityExportSettings; + +import java.util.List; + +public abstract class AbstractEntityExportService, D extends EntityExportData> implements EntityExportService { + + @Autowired @Lazy + private ExportableEntitiesService exportableEntitiesService; + @Autowired + private RelationService relationService; + + + @Override + public final D getExportData(TenantId tenantId, I entityId, EntityExportSettings exportSettings) { + D exportData = newExportData(); + + E mainEntity = exportableEntitiesService.findEntityById(tenantId, entityId); + exportData.setMainEntity(mainEntity); + setRelatedEntities(tenantId, mainEntity, exportData); + if (exportSettings.isExportInboundRelations()) { + exportData.setInboundRelations(getInboundRelations(tenantId, entityId)); + } + + return exportData; + } + + protected List getInboundRelations(TenantId tenantId, I entityId) { + return relationService.findByTo(tenantId, entityId, RelationTypeGroup.COMMON); // FIXME [viacheslav]: RelationTypeGroup + } + + protected void setRelatedEntities(TenantId tenantId, E mainEntity, D exportData) {} + + protected abstract D newExportData(); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AssetExportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AssetExportService.java index bb4b68bb29..ec1b2dddc3 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AssetExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AssetExportService.java @@ -15,31 +15,20 @@ */ package org.thingsboard.server.service.expimp.exp.impl; -import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.export.EntityExportData; import org.thingsboard.server.common.data.export.impl.AssetExportData; import org.thingsboard.server.common.data.id.AssetId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.exp.EntityExportService; @Service @TbCoreComponent -@RequiredArgsConstructor -public class AssetExportService implements EntityExportService { - - private final AssetService assetService; - +public class AssetExportService extends AbstractEntityExportService { @Override - public EntityExportData getExportData(TenantId tenantId, AssetId assetId) { - AssetExportData exportData = new AssetExportData(); - exportData.setAsset(assetService.findAssetById(tenantId, assetId)); - return exportData; + protected AssetExportData newExportData() { + return new AssetExportData(); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/CustomerExportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/CustomerExportService.java index 7430f9ddd3..32e33f757c 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/CustomerExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/CustomerExportService.java @@ -15,31 +15,20 @@ */ package org.thingsboard.server.service.expimp.exp.impl; -import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.EntityExportData; import org.thingsboard.server.common.data.export.impl.CustomerExportData; import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.exp.EntityExportService; @Service @TbCoreComponent -@RequiredArgsConstructor -public class CustomerExportService implements EntityExportService { - - private final CustomerService customerService; - +public class CustomerExportService extends AbstractEntityExportService { @Override - public EntityExportData getExportData(TenantId tenantId, CustomerId customerId) { - CustomerExportData exportData = new CustomerExportData(); - exportData.setCustomer(customerService.findCustomerById(tenantId, customerId)); - return exportData; + protected CustomerExportData newExportData() { + return new CustomerExportData(); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DashboardExportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DashboardExportService.java index 87959429d9..be86c41c9a 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DashboardExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DashboardExportService.java @@ -15,31 +15,20 @@ */ package org.thingsboard.server.service.expimp.exp.impl; -import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.EntityExportData; import org.thingsboard.server.common.data.export.impl.DashboardExportData; import org.thingsboard.server.common.data.id.DashboardId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.exp.EntityExportService; @Service @TbCoreComponent -@RequiredArgsConstructor -public class DashboardExportService implements EntityExportService { - - private final DashboardService dashboardService; - +public class DashboardExportService extends AbstractEntityExportService { @Override - public EntityExportData getExportData(TenantId tenantId, DashboardId dashboardId) { - DashboardExportData exportData = new DashboardExportData(); - exportData.setDashboard(dashboardService.findDashboardById(tenantId, dashboardId)); - return exportData; + protected DashboardExportData newExportData() { + return new DashboardExportData(); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceExportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceExportService.java index 860aaa9b1d..c3542dc95b 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceExportService.java @@ -19,30 +19,28 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.EntityExportData; import org.thingsboard.server.common.data.export.impl.DeviceExportData; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.device.DeviceCredentialsService; -import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.exp.EntityExportService; @Service @TbCoreComponent @RequiredArgsConstructor -public class DeviceExportService implements EntityExportService { +public class DeviceExportService extends AbstractEntityExportService { - private final DeviceService deviceService; private final DeviceCredentialsService deviceCredentialsService; @Override - public EntityExportData getExportData(TenantId tenantId, DeviceId deviceId) { - DeviceExportData exportData = new DeviceExportData(); - exportData.setDevice(deviceService.findDeviceById(tenantId, deviceId)); - exportData.setCredentials(deviceCredentialsService.findDeviceCredentialsByDeviceId(TenantId.SYS_TENANT_ID, deviceId)); - return exportData; + protected void setRelatedEntities(TenantId tenantId, Device device, DeviceExportData exportData) { + exportData.setCredentials(deviceCredentialsService.findDeviceCredentialsByDeviceId(TenantId.SYS_TENANT_ID, device.getId())); + } + + @Override + protected DeviceExportData newExportData() { + return new DeviceExportData(); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceProfileExportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceProfileExportService.java index 425dbfd5be..03787ab30e 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceProfileExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceProfileExportService.java @@ -15,31 +15,20 @@ */ package org.thingsboard.server.service.expimp.exp.impl; -import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.EntityExportData; import org.thingsboard.server.common.data.export.impl.DeviceProfileExportData; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.exp.EntityExportService; @Service @TbCoreComponent -@RequiredArgsConstructor -public class DeviceProfileExportService implements EntityExportService { - - private final DeviceProfileService deviceProfileService; - +public class DeviceProfileExportService extends AbstractEntityExportService { @Override - public EntityExportData getExportData(TenantId tenantId, DeviceProfileId deviceProfileId) { - DeviceProfileExportData exportData = new DeviceProfileExportData(); - exportData.setDeviceProfile(deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId)); - return exportData; + protected DeviceProfileExportData newExportData() { + return new DeviceProfileExportData(); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/RuleChainExportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/RuleChainExportService.java index 7a85281d63..0e2d24e990 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/RuleChainExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/RuleChainExportService.java @@ -18,29 +18,29 @@ package org.thingsboard.server.service.expimp.exp.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.EntityExportData; import org.thingsboard.server.common.data.export.impl.RuleChainExportData; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.exp.EntityExportService; @Service @TbCoreComponent @RequiredArgsConstructor -public class RuleChainExportService implements EntityExportService { +public class RuleChainExportService extends AbstractEntityExportService { private final RuleChainService ruleChainService; @Override - public EntityExportData getExportData(TenantId tenantId, RuleChainId ruleChainId) { - RuleChainExportData exportData = new RuleChainExportData(); - exportData.setRuleChain(ruleChainService.findRuleChainById(tenantId, ruleChainId)); - exportData.setMetaData(ruleChainService.loadRuleChainMetaData(tenantId, ruleChainId)); - return exportData; + protected void setRelatedEntities(TenantId tenantId, RuleChain ruleChain, RuleChainExportData exportData) { + exportData.setMetaData(ruleChainService.loadRuleChainMetaData(tenantId, ruleChain.getId())); + } + + @Override + protected RuleChainExportData newExportData() { + return new RuleChainExportData(); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java index 96a0f732ed..693ff310bf 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java @@ -23,7 +23,13 @@ import org.thingsboard.server.common.data.id.TenantId; public interface EntityImportService, D extends EntityExportData> { - EntityImportResult importEntity(TenantId tenantId, D exportData); + /* + * TODO [viacheslav]: should be as options: + * to update related entities e.g. firmware or device profile if entity already exists + * to delete current relations when importing new + * to ignore when cannot find linked entity by external id + * */ + EntityImportResult importEntity(TenantId tenantId, D exportData, EntityImportSettings importSettings); EntityType getEntityType(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportRequest.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportSettings.java similarity index 61% rename from common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportRequest.java rename to application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportSettings.java index d3502695c0..1b29139e6b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/EntitiesExportRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportSettings.java @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.export; +package org.thingsboard.server.service.expimp.imp; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.id.EntityId; - -import java.util.List; -import java.util.Map; +import lombok.NoArgsConstructor; @Data -public class EntitiesExportRequest { - // todo: should be processed in a specific order, e.g. device profiles, then devices, then relations, etc. - private Map> entities; +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class EntityImportSettings { + private boolean importInboundRelations; } diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java index fd635e239c..fd27feeea9 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.expimp.imp.impl; +import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.thingsboard.server.common.data.export.EntityExportData; @@ -22,23 +23,22 @@ import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.service.expimp.ExportableEntitiesService; import org.thingsboard.server.service.expimp.imp.EntityImportResult; import org.thingsboard.server.service.expimp.imp.EntityImportService; +import org.thingsboard.server.service.expimp.imp.EntityImportSettings; public abstract class AbstractEntityImportService, D extends EntityExportData> implements EntityImportService { @Autowired @Lazy private ExportableEntitiesService exportableEntitiesService; + @Autowired + private RelationService relationService; + - // FIXME what if exporting and importing back already exported entity ? (save version and then load it back) - /* - * export entity -> id from env1 -> import this entity -> ... - * - * maybe find not only by external id but by internal too ? but then what if we will try - * */ @Override - public final EntityImportResult importEntity(TenantId tenantId, D exportData) { + public final EntityImportResult importEntity(TenantId tenantId, D exportData, EntityImportSettings importSettings) { E entity = exportData.getMainEntity(); E existingEntity = findByExternalId(tenantId, entity.getId()); @@ -51,7 +51,18 @@ public abstract class AbstractEntityImportService { + relation.setTo(savedEntity.getId()); + relation.setFrom(getInternalId(tenantId, relation.getFrom())); + relationService.saveRelation(tenantId, relation); + }); + } EntityImportResult importResult = new EntityImportResult<>(); importResult.setSavedEntity(savedEntity); @@ -59,17 +70,14 @@ public abstract class AbstractEntityImportService ID getInternalId(TenantId tenantId, ID externalId) { if (externalId == null) { return null; diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java index 4e6fa2b33e..f228b9be5e 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.expimp.imp.EntityImportSettings; @Service @TbCoreComponent @@ -34,7 +35,7 @@ public class AssetImportService extends AbstractEntityImportService { // ruleChainConnectionInfo.setTargetRuleChainId(); - // TODO: check if this thing is needed for "Other Rule Chain Node" - // TODO: and check import of tenant rule chains + // TODO [viacheslav]: check if this thing is needed for "Other Rule Chain Node" + // TODO [viacheslav]: and check import of tenant rule chains }); ruleChainService.saveRuleChainMetaData(tenantId, metaData); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java index f87b44b0eb..c2f23a030f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java @@ -40,7 +40,7 @@ public class Customer extends ContactBased implements HasTenantId, E private TenantId tenantId; @Getter @Setter - private CustomerId externalId; // FIXME: add to hashcode, equals, etc + private CustomerId externalId; // FIXME [viacheslav]: add to hashcode, equals, etc public Customer() { super(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java index b428c3e6b5..261178d913 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Data; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.export.impl.AssetExportData; import org.thingsboard.server.common.data.export.impl.CustomerExportData; @@ -28,6 +29,9 @@ import org.thingsboard.server.common.data.export.impl.DeviceExportData; import org.thingsboard.server.common.data.export.impl.DeviceProfileExportData; import org.thingsboard.server.common.data.export.impl.RuleChainExportData; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.relation.EntityRelation; + +import java.util.List; @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(property = "entityType", use = JsonTypeInfo.Id.NAME) @@ -39,12 +43,13 @@ import org.thingsboard.server.common.data.id.EntityId; @Type(name = "DASHBOARD", value = DashboardExportData.class), @Type(name = "CUSTOMER", value = CustomerExportData.class) }) -public interface EntityExportData> { +@Data +public abstract class EntityExportData> { - @JsonIgnore - E getMainEntity(); + private E mainEntity; + private List inboundRelations; @JsonIgnore - EntityType getEntityType(); // fixme: maybe remove if not needed + public abstract EntityType getEntityType(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/ExportableEntity.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/ExportableEntity.java index 89b0cc3de7..d990652298 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/ExportableEntity.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/ExportableEntity.java @@ -15,8 +15,6 @@ */ package org.thingsboard.server.common.data.export; -import com.fasterxml.jackson.annotation.JsonIgnore; -import org.springframework.data.annotation.Transient; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.EntityId; @@ -25,9 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId; public interface ExportableEntity extends HasId, HasTenantId, HasName { - @Transient @JsonIgnore I getId(); - @Transient @JsonIgnore void setId(I id); I getExternalId(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/AssetExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/AssetExportData.java index abb268eeb6..6277a56afe 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/AssetExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/AssetExportData.java @@ -16,19 +16,14 @@ package org.thingsboard.server.common.data.export.impl; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.export.EntityExportData; +@EqualsAndHashCode(callSuper = true) @Data -public class AssetExportData implements EntityExportData { - - private Asset asset; - - @Override - public Asset getMainEntity() { - return asset; - } +public class AssetExportData extends EntityExportData { @Override public EntityType getEntityType() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/CustomerExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/CustomerExportData.java index 02ec9e7efa..3de1dbc1b9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/CustomerExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/CustomerExportData.java @@ -16,19 +16,14 @@ package org.thingsboard.server.common.data.export.impl; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.export.EntityExportData; +@EqualsAndHashCode(callSuper = true) @Data -public class CustomerExportData implements EntityExportData { - - private Customer customer; - - @Override - public Customer getMainEntity() { - return customer; - } +public class CustomerExportData extends EntityExportData { @Override public EntityType getEntityType() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DashboardExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DashboardExportData.java index f75f2479f6..7310ad1f92 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DashboardExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DashboardExportData.java @@ -16,19 +16,14 @@ package org.thingsboard.server.common.data.export.impl; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.export.EntityExportData; +@EqualsAndHashCode(callSuper = true) @Data -public class DashboardExportData implements EntityExportData { - - private Dashboard dashboard; - - @Override - public Dashboard getMainEntity() { - return dashboard; - } +public class DashboardExportData extends EntityExportData { @Override public EntityType getEntityType() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceExportData.java index ff3741dabc..7d4d404ebe 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceExportData.java @@ -16,22 +16,18 @@ package org.thingsboard.server.common.data.export.impl; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.export.EntityExportData; import org.thingsboard.server.common.data.security.DeviceCredentials; +@EqualsAndHashCode(callSuper = true) @Data -public class DeviceExportData implements EntityExportData { +public class DeviceExportData extends EntityExportData { - private Device device; private DeviceCredentials credentials; - @Override - public Device getMainEntity() { - return device; - } - @Override public EntityType getEntityType() { return EntityType.DEVICE; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceProfileExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceProfileExportData.java index a5d7872cc6..4ebae37b8e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceProfileExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceProfileExportData.java @@ -16,19 +16,14 @@ package org.thingsboard.server.common.data.export.impl; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.export.EntityExportData; +@EqualsAndHashCode(callSuper = true) @Data -public class DeviceProfileExportData implements EntityExportData { - - private DeviceProfile deviceProfile; - - @Override - public DeviceProfile getMainEntity() { - return deviceProfile; - } +public class DeviceProfileExportData extends EntityExportData { @Override public EntityType getEntityType() { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/RuleChainExportData.java b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/RuleChainExportData.java index 101f516881..d52c68b99e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/RuleChainExportData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/RuleChainExportData.java @@ -16,22 +16,18 @@ package org.thingsboard.server.common.data.export.impl; import lombok.Data; +import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.export.EntityExportData; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; +@EqualsAndHashCode(callSuper = true) @Data -public class RuleChainExportData implements EntityExportData { +public class RuleChainExportData extends EntityExportData { - private RuleChain ruleChain; private RuleChainMetaData metaData; - @Override - public RuleChain getMainEntity() { - return ruleChain; - } - @Override public EntityType getEntityType() { return EntityType.RULE_CHAIN; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/IdBased.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/IdBased.java index cfe6918a7d..5c99ad8ddb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/IdBased.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/IdBased.java @@ -16,33 +16,34 @@ package org.thingsboard.server.common.data.id; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonSetter; -import java.io.Serializable; import java.util.UUID; public abstract class IdBased implements HasId { protected I id; - - public IdBased() { - super(); - } - - public IdBased(I id) { - super(); - this.id = id; - } - public void setId(I id) { - this.id = id; - } + public IdBased() { + super(); + } - public I getId() { - return id; - } + public IdBased(I id) { + super(); + this.id = id; + } + + @JsonSetter + public void setId(I id) { + this.id = id; + } + + public I getId() { + return id; + } - @JsonIgnore - public UUID getUuidId() { + @JsonIgnore + public UUID getUuidId() { if (id != null) { return id.getId(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java index c7741ec013..4e0822936b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java @@ -24,7 +24,7 @@ public interface ExportableEntityDao { T findByTenantIdAndExternalId(UUID tenantId, UUID externalId); T findByTenantIdAndId(UUID tenantId, UUID id); - // fixme: get rid of boilerplate ? + // fixme [viacheslav]: get rid of boilerplate ? EntityType getEntityType(); From 040b6a31adb7bae03139b843697b2ae4e5cb0849 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Fri, 25 Mar 2022 15:54:07 +0200 Subject: [PATCH 06/39] Rename packages for export/import --- .../EntitiesExportImportController.java | 12 ++++++------ .../DefaultEntitiesExportImportService.java | 16 ++++++++-------- .../EntitiesExportImportService.java | 12 ++++++------ .../ExportableEntitiesService.java | 4 ++-- .../exporting}/EntityExportService.java | 6 +++--- .../exporting}/EntityExportSettings.java | 2 +- .../exporting/data}/AssetExportData.java | 3 +-- .../exporting/data}/CustomerExportData.java | 3 +-- .../exporting/data}/DashboardExportData.java | 3 +-- .../exporting/data}/DeviceExportData.java | 3 +-- .../exporting/data}/DeviceProfileExportData.java | 3 +-- .../exporting/data}/EntityExportData.java | 9 ++------- .../exporting/data}/RuleChainExportData.java | 3 +-- .../impl/AbstractEntityExportService.java | 12 ++++++------ .../exporting}/impl/AssetExportService.java | 4 ++-- .../exporting}/impl/CustomerExportService.java | 4 ++-- .../exporting}/impl/DashboardExportService.java | 4 ++-- .../exporting}/impl/DeviceExportService.java | 4 ++-- .../impl/DeviceProfileExportService.java | 4 ++-- .../exporting}/impl/RuleChainExportService.java | 4 ++-- .../importing}/EntityImportResult.java | 4 ++-- .../importing}/EntityImportService.java | 6 +++--- .../importing}/EntityImportSettings.java | 2 +- .../impl/AbstractEntityImportService.java | 14 +++++++------- .../importing}/impl/AssetImportService.java | 6 +++--- .../importing}/impl/CustomerImportService.java | 6 +++--- .../importing}/impl/DashboardImportService.java | 6 +++--- .../importing}/impl/DeviceImportService.java | 6 +++--- .../impl/DeviceProfileImportService.java | 6 +++--- .../importing}/impl/RuleChainImportService.java | 6 +++--- .../thingsboard/server/common/data/Customer.java | 1 - .../server/common/data/Dashboard.java | 1 - .../thingsboard/server/common/data/Device.java | 1 - .../server/common/data/DeviceProfile.java | 1 - .../data/{export => }/ExportableEntity.java | 4 +--- .../server/common/data/asset/Asset.java | 2 +- .../server/common/data/rule/RuleChain.java | 2 +- 37 files changed, 86 insertions(+), 103 deletions(-) rename application/src/main/java/org/thingsboard/server/service/{expimp => exportimport}/DefaultEntitiesExportImportService.java (88%) rename application/src/main/java/org/thingsboard/server/service/{expimp => exportimport}/EntitiesExportImportService.java (72%) rename application/src/main/java/org/thingsboard/server/service/{expimp => exportimport}/ExportableEntitiesService.java (89%) rename application/src/main/java/org/thingsboard/server/service/{expimp/exp => exportimport/exporting}/EntityExportService.java (83%) rename application/src/main/java/org/thingsboard/server/service/{expimp/exp => exportimport/exporting}/EntityExportSettings.java (93%) rename {common/data/src/main/java/org/thingsboard/server/common/data/export/impl => application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data}/AssetExportData.java (88%) rename {common/data/src/main/java/org/thingsboard/server/common/data/export/impl => application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data}/CustomerExportData.java (88%) rename {common/data/src/main/java/org/thingsboard/server/common/data/export/impl => application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data}/DashboardExportData.java (88%) rename {common/data/src/main/java/org/thingsboard/server/common/data/export/impl => application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data}/DeviceExportData.java (89%) rename {common/data/src/main/java/org/thingsboard/server/common/data/export/impl => application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data}/DeviceProfileExportData.java (89%) rename {common/data/src/main/java/org/thingsboard/server/common/data/export => application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data}/EntityExportData.java (79%) rename {common/data/src/main/java/org/thingsboard/server/common/data/export/impl => application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data}/RuleChainExportData.java (89%) rename application/src/main/java/org/thingsboard/server/service/{expimp/exp => exportimport/exporting}/impl/AbstractEntityExportService.java (83%) rename application/src/main/java/org/thingsboard/server/service/{expimp/exp => exportimport/exporting}/impl/AssetExportService.java (88%) rename application/src/main/java/org/thingsboard/server/service/{expimp/exp => exportimport/exporting}/impl/CustomerExportService.java (88%) rename application/src/main/java/org/thingsboard/server/service/{expimp/exp => exportimport/exporting}/impl/DashboardExportService.java (88%) rename application/src/main/java/org/thingsboard/server/service/{expimp/exp => exportimport/exporting}/impl/DeviceExportService.java (91%) rename application/src/main/java/org/thingsboard/server/service/{expimp/exp => exportimport/exporting}/impl/DeviceProfileExportService.java (88%) rename application/src/main/java/org/thingsboard/server/service/{expimp/exp => exportimport/exporting}/impl/RuleChainExportService.java (91%) rename application/src/main/java/org/thingsboard/server/service/{expimp/imp => exportimport/importing}/EntityImportResult.java (87%) rename application/src/main/java/org/thingsboard/server/service/{expimp/imp => exportimport/importing}/EntityImportService.java (86%) rename application/src/main/java/org/thingsboard/server/service/{expimp/imp => exportimport/importing}/EntityImportSettings.java (93%) rename application/src/main/java/org/thingsboard/server/service/{expimp/imp => exportimport/importing}/impl/AbstractEntityImportService.java (87%) rename application/src/main/java/org/thingsboard/server/service/{expimp/imp => exportimport/importing}/impl/AssetImportService.java (87%) rename application/src/main/java/org/thingsboard/server/service/{expimp/imp => exportimport/importing}/impl/CustomerImportService.java (87%) rename application/src/main/java/org/thingsboard/server/service/{expimp/imp => exportimport/importing}/impl/DashboardImportService.java (88%) rename application/src/main/java/org/thingsboard/server/service/{expimp/imp => exportimport/importing}/impl/DeviceImportService.java (89%) rename application/src/main/java/org/thingsboard/server/service/{expimp/imp => exportimport/importing}/impl/DeviceProfileImportService.java (89%) rename application/src/main/java/org/thingsboard/server/service/{expimp/imp => exportimport/importing}/impl/RuleChainImportService.java (91%) rename common/data/src/main/java/org/thingsboard/server/common/data/{export => }/ExportableEntity.java (86%) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index 96bc70656f..fc3b0b712b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -26,17 +26,17 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.export.EntityExportData; -import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.EntitiesExportImportService; -import org.thingsboard.server.service.expimp.exp.EntityExportSettings; -import org.thingsboard.server.service.expimp.imp.EntityImportResult; -import org.thingsboard.server.service.expimp.imp.EntityImportSettings; +import org.thingsboard.server.service.exportimport.EntitiesExportImportService; +import org.thingsboard.server.service.exportimport.exporting.EntityExportSettings; +import org.thingsboard.server.service.exportimport.exporting.data.EntityExportData; +import org.thingsboard.server.service.exportimport.importing.EntityImportResult; +import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/DefaultEntitiesExportImportService.java similarity index 88% rename from application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/DefaultEntitiesExportImportService.java index 0136bd1d52..4e9c42cffd 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/DefaultEntitiesExportImportService.java @@ -13,22 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp; +package org.thingsboard.server.service.exportimport; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.EntityExportData; -import org.thingsboard.server.common.data.export.ExportableEntity; +import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.exp.EntityExportService; -import org.thingsboard.server.service.expimp.exp.EntityExportSettings; -import org.thingsboard.server.service.expimp.imp.EntityImportResult; -import org.thingsboard.server.service.expimp.imp.EntityImportService; -import org.thingsboard.server.service.expimp.imp.EntityImportSettings; +import org.thingsboard.server.service.exportimport.exporting.EntityExportService; +import org.thingsboard.server.service.exportimport.exporting.EntityExportSettings; +import org.thingsboard.server.service.exportimport.exporting.data.EntityExportData; +import org.thingsboard.server.service.exportimport.importing.EntityImportResult; +import org.thingsboard.server.service.exportimport.importing.EntityImportService; +import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; import java.util.Collection; import java.util.HashMap; diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/EntitiesExportImportService.java similarity index 72% rename from application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/EntitiesExportImportService.java index 752d4b6315..1eb085d219 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/EntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/EntitiesExportImportService.java @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp; +package org.thingsboard.server.service.exportimport; -import org.thingsboard.server.common.data.export.EntityExportData; -import org.thingsboard.server.common.data.export.ExportableEntity; +import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.service.expimp.exp.EntityExportSettings; -import org.thingsboard.server.service.expimp.imp.EntityImportResult; -import org.thingsboard.server.service.expimp.imp.EntityImportSettings; +import org.thingsboard.server.service.exportimport.exporting.EntityExportSettings; +import org.thingsboard.server.service.exportimport.exporting.data.EntityExportData; +import org.thingsboard.server.service.exportimport.importing.EntityImportResult; +import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; public interface EntitiesExportImportService extends ExportableEntitiesService { diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/ExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/ExportableEntitiesService.java similarity index 89% rename from application/src/main/java/org/thingsboard/server/service/expimp/ExportableEntitiesService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/ExportableEntitiesService.java index 17151e3889..bb8f30c4e1 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/ExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/ExportableEntitiesService.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp; +package org.thingsboard.server.service.exportimport; -import org.thingsboard.server.common.data.export.ExportableEntity; +import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/EntityExportService.java similarity index 83% rename from application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/exporting/EntityExportService.java index 7c7fc49240..929092e90e 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/EntityExportService.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.exp; +package org.thingsboard.server.service.exportimport.exporting; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.EntityExportData; -import org.thingsboard.server.common.data.export.ExportableEntity; +import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.service.exportimport.exporting.data.EntityExportData; public interface EntityExportService, D extends EntityExportData> { diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportSettings.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/EntityExportSettings.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportSettings.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/exporting/EntityExportSettings.java index c70b0b2216..3755ce801d 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/exp/EntityExportSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/EntityExportSettings.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.exp; +package org.thingsboard.server.service.exportimport.exporting; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/AssetExportData.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/AssetExportData.java similarity index 88% rename from common/data/src/main/java/org/thingsboard/server/common/data/export/impl/AssetExportData.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/AssetExportData.java index 6277a56afe..4c4872760b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/AssetExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/AssetExportData.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.export.impl; +package org.thingsboard.server.service.exportimport.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.export.EntityExportData; @EqualsAndHashCode(callSuper = true) @Data diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/CustomerExportData.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/CustomerExportData.java similarity index 88% rename from common/data/src/main/java/org/thingsboard/server/common/data/export/impl/CustomerExportData.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/CustomerExportData.java index 3de1dbc1b9..1fc820814e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/CustomerExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/CustomerExportData.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.export.impl; +package org.thingsboard.server.service.exportimport.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.EntityExportData; @EqualsAndHashCode(callSuper = true) @Data diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DashboardExportData.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DashboardExportData.java similarity index 88% rename from common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DashboardExportData.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DashboardExportData.java index 7310ad1f92..6ef45fc531 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DashboardExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DashboardExportData.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.export.impl; +package org.thingsboard.server.service.exportimport.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.EntityExportData; @EqualsAndHashCode(callSuper = true) @Data diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceExportData.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DeviceExportData.java similarity index 89% rename from common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceExportData.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DeviceExportData.java index 7d4d404ebe..5f5d8e8f72 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DeviceExportData.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.export.impl; +package org.thingsboard.server.service.exportimport.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.EntityExportData; import org.thingsboard.server.common.data.security.DeviceCredentials; @EqualsAndHashCode(callSuper = true) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceProfileExportData.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DeviceProfileExportData.java similarity index 89% rename from common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceProfileExportData.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DeviceProfileExportData.java index 4ebae37b8e..def6b7edbe 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/DeviceProfileExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DeviceProfileExportData.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.export.impl; +package org.thingsboard.server.service.exportimport.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.EntityExportData; @EqualsAndHashCode(callSuper = true) @Data diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/EntityExportData.java similarity index 79% rename from common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/EntityExportData.java index 261178d913..74116276cb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/EntityExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/EntityExportData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.export; +package org.thingsboard.server.service.exportimport.exporting.data; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -22,12 +22,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.impl.AssetExportData; -import org.thingsboard.server.common.data.export.impl.CustomerExportData; -import org.thingsboard.server.common.data.export.impl.DashboardExportData; -import org.thingsboard.server.common.data.export.impl.DeviceExportData; -import org.thingsboard.server.common.data.export.impl.DeviceProfileExportData; -import org.thingsboard.server.common.data.export.impl.RuleChainExportData; +import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.relation.EntityRelation; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/RuleChainExportData.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/RuleChainExportData.java similarity index 89% rename from common/data/src/main/java/org/thingsboard/server/common/data/export/impl/RuleChainExportData.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/RuleChainExportData.java index d52c68b99e..f92616fead 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/impl/RuleChainExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/RuleChainExportData.java @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.export.impl; +package org.thingsboard.server.service.exportimport.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.EntityExportData; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AbstractEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AbstractEntityExportService.java similarity index 83% rename from application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AbstractEntityExportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AbstractEntityExportService.java index f6c2f5d5f7..7b298a7d3c 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AbstractEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AbstractEntityExportService.java @@ -13,20 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.exp.impl; +package org.thingsboard.server.service.exportimport.exporting.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; -import org.thingsboard.server.common.data.export.EntityExportData; -import org.thingsboard.server.common.data.export.ExportableEntity; +import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.service.expimp.ExportableEntitiesService; -import org.thingsboard.server.service.expimp.exp.EntityExportService; -import org.thingsboard.server.service.expimp.exp.EntityExportSettings; +import org.thingsboard.server.service.exportimport.ExportableEntitiesService; +import org.thingsboard.server.service.exportimport.exporting.EntityExportService; +import org.thingsboard.server.service.exportimport.exporting.EntityExportSettings; +import org.thingsboard.server.service.exportimport.exporting.data.EntityExportData; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AssetExportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AssetExportService.java similarity index 88% rename from application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AssetExportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AssetExportService.java index ec1b2dddc3..a48167053b 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/AssetExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AssetExportService.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.exp.impl; +package org.thingsboard.server.service.exportimport.exporting.impl; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.export.impl.AssetExportData; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.exportimport.exporting.data.AssetExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/CustomerExportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/CustomerExportService.java similarity index 88% rename from application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/CustomerExportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/CustomerExportService.java index 32e33f757c..518a830a21 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/CustomerExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/CustomerExportService.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.exp.impl; +package org.thingsboard.server.service.exportimport.exporting.impl; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.impl.CustomerExportData; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.exportimport.exporting.data.CustomerExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DashboardExportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DashboardExportService.java similarity index 88% rename from application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DashboardExportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DashboardExportService.java index be86c41c9a..167b57be75 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DashboardExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DashboardExportService.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.exp.impl; +package org.thingsboard.server.service.exportimport.exporting.impl; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.impl.DashboardExportData; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.exportimport.exporting.data.DashboardExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceExportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceExportService.java similarity index 91% rename from application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceExportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceExportService.java index c3542dc95b..8a8bc26820 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceExportService.java @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.exp.impl; +package org.thingsboard.server.service.exportimport.exporting.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.impl.DeviceExportData; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.exportimport.exporting.data.DeviceExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceProfileExportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceProfileExportService.java similarity index 88% rename from application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceProfileExportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceProfileExportService.java index 03787ab30e..ce96e3b1fe 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/DeviceProfileExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceProfileExportService.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.exp.impl; +package org.thingsboard.server.service.exportimport.exporting.impl; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.impl.DeviceProfileExportData; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.exportimport.exporting.data.DeviceProfileExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/RuleChainExportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/RuleChainExportService.java similarity index 91% rename from application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/RuleChainExportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/RuleChainExportService.java index 0e2d24e990..8c173fe2ce 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/exp/impl/RuleChainExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/RuleChainExportService.java @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.exp.impl; +package org.thingsboard.server.service.exportimport.exporting.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.impl.RuleChainExportData; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.exportimport.exporting.data.RuleChainExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportResult.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportResult.java similarity index 87% rename from application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportResult.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportResult.java index 90f5d42bc9..cc44670757 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportResult.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportResult.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.imp; +package org.thingsboard.server.service.exportimport.importing; import lombok.Data; -import org.thingsboard.server.common.data.export.ExportableEntity; +import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; @Data diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportService.java similarity index 86% rename from application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportService.java index 693ff310bf..b001290cef 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportService.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.imp; +package org.thingsboard.server.service.exportimport.importing; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.EntityExportData; -import org.thingsboard.server.common.data.export.ExportableEntity; +import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.service.exportimport.exporting.data.EntityExportData; public interface EntityImportService, D extends EntityExportData> { diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportSettings.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportSettings.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportSettings.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportSettings.java index 1b29139e6b..d153d1378a 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/EntityImportSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportSettings.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.imp; +package org.thingsboard.server.service.exportimport.importing; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AbstractEntityImportService.java similarity index 87% rename from application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AbstractEntityImportService.java index fd27feeea9..075991104c 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AbstractEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AbstractEntityImportService.java @@ -13,21 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.imp.impl; +package org.thingsboard.server.service.exportimport.importing.impl; import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; -import org.thingsboard.server.common.data.export.EntityExportData; -import org.thingsboard.server.common.data.export.ExportableEntity; +import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.service.expimp.ExportableEntitiesService; -import org.thingsboard.server.service.expimp.imp.EntityImportResult; -import org.thingsboard.server.service.expimp.imp.EntityImportService; -import org.thingsboard.server.service.expimp.imp.EntityImportSettings; +import org.thingsboard.server.service.exportimport.ExportableEntitiesService; +import org.thingsboard.server.service.exportimport.exporting.data.EntityExportData; +import org.thingsboard.server.service.exportimport.importing.EntityImportResult; +import org.thingsboard.server.service.exportimport.importing.EntityImportService; +import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; public abstract class AbstractEntityImportService, D extends EntityExportData> implements EntityImportService { diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AssetImportService.java similarity index 87% rename from application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AssetImportService.java index f228b9be5e..6699219388 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/AssetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AssetImportService.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.imp.impl; +package org.thingsboard.server.service.exportimport.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.export.impl.AssetExportData; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.imp.EntityImportSettings; +import org.thingsboard.server.service.exportimport.exporting.data.AssetExportData; +import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/CustomerImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/CustomerImportService.java similarity index 87% rename from application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/CustomerImportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/CustomerImportService.java index 7a8bfa7c9d..251dd4acbf 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/CustomerImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/CustomerImportService.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.imp.impl; +package org.thingsboard.server.service.exportimport.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.impl.CustomerExportData; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.imp.EntityImportSettings; +import org.thingsboard.server.service.exportimport.exporting.data.CustomerExportData; +import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DashboardImportService.java similarity index 88% rename from application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DashboardImportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DashboardImportService.java index dcfba5f17b..b429ba7111 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DashboardImportService.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.imp.impl; +package org.thingsboard.server.service.exportimport.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.impl.DashboardExportData; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.imp.EntityImportSettings; +import org.thingsboard.server.service.exportimport.exporting.data.DashboardExportData; +import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceImportService.java similarity index 89% rename from application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceImportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceImportService.java index 50db77fb2e..01312dfa44 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceImportService.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.imp.impl; +package org.thingsboard.server.service.exportimport.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.impl.DeviceExportData; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.imp.EntityImportSettings; +import org.thingsboard.server.service.exportimport.exporting.data.DeviceExportData; +import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceProfileImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceProfileImportService.java similarity index 89% rename from application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceProfileImportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceProfileImportService.java index 8fe7586a25..ef72d982ee 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/DeviceProfileImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceProfileImportService.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.imp.impl; +package org.thingsboard.server.service.exportimport.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.impl.DeviceProfileExportData; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.imp.EntityImportSettings; +import org.thingsboard.server.service.exportimport.exporting.data.DeviceProfileExportData; +import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/RuleChainImportService.java similarity index 91% rename from application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/RuleChainImportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/RuleChainImportService.java index 5dfe6a8692..5fc1c8fb8c 100644 --- a/application/src/main/java/org/thingsboard/server/service/expimp/imp/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/RuleChainImportService.java @@ -13,20 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.expimp.imp.impl; +package org.thingsboard.server.service.exportimport.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.export.impl.RuleChainExportData; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.expimp.imp.EntityImportSettings; +import org.thingsboard.server.service.exportimport.exporting.data.RuleChainExportData; +import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; @Service @TbCoreComponent diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java index c2f23a030f..b7287c55af 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java @@ -22,7 +22,6 @@ import com.fasterxml.jackson.databind.JsonNode; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; -import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.Length; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java b/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java index c8b0df98b4..dce910c4ff 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java @@ -19,7 +19,6 @@ import com.fasterxml.jackson.databind.JsonNode; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; -import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.DashboardId; public class Dashboard extends DashboardInfo implements ExportableEntity { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java index 3bd369591f..678c68c115 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java @@ -25,7 +25,6 @@ import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.device.data.DeviceData; -import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java index 0bd770d02d..891367fac4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java @@ -24,7 +24,6 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; -import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.OtaPackageId; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/export/ExportableEntity.java b/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java similarity index 86% rename from common/data/src/main/java/org/thingsboard/server/common/data/export/ExportableEntity.java rename to common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java index d990652298..f1e8c3754e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/export/ExportableEntity.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java @@ -13,10 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.export; +package org.thingsboard.server.common.data; -import org.thingsboard.server.common.data.HasName; -import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java index 694a0162bc..da6d9615a1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/asset/Asset.java @@ -21,11 +21,11 @@ import io.swagger.annotations.ApiModelProperty; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; +import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.HasCustomerId; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; -import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java index 370087deb3..74186b49bf 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java @@ -22,10 +22,10 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; -import org.thingsboard.server.common.data.export.ExportableEntity; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TenantId; From ab5d6f3c0dcb6ad301fbd26005854632c104bdc0 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Wed, 30 Mar 2022 13:31:43 +0300 Subject: [PATCH 07/39] Entities export/import API refactoring --- .../EntitiesExportImportController.java | 144 ++++++++++++++---- .../DefaultEntitiesExportImportService.java | 26 ++++ .../EntitiesExportImportService.java | 4 + .../exporting/data/EntityExportData.java | 2 + .../server/common/data/Customer.java | 35 +---- .../server/common/data/Dashboard.java | 27 +--- .../server/dao/ExportableEntityDao.java | 7 +- 7 files changed, 153 insertions(+), 92 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index fc3b0b712b..ec2d4273cc 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -27,23 +27,37 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.query.EntityData; +import org.thingsboard.server.common.data.query.EntityDataPageLink; +import org.thingsboard.server.common.data.query.EntityDataQuery; +import org.thingsboard.server.common.data.query.EntityDataSortOrder; +import org.thingsboard.server.common.data.query.EntityFilter; +import org.thingsboard.server.common.data.query.EntityKey; +import org.thingsboard.server.common.data.query.EntityKeyType; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.exportimport.EntitiesExportImportService; import org.thingsboard.server.service.exportimport.exporting.EntityExportSettings; import org.thingsboard.server.service.exportimport.exporting.data.EntityExportData; import org.thingsboard.server.service.exportimport.importing.EntityImportResult; import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; +import org.thingsboard.server.service.query.EntityQueryService; 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.Collections; +import java.util.List; import java.util.UUID; import java.util.stream.Collectors; +import static org.thingsboard.server.dao.sql.query.EntityKeyMapping.CREATED_TIME; + @RestController @RequestMapping("/api/entities") @TbCoreComponent @@ -51,7 +65,58 @@ import java.util.stream.Collectors; public class EntitiesExportImportController extends BaseController { private final EntitiesExportImportService exportImportService; + private final EntityQueryService entityQueryService; + + + // TODO [viacheslav]: export and import of batches + // TODO [viacheslav]: api to export and import whole customer, whole tenant + + + @PostMapping("/exportByFilter") + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + public List> exportEntitiesByFilter(@RequestBody EntityFilter filter, + @RequestParam(defaultValue = "false") boolean exportInboundRelations, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "100") int pageSize) throws ThingsboardException { + EntityDataPageLink pageLink = new EntityDataPageLink(); + pageLink.setPage(page); + pageLink.setPageSize(pageSize); + pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ENTITY_FIELD, CREATED_TIME), EntityDataSortOrder.Direction.DESC)); + EntityDataQuery entityDataQuery = new EntityDataQuery(filter, pageLink, List.of(new EntityKey(EntityKeyType.ENTITY_FIELD, CREATED_TIME)), + Collections.emptyList(), Collections.emptyList()); + + SecurityUser user = getCurrentUser(); + EntityExportSettings exportSettings = toExportSettings(exportInboundRelations); + + try { + // FIXME [viacheslav]: check read permission for relation fromId + return entityQueryService.findEntityDataByQuery(user, entityDataQuery).getData().stream() + .map(EntityData::getEntityId) + .map(entityId -> { + return exportImportService.exportEntity(user.getTenantId(), entityId, exportSettings); + }) + .collect(Collectors.toList()); + } catch (Exception e) { + throw handleException(e); + } + } + @PostMapping("/exportByQuery") + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + public List> exportEntitiesByQuery(@RequestBody EntityDataQuery query, + @RequestParam(defaultValue = "false") boolean exportInboundRelations) throws ThingsboardException { + SecurityUser user = getCurrentUser(); + EntityExportSettings exportSettings = toExportSettings(exportInboundRelations); +// FIXME [viacheslav]: check read permission for relation fromId + try { + return entityQueryService.findEntityDataByQuery(user, query).getData().stream() + .map(EntityData::getEntityId) + .map(entityId -> exportImportService.exportEntity(user.getTenantId(), entityId, exportSettings)) + .collect(Collectors.toList()); + } catch (Exception e) { + throw handleException(e); + } + } @PostMapping("/export/{entityType}/{entityId}") @PreAuthorize("hasAuthority('TENANT_ADMIN')") @@ -60,11 +125,13 @@ public class EntitiesExportImportController extends BaseController { @PathVariable("entityId") UUID entityUuid, @RequestParam(defaultValue = "false") boolean exportInboundRelations) throws ThingsboardException { EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); - EntityExportSettings exportSettings = EntityExportSettings.builder() - .exportInboundRelations(exportInboundRelations) - .build(); - try { - return exportEntity(getCurrentUser(), entityId, exportSettings); + checkEntityId(entityId, Operation.READ); + + SecurityUser user = getCurrentUser(); + EntityExportSettings exportSettings = toExportSettings(exportInboundRelations); + + try { // FIXME [viacheslav]: check read permission for relation fromId + return exportImportService.exportEntity(user.getTenantId(), entityId, exportSettings); } catch (Exception e) { throw handleException(e); } @@ -73,45 +140,64 @@ public class EntitiesExportImportController extends BaseController { @PostMapping("/import") @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public EntityImportResult> importEntity(@RequestBody EntityExportData> exportData, - @RequestParam(defaultValue = "false") boolean importInboundRelations) throws ThingsboardException { - EntityImportSettings importSettings = EntityImportSettings.builder() - .importInboundRelations(importInboundRelations) - .build(); + public List>> importEntity(@RequestBody List>> exportDataList, + @RequestParam(defaultValue = "false") boolean importInboundRelations) throws ThingsboardException { + SecurityUser user = getCurrentUser(); + EntityImportSettings importSettings = toImportSettings(importInboundRelations); + + for (EntityExportData> exportData : exportDataList) { + checkPermissionsForImport(user, exportData, importSettings); + } + try { - return importEntity(getCurrentUser(), exportData, importSettings); + return exportImportService.importEntities(user.getTenantId(), exportDataList, importSettings); } catch (Exception e) { throw handleException(e); } } - - // TODO [viacheslav]: export and import of batches - // TODO [viacheslav]: api to export and import whole customer, whole tenant - - private EntityExportData> exportEntity(SecurityUser user, EntityId entityId, EntityExportSettings exportSettings) throws ThingsboardException { + public void checkPermissionsForExport(SecurityUser user, EntityId entityId, EntityExportSettings exportSettings) throws ThingsboardException { checkEntityId(entityId, Operation.READ); - return exportImportService.exportEntity(user.getTenantId(), entityId, exportSettings); + if (exportSettings.isExportInboundRelations()) { + for (EntityRelation entityRelation : relationService.findByTo(user.getTenantId(), entityId, RelationTypeGroup.COMMON)) { + EntityId fromId = entityRelation.getFrom(); + checkEntityId(fromId, Operation.READ); + } + } } - private EntityImportResult> importEntity(SecurityUser user, EntityExportData> exportData, EntityImportSettings importSettings) throws ThingsboardException { - ExportableEntity existingEntity = exportImportService.findEntityByExternalId(user.getTenantId(), exportData.getMainEntity().getId()); - if (existingEntity != null) {// todo [viacheslav] maybe need to extract permission check to BaseController and put there permission checks from other controllers + public void checkPermissionsForImport(SecurityUser user, EntityExportData> exportData, EntityImportSettings importSettings) throws ThingsboardException { + ExportableEntity existingEntity = exportImportService.findEntityByExternalId(user.getTenantId(), exportData.getMainEntity().getId()); + if (existingEntity != null) { checkEntityId(existingEntity.getId(), Operation.WRITE); - if (importSettings.isImportInboundRelations() && CollectionUtils.isNotEmpty(exportData.getInboundRelations())) { - for (EntityId fromId : exportData.getInboundRelations().stream().map(EntityRelation::getFrom).collect(Collectors.toSet())) { - // FIXME [viacheslav]: fromId is external -// checkEntityId(fromId, Operation.WRITE); + } else { + accessControlService.checkPermission(user, Resource.of(exportData.getEntityType()), Operation.CREATE); + } + + if (importSettings.isImportInboundRelations() && CollectionUtils.isNotEmpty(exportData.getInboundRelations())) { + for (EntityRelation entityRelation : exportData.getInboundRelations()) { + ExportableEntity entityFrom = exportImportService.findEntityByExternalId(user.getTenantId(), entityRelation.getFrom()); + if (entityFrom != null) { + accessControlService.checkPermission(user, Resource.of(entityFrom.getId().getEntityType()), Operation.WRITE, entityFrom.getId(), entityFrom); + } else { + throw new ThingsboardException("Relation with non-existing entity", ThingsboardErrorCode.BAD_REQUEST_PARAMS); } } - } else { - checkEntity(null, exportData.getMainEntity(), Resource.of(exportData.getEntityType())); } + } + - EntityImportResult> importResult = exportImportService.importEntity(getTenantId(), exportData, importSettings); - onEntityUpdatedOrCreated(user, importResult.getSavedEntity(), importResult.getOldEntity(), importResult.getOldEntity() == null); + private EntityImportSettings toImportSettings(boolean importInboundRelations) { + EntityImportSettings importSettings = EntityImportSettings.builder() + .importInboundRelations(importInboundRelations) + .build(); + return importSettings; + } - return importResult; + private EntityExportSettings toExportSettings(boolean exportInboundRelations) { + return EntityExportSettings.builder() + .exportInboundRelations(exportInboundRelations) + .build(); } } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/DefaultEntitiesExportImportService.java index 4e9c42cffd..93fa370c94 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/DefaultEntitiesExportImportService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.exportimport; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; @@ -31,9 +32,12 @@ import org.thingsboard.server.service.exportimport.importing.EntityImportService import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; import java.util.Collection; +import java.util.Comparator; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; // FIXME [viacheslav]: review packages and classes naming @Service @@ -44,6 +48,11 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS private final Map> importServices = new HashMap<>(); private final Map> daos = new HashMap<>(); + protected static final List SUPPORTED_ENTITY_TYPES = List.of( + EntityType.CUSTOMER, EntityType.ASSET, EntityType.RULE_CHAIN, + EntityType.DEVICE_PROFILE, EntityType.DEVICE, EntityType.DASHBOARD + ); + // TODO [viacheslav]: export and import of the whole tenant // TODO [viacheslav]: export and import of the whole customer ? @@ -55,7 +64,9 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS return exportService.getExportData(tenantId, entityId, exportSettings); } + // FIXME [viacheslav]: somehow validate export data + @Transactional @Override public , I extends EntityId> EntityImportResult importEntity(TenantId tenantId, EntityExportData exportData, EntityImportSettings importSettings) { EntityType entityType = exportData.getEntityType(); @@ -64,6 +75,15 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS return importService.importEntity(tenantId, exportData, importSettings); } + @Transactional + @Override + public , I extends EntityId> List> importEntities(TenantId tenantId, List> exportDataList, EntityImportSettings importSettings) { + return exportDataList.stream() + .sorted(Comparator.comparing(exportData -> SUPPORTED_ENTITY_TYPES.indexOf(exportData.getEntityType()))) + .map(exportData -> importEntity(tenantId, exportData, importSettings)) + .collect(Collectors.toList()); + } + @Override public , I extends EntityId> E findEntityById(TenantId tenantId, I id) { @@ -81,11 +101,17 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS @SuppressWarnings("unchecked") private , D extends EntityExportData> EntityExportService getExportService(EntityType entityType) { + if (!SUPPORTED_ENTITY_TYPES.contains(entityType)) { + throw new IllegalArgumentException("Export for entity type " + entityType + " is not supported"); + } return (EntityExportService) exportServices.get(entityType); } @SuppressWarnings("unchecked") private , D extends EntityExportData> EntityImportService getImportService(EntityType entityType) { + if (!SUPPORTED_ENTITY_TYPES.contains(entityType)) { + throw new IllegalArgumentException("Import for entity type " + entityType + " is not supported"); + } return (EntityImportService) importServices.get(entityType); } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/EntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/EntitiesExportImportService.java index 1eb085d219..e8d4a40efe 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/EntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/EntitiesExportImportService.java @@ -23,10 +23,14 @@ import org.thingsboard.server.service.exportimport.exporting.data.EntityExportDa import org.thingsboard.server.service.exportimport.importing.EntityImportResult; import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; +import java.util.List; + public interface EntitiesExportImportService extends ExportableEntitiesService { , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId, EntityExportSettings exportSettings); , I extends EntityId> EntityImportResult importEntity(TenantId tenantId, EntityExportData exportData, EntityImportSettings importSettings); + , I extends EntityId> List> importEntities(TenantId tenantId, List> exportDataList, EntityImportSettings importSettings); + } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/EntityExportData.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/EntityExportData.java index 74116276cb..f77a5fbb29 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/EntityExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/EntityExportData.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.exportimport.exporting.data; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; @@ -42,6 +43,7 @@ import java.util.List; public abstract class EntityExportData> { private E mainEntity; + @JsonInclude(JsonInclude.Include.NON_NULL) private List inboundRelations; @JsonIgnore diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java index b7287c55af..92e5e9ae78 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Customer.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty.Access; import com.fasterxml.jackson.databind.JsonNode; import io.swagger.annotations.ApiModelProperty; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import org.thingsboard.server.common.data.id.CustomerId; @@ -27,6 +28,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; +@EqualsAndHashCode(callSuper = false) public class Customer extends ContactBased implements HasTenantId, ExportableEntity { private static final long serialVersionUID = -1599722990298929275L; @@ -39,7 +41,7 @@ public class Customer extends ContactBased implements HasTenantId, E private TenantId tenantId; @Getter @Setter - private CustomerId externalId; // FIXME [viacheslav]: add to hashcode, equals, etc + private CustomerId externalId; public Customer() { super(); @@ -167,37 +169,6 @@ public class Customer extends ContactBased implements HasTenantId, E return getTitle(); } - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((tenantId == null) ? 0 : tenantId.hashCode()); - result = prime * result + ((title == null) ? 0 : title.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!super.equals(obj)) - return false; - if (getClass() != obj.getClass()) - return false; - Customer other = (Customer) obj; - if (tenantId == null) { - if (other.tenantId != null) - return false; - } else if (!tenantId.equals(other.tenantId)) - return false; - if (title == null) { - if (other.title != null) - return false; - } else if (!title.equals(other.title)) - return false; - return true; - } - @Override public String toString() { StringBuilder builder = new StringBuilder(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java b/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java index dce910c4ff..59c122f3d0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Dashboard.java @@ -17,10 +17,12 @@ package org.thingsboard.server.common.data; import com.fasterxml.jackson.databind.JsonNode; import io.swagger.annotations.ApiModelProperty; +import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import org.thingsboard.server.common.data.id.DashboardId; +@EqualsAndHashCode(callSuper = false) public class Dashboard extends DashboardInfo implements ExportableEntity { private static final long serialVersionUID = 872682138346187503L; @@ -60,31 +62,6 @@ public class Dashboard extends DashboardInfo implements ExportableEntity { T findByTenantIdAndExternalId(UUID tenantId, UUID externalId); T findByTenantIdAndId(UUID tenantId, UUID id); - // fixme [viacheslav]: get rid of boilerplate ? - EntityType getEntityType(); - /* - * default > ExportableEntityRepository getExportableEntityRepository() { - * ((ExportableEntityRepository) getJpaRepository).find... - * */ + EntityType getEntityType(); } From 6dc962882e954e00cec3caf8679af88461e29d94 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Wed, 30 Mar 2022 16:42:33 +0300 Subject: [PATCH 08/39] Add more options for entity export and import settings; refactoring --- .../upgrade/{3.4 => 3.3.4}/schema_update.sql | 0 .../server/controller/BaseController.java | 2 +- .../EntitiesExportImportController.java | 49 ++--- .../exporting/EntityExportSettings.java | 1 + .../exporting/data/EntityExportData.java | 3 +- .../impl/AbstractEntityExportService.java | 12 +- .../importing/EntityImportService.java | 6 - .../importing/EntityImportSettings.java | 4 + .../impl/AbstractEntityImportService.java | 83 ++++++-- .../importing/impl/AssetImportService.java | 9 +- .../importing/impl/CustomerImportService.java | 4 +- .../impl/DashboardImportService.java | 17 +- .../importing/impl/DeviceImportService.java | 15 +- .../impl/DeviceProfileImportService.java | 15 +- .../impl/RuleChainImportService.java | 8 +- .../main/resources/sql/schema-entities.sql | 194 +++++++++--------- 16 files changed, 234 insertions(+), 188 deletions(-) rename application/src/main/data/upgrade/{3.4 => 3.3.4}/schema_update.sql (100%) diff --git a/application/src/main/data/upgrade/3.4/schema_update.sql b/application/src/main/data/upgrade/3.3.4/schema_update.sql similarity index 100% rename from application/src/main/data/upgrade/3.4/schema_update.sql rename to application/src/main/data/upgrade/3.3.4/schema_update.sql diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index e244bcf680..7416444104 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -924,7 +924,7 @@ public abstract class BaseController { } } - public & HasTenantId, I extends EntityId> void onEntityUpdatedOrCreated(User user, E savedEntity, E oldEntity, boolean isNewEntity) { + protected & HasTenantId, I extends EntityId> void onEntityUpdatedOrCreated(User user, E savedEntity, E oldEntity, boolean isNewEntity) { boolean notifyEdgeService = false; EntityType entityType = savedEntity.getId().getEntityType(); diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index ec2d4273cc..f7513eb2d5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -72,9 +72,28 @@ public class EntitiesExportImportController extends BaseController { // TODO [viacheslav]: api to export and import whole customer, whole tenant + @PostMapping("/export/{entityType}/{entityId}") + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + public EntityExportData exportEntity(@ApiParam(allowableValues = "DEVICE, DEVICE_PROFILE, ASSET, RULE_CHAIN, DASHBOARD, CUSTOMER") + @PathVariable EntityType entityType, + @PathVariable("entityId") UUID entityUuid, + @RequestParam(defaultValue = "false") boolean exportInboundRelations) throws ThingsboardException { + EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); + checkEntityId(entityId, Operation.READ); + + SecurityUser user = getCurrentUser(); + EntityExportSettings exportSettings = toExportSettings(exportInboundRelations); + + try { // FIXME [viacheslav]: check read permission for relation fromId + return exportImportService.exportEntity(user.getTenantId(), entityId, exportSettings); + } catch (Exception e) { + throw handleException(e); + } + } + @PostMapping("/exportByFilter") @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public List> exportEntitiesByFilter(@RequestBody EntityFilter filter, + public List> exportEntitiesByFilter(@RequestBody EntityFilter filter, // TODO [viacheslav]: exportInboundRelations, exportOutboundRelations @RequestParam(defaultValue = "false") boolean exportInboundRelations, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "100") int pageSize) throws ThingsboardException { @@ -118,25 +137,6 @@ public class EntitiesExportImportController extends BaseController { } } - @PostMapping("/export/{entityType}/{entityId}") - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public EntityExportData exportEntity(@ApiParam(allowableValues = "DEVICE, DEVICE_PROFILE, ASSET, RULE_CHAIN, DASHBOARD, CUSTOMER") - @PathVariable EntityType entityType, - @PathVariable("entityId") UUID entityUuid, - @RequestParam(defaultValue = "false") boolean exportInboundRelations) throws ThingsboardException { - EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); - checkEntityId(entityId, Operation.READ); - - SecurityUser user = getCurrentUser(); - EntityExportSettings exportSettings = toExportSettings(exportInboundRelations); - - try { // FIXME [viacheslav]: check read permission for relation fromId - return exportImportService.exportEntity(user.getTenantId(), entityId, exportSettings); - } catch (Exception e) { - throw handleException(e); - } - } - @PostMapping("/import") @PreAuthorize("hasAuthority('TENANT_ADMIN')") @@ -150,7 +150,11 @@ public class EntitiesExportImportController extends BaseController { } try { - return exportImportService.importEntities(user.getTenantId(), exportDataList, importSettings); + List>> importResultList = exportImportService.importEntities(user.getTenantId(), exportDataList, importSettings); + importResultList.forEach(entityImportResult -> { + onEntityUpdatedOrCreated(user, entityImportResult.getSavedEntity(), entityImportResult.getOldEntity(), entityImportResult.getOldEntity() == null); + }); + return importResultList; } catch (Exception e) { throw handleException(e); } @@ -188,10 +192,9 @@ public class EntitiesExportImportController extends BaseController { private EntityImportSettings toImportSettings(boolean importInboundRelations) { - EntityImportSettings importSettings = EntityImportSettings.builder() + return EntityImportSettings.builder() .importInboundRelations(importInboundRelations) .build(); - return importSettings; } private EntityExportSettings toExportSettings(boolean exportInboundRelations) { diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/EntityExportSettings.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/EntityExportSettings.java index 3755ce801d..f4eea9913a 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/EntityExportSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/EntityExportSettings.java @@ -26,4 +26,5 @@ import lombok.NoArgsConstructor; @Builder public class EntityExportSettings { private boolean exportInboundRelations; + private boolean exportOutboundRelations; } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/EntityExportData.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/EntityExportData.java index f77a5fbb29..b9cc8c71a3 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/EntityExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/EntityExportData.java @@ -39,12 +39,13 @@ import java.util.List; @Type(name = "DASHBOARD", value = DashboardExportData.class), @Type(name = "CUSTOMER", value = CustomerExportData.class) }) +@JsonInclude(JsonInclude.Include.NON_NULL) @Data public abstract class EntityExportData> { private E mainEntity; - @JsonInclude(JsonInclude.Include.NON_NULL) private List inboundRelations; + private List outboundRelations; @JsonIgnore public abstract EntityType getEntityType(); diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AbstractEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AbstractEntityExportService.java index 7b298a7d3c..ee6437c7dc 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AbstractEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AbstractEntityExportService.java @@ -45,17 +45,19 @@ public abstract class AbstractEntityExportService inboundRelations = relationService.findByTo(tenantId, entityId, RelationTypeGroup.COMMON); + exportData.setInboundRelations(inboundRelations); + } + if (exportSettings.isExportOutboundRelations()) { + List outboundRelations = relationService.findByFrom(tenantId, entityId, RelationTypeGroup.COMMON); + exportData.setOutboundRelations(outboundRelations); } return exportData; } - protected List getInboundRelations(TenantId tenantId, I entityId) { - return relationService.findByTo(tenantId, entityId, RelationTypeGroup.COMMON); // FIXME [viacheslav]: RelationTypeGroup - } - protected void setRelatedEntities(TenantId tenantId, E mainEntity, D exportData) {} protected abstract D newExportData(); diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportService.java index b001290cef..8697b04f59 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportService.java @@ -23,12 +23,6 @@ import org.thingsboard.server.service.exportimport.exporting.data.EntityExportDa public interface EntityImportService, D extends EntityExportData> { - /* - * TODO [viacheslav]: should be as options: - * to update related entities e.g. firmware or device profile if entity already exists - * to delete current relations when importing new - * to ignore when cannot find linked entity by external id - * */ EntityImportResult importEntity(TenantId tenantId, D exportData, EntityImportSettings importSettings); EntityType getEntityType(); diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportSettings.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportSettings.java index d153d1378a..22b67bcbb4 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportSettings.java @@ -26,4 +26,8 @@ import lombok.NoArgsConstructor; @Builder public class EntityImportSettings { private boolean importInboundRelations; + private boolean importOutboundRelations; + private boolean removeExistingRelationsAndSaveNew; + + private boolean updateReferencesToOtherEntities; } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AbstractEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AbstractEntityImportService.java index 075991104c..b521074bab 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AbstractEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AbstractEntityImportService.java @@ -18,10 +18,13 @@ package org.thingsboard.server.service.exportimport.importing.impl; import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.service.exportimport.ExportableEntitiesService; import org.thingsboard.server.service.exportimport.exporting.data.EntityExportData; @@ -29,6 +32,11 @@ import org.thingsboard.server.service.exportimport.importing.EntityImportResult; import org.thingsboard.server.service.exportimport.importing.EntityImportService; import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; +import java.util.LinkedList; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + public abstract class AbstractEntityImportService, D extends EntityExportData> implements EntityImportService { @Autowired @Lazy @@ -36,11 +44,11 @@ public abstract class AbstractEntityImportService importEntity(TenantId tenantId, D exportData, EntityImportSettings importSettings) { + public EntityImportResult importEntity(TenantId tenantId, D exportData, EntityImportSettings importSettings) { E entity = exportData.getMainEntity(); - E existingEntity = findByExternalId(tenantId, entity.getId()); + E existingEntity = exportableEntitiesService.findEntityByExternalId(tenantId, entity.getId()); entity.setExternalId(entity.getId()); entity.setTenantId(tenantId); @@ -51,18 +59,19 @@ public abstract class AbstractEntityImportService() { + @Override + public ID get(TenantId tenantId, Function idExtractor) { + if (existingEntity == null || importSettings.isUpdateReferencesToOtherEntities()) { + return getInternalId(tenantId, idExtractor.apply(entity)); + } else { + return idExtractor.apply(existingEntity); + } } - exportData.getInboundRelations().forEach(relation -> { - relation.setTo(savedEntity.getId()); - relation.setFrom(getInternalId(tenantId, relation.getFrom())); - relationService.saveRelation(tenantId, relation); - }); - } + }); + + E savedEntity = saveEntity(tenantId, entity, existingEntity, exportData); + importRelations(tenantId, savedEntity, existingEntity, exportData, importSettings); EntityImportResult importResult = new EntityImportResult<>(); importResult.setSavedEntity(savedEntity); @@ -70,16 +79,46 @@ public abstract class AbstractEntityImportService idProvider) {} + + protected abstract E saveEntity(TenantId tenantId, E entity, E existingEntity, D exportData); + + private void importRelations(TenantId tenantId, E savedEntity, E existingEntity, D exportData, EntityImportSettings importSettings) { + List newRelations = new LinkedList<>(); - private E findByExternalId(TenantId tenantId, I externalId) { - return exportableEntitiesService.findEntityByExternalId(tenantId, externalId); + if (importSettings.isImportInboundRelations() && CollectionUtils.isNotEmpty(exportData.getInboundRelations())) { + newRelations.addAll(exportData.getInboundRelations().stream() + .peek(relation -> { + relation.setTo(savedEntity.getId()); + relation.setFrom(getInternalId(tenantId, relation.getFrom())); + }) + .collect(Collectors.toList())); + if (importSettings.isRemoveExistingRelationsAndSaveNew() && existingEntity != null) { + relationService.findByTo(tenantId, savedEntity.getId(), RelationTypeGroup.COMMON).forEach(existingRelation -> { + relationService.deleteRelation(tenantId, existingRelation); + }); + } + } + if (importSettings.isImportOutboundRelations() && CollectionUtils.isNotEmpty(exportData.getOutboundRelations())) { + newRelations.addAll(exportData.getOutboundRelations().stream() + .peek(relation -> { + relation.setTo(getInternalId(tenantId, relation.getTo())); + relation.setFrom(savedEntity.getId()); + }) + .collect(Collectors.toList())); + if (importSettings.isRemoveExistingRelationsAndSaveNew() && existingEntity != null) { + relationService.findByFrom(tenantId, savedEntity.getId(), RelationTypeGroup.COMMON).forEach(existingRelation -> { + relationService.deleteRelation(tenantId, existingRelation); + }); + } + } + + newRelations.forEach(relation -> relationService.saveRelation(tenantId, relation)); } - // FIXME [viacheslav]: review use cases for version controlling: in the same tenant, between tenants, between environments and different tenants - protected final ID getInternalId(TenantId tenantId, ID externalId) { - if (externalId == null) { + private ID getInternalId(TenantId tenantId, ID externalId) { + if (externalId == null || externalId.isNullUid()) { return null; } HasId entity = exportableEntitiesService.findEntityByExternalId(tenantId, externalId); @@ -89,4 +128,8 @@ public abstract class AbstractEntityImportService { + I get(TenantId tenantId, Function idExtractor); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AssetImportService.java index 6699219388..0d6e6d01b5 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AssetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AssetImportService.java @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.exportimport.exporting.data.AssetExportData; -import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; @Service @TbCoreComponent @@ -33,11 +32,13 @@ public class AssetImportService extends AbstractEntityImportService idProvider) { + asset.setCustomerId(idProvider.get(tenantId, Asset::getCustomerId)); + } + @Override + protected Asset saveEntity(TenantId tenantId, Asset asset, Asset existingAsset, AssetExportData exportData) { return assetService.saveAsset(asset); } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/CustomerImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/CustomerImportService.java index 251dd4acbf..22cdc10204 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/CustomerImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/CustomerImportService.java @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.exportimport.exporting.data.CustomerExportData; -import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; @Service @TbCoreComponent @@ -33,9 +32,8 @@ public class CustomerImportService extends AbstractEntityImportService idProvider) { +// if (existingDashboard == null) { +// dashboard.setAssignedCustomers(null); // FIXME [viacheslav]: need to assign dashboard to customers ? +// } else { +// dashboard.setAssignedCustomers(existingDashboard.getAssignedCustomers()); +// } + } + @Override + protected Dashboard saveEntity(TenantId tenantId, Dashboard dashboard, Dashboard existingDashboard, DashboardExportData exportData) { return dashboardService.saveDashboard(dashboard); } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceImportService.java index 01312dfa44..8c02edec05 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceImportService.java @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.exportimport.exporting.data.DeviceExportData; -import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; @Service @TbCoreComponent @@ -33,14 +32,16 @@ public class DeviceImportService extends AbstractEntityImportService idProvider) { + device.setCustomerId(idProvider.get(tenantId, Device::getCustomerId)); + device.setDeviceProfileId(idProvider.get(tenantId, Device::getDeviceProfileId)); + device.setFirmwareId(idProvider.get(tenantId, Device::getFirmwareId)); + device.setSoftwareId(idProvider.get(tenantId, Device::getSoftwareId)); + } + @Override + protected Device saveEntity(TenantId tenantId, Device device, Device existingDevice, DeviceExportData exportData) { return deviceService.saveDeviceWithCredentials(device, exportData.getCredentials()); } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceProfileImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceProfileImportService.java index ef72d982ee..9981607481 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceProfileImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceProfileImportService.java @@ -24,7 +24,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.exportimport.exporting.data.DeviceProfileExportData; -import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; @Service @TbCoreComponent @@ -33,14 +32,16 @@ public class DeviceProfileImportService extends AbstractEntityImportService idProvider) { + deviceProfile.setDefaultRuleChainId(idProvider.get(tenantId, DeviceProfile::getDefaultRuleChainId)); + deviceProfile.setDefaultDashboardId(idProvider.get(tenantId, DeviceProfile::getDefaultDashboardId)); + deviceProfile.setFirmwareId(idProvider.get(tenantId, DeviceProfile::getFirmwareId)); + deviceProfile.setSoftwareId(idProvider.get(tenantId, DeviceProfile::getSoftwareId)); + } + @Override + protected DeviceProfile saveEntity(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfile existingDeviceProfile, DeviceProfileExportData exportData) { return deviceProfileService.saveDeviceProfile(deviceProfile); } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/RuleChainImportService.java index 5fc1c8fb8c..6be2fc4317 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/RuleChainImportService.java @@ -17,7 +17,6 @@ package org.thingsboard.server.service.exportimport.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; @@ -26,7 +25,6 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.exportimport.exporting.data.RuleChainExportData; -import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; @Service @TbCoreComponent @@ -35,10 +33,12 @@ public class RuleChainImportService extends AbstractEntityImportService idProvider) { + } - @Transactional @Override - protected RuleChain prepareAndSaveEntity(TenantId tenantId, RuleChain ruleChain, RuleChain existingRuleChain, RuleChainExportData exportData, EntityImportSettings importSettings) { + protected RuleChain saveEntity(TenantId tenantId, RuleChain ruleChain, RuleChain existingRuleChain, RuleChainExportData exportData) { ruleChain.setFirstRuleNodeId(null); // will be set during metadata persisting if (existingRuleChain != null) { ruleChainService.deleteRuleNodes(tenantId, existingRuleChain.getId()); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index e18f73a065..9862ded617 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -72,19 +72,18 @@ CREATE TABLE IF NOT EXISTS entity_alarm ( CONSTRAINT fk_entity_alarm_id FOREIGN KEY (alarm_id) REFERENCES alarm(id) ON DELETE CASCADE ); -CREATE TABLE IF NOT EXISTS asset( - id uuid NOT NULL - CONSTRAINT asset_pkey PRIMARY KEY, - created_time bigint NOT NULL, - additional_info varchar, - customer_id uuid, - name varchar(255), - label varchar(255), - search_text varchar(255), - tenant_id uuid, - type varchar(255), - external_id uuid, - CONSTRAINT asset_name_unq_key UNIQUE (tenant_id, name) +CREATE TABLE IF NOT EXISTS asset ( + id uuid NOT NULL CONSTRAINT asset_pkey PRIMARY KEY, + created_time bigint NOT NULL, + additional_info varchar, + customer_id uuid, + name varchar(255), + label varchar(255), + search_text varchar(255), + tenant_id uuid, + type varchar(255), + external_id uuid, + CONSTRAINT asset_name_unq_key UNIQUE (tenant_id, name) ); CREATE TABLE IF NOT EXISTS audit_log ( @@ -130,53 +129,50 @@ CREATE TABLE IF NOT EXISTS component_descriptor ( ); CREATE TABLE IF NOT EXISTS customer ( - id uuid NOT NULL CONSTRAINT customer_pkey PRIMARY KEY, - created_time bigint NOT NULL, - additional_info varchar, - address varchar, - address2 varchar, - city varchar(255), - country varchar(255), - email varchar(255), - phone varchar(255), - search_text varchar(255), - state varchar(255), - tenant_id uuid, - title varchar(255), - zip varchar(255), - external_id uuid -); - -CREATE TABLE IF NOT EXISTS dashboard -( - id uuid NOT NULL - CONSTRAINT dashboard_pkey PRIMARY KEY, - created_time bigint NOT NULL, - configuration varchar, + id uuid NOT NULL CONSTRAINT customer_pkey PRIMARY KEY, + created_time bigint NOT NULL, + additional_info varchar, + address varchar, + address2 varchar, + city varchar(255), + country varchar(255), + email varchar(255), + phone varchar(255), + search_text varchar(255), + state varchar(255), + tenant_id uuid, + title varchar(255), + zip varchar(255), + external_id uuid +); + +CREATE TABLE IF NOT EXISTS dashboard ( + id uuid NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY, + created_time bigint NOT NULL, + configuration varchar, assigned_customers varchar(1000000), - search_text varchar(255), - tenant_id uuid, - title varchar(255), - mobile_hide boolean DEFAULT false, - mobile_order int, - image varchar(1000000), - external_id uuid + search_text varchar(255), + tenant_id uuid, + title varchar(255), + mobile_hide boolean DEFAULT false, + mobile_order int, + image varchar(1000000), + external_id uuid ); CREATE TABLE IF NOT EXISTS rule_chain ( - id uuid NOT NULL - CONSTRAINT rule_chain_pkey PRIMARY KEY, - created_time bigint NOT NULL, - additional_info varchar, - configuration varchar(10000000), - name varchar(255), - type varchar(255), - first_rule_node_id uuid, - root boolean, - debug_mode boolean, - search_text varchar(255), - tenant_id uuid, - external_id uuid + id uuid NOT NULL CONSTRAINT rule_chain_pkey PRIMARY KEY, + created_time bigint NOT NULL, + additional_info varchar, + configuration varchar(10000000), + name varchar(255), + type varchar(255), + first_rule_node_id uuid, + root boolean, + debug_mode boolean, + search_text varchar(255), + tenant_id uuid, + external_id uuid ); CREATE TABLE IF NOT EXISTS rule_node ( @@ -224,31 +220,31 @@ CREATE TABLE IF NOT EXISTS ota_package ( ); CREATE TABLE IF NOT EXISTS device_profile ( - id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY, - created_time bigint NOT NULL, - name varchar(255), - type varchar(255), - image varchar(1000000), - transport_type varchar(255), - provision_type varchar(255), - profile_data jsonb, - description varchar, - search_text varchar(255), - is_default boolean, - tenant_id uuid, - firmware_id uuid, - software_id uuid, - default_rule_chain_id uuid, - default_dashboard_id uuid, - default_queue_name varchar(255), - provision_device_key varchar, - external_id uuid, - CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), - CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), - CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain (id), - CONSTRAINT fk_default_dashboard_device_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard (id), - CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES ota_package (id), - CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES ota_package (id) + id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY, + created_time bigint NOT NULL, + name varchar(255), + type varchar(255), + image varchar(1000000), + transport_type varchar(255), + provision_type varchar(255), + profile_data jsonb, + description varchar, + search_text varchar(255), + is_default boolean, + tenant_id uuid, + firmware_id uuid, + software_id uuid, + default_rule_chain_id uuid, + default_dashboard_id uuid, + default_queue_name varchar(255), + provision_device_key varchar, + external_id uuid, + CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), + CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), + CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), + CONSTRAINT fk_default_dashboard_device_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id), + CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES ota_package(id), + CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES ota_package(id) ); ALTER TABLE ota_package @@ -266,24 +262,24 @@ ALTER TABLE ota_package -- ); CREATE TABLE IF NOT EXISTS device ( - id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY, - created_time bigint NOT NULL, - additional_info varchar, - customer_id uuid, - device_profile_id uuid NOT NULL, - device_data jsonb, - type varchar(255), - name varchar(255), - label varchar(255), - search_text varchar(255), - tenant_id uuid, - firmware_id uuid, - software_id uuid, - external_id uuid, - CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), - CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile (id), - CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES ota_package (id), - CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES ota_package (id) + id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY, + created_time bigint NOT NULL, + additional_info varchar, + customer_id uuid, + device_profile_id uuid NOT NULL, + device_data jsonb, + type varchar(255), + name varchar(255), + label varchar(255), + search_text varchar(255), + tenant_id uuid, + firmware_id uuid, + software_id uuid, + external_id uuid, + CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), + CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id), + CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES ota_package(id), + CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES ota_package(id) ); CREATE TABLE IF NOT EXISTS device_credentials ( From 19a27f93f456266895af6026cc86f5c7d27971a6 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 31 Mar 2022 19:41:25 +0300 Subject: [PATCH 09/39] Export/import API improvements and refactoring --- .../EntitiesExportImportController.java | 275 ++++++++++++------ .../DefaultEntitiesExportImportService.java | 8 +- .../EntitiesExportImportService.java | 2 +- .../exporting/impl/AssetExportService.java | 2 +- ...vice.java => BaseEntityExportService.java} | 3 +- .../exporting/impl/CustomerExportService.java | 2 +- .../impl/DashboardExportService.java | 2 +- .../exporting/impl/DeviceExportService.java | 3 +- .../impl/DeviceProfileExportService.java | 2 +- .../impl/RuleChainExportService.java | 3 +- .../importing/EntityImportSettings.java | 3 +- .../importing/impl/AssetImportService.java | 8 +- ...vice.java => BaseEntityImportService.java} | 65 +++-- .../importing/impl/CustomerImportService.java | 4 +- .../impl/DashboardImportService.java | 46 ++- .../importing/impl/DeviceImportService.java | 8 +- .../impl/DeviceProfileImportService.java | 8 +- .../impl/RuleChainImportService.java | 57 ++-- .../query/DefaultEntityQueryRepository.java | 2 + .../dao/sql/query/EntityKeyMapping.java | 1 + 20 files changed, 320 insertions(+), 184 deletions(-) rename application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/{AbstractEntityExportService.java => BaseEntityExportService.java} (93%) rename application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/{AbstractEntityImportService.java => BaseEntityImportService.java} (68%) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index f7513eb2d5..411a5ec00e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -15,9 +15,7 @@ */ package org.thingsboard.server.controller; -import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; -import org.apache.commons.collections.CollectionUtils; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -27,10 +25,11 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; -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.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.query.EntityData; import org.thingsboard.server.common.data.query.EntityDataPageLink; import org.thingsboard.server.common.data.query.EntityDataQuery; @@ -38,169 +37,263 @@ import org.thingsboard.server.common.data.query.EntityDataSortOrder; import org.thingsboard.server.common.data.query.EntityFilter; import org.thingsboard.server.common.data.query.EntityKey; import org.thingsboard.server.common.data.query.EntityKeyType; +import org.thingsboard.server.common.data.query.EntityTypeFilter; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.exportimport.EntitiesExportImportService; +import org.thingsboard.server.service.exportimport.ExportableEntitiesService; import org.thingsboard.server.service.exportimport.exporting.EntityExportSettings; import org.thingsboard.server.service.exportimport.exporting.data.EntityExportData; import org.thingsboard.server.service.exportimport.importing.EntityImportResult; import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; -import org.thingsboard.server.service.query.EntityQueryService; 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.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.UUID; +import java.util.function.Function; import java.util.stream.Collectors; import static org.thingsboard.server.dao.sql.query.EntityKeyMapping.CREATED_TIME; @RestController @RequestMapping("/api/entities") +@PreAuthorize("hasAuthority('TENANT_ADMIN')") @TbCoreComponent @RequiredArgsConstructor public class EntitiesExportImportController extends BaseController { private final EntitiesExportImportService exportImportService; - private final EntityQueryService entityQueryService; + private final ExportableEntitiesService exportableEntitiesService; + private final EntityService entityService; - // TODO [viacheslav]: export and import of batches - // TODO [viacheslav]: api to export and import whole customer, whole tenant - + @PostMapping("/export/{entityType}/{id}") + public EntityExportData> exportSingleEntity(@PathVariable EntityType entityType, + @PathVariable UUID id, + @RequestParam Map exportSettingsParams) throws ThingsboardException { + EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, id); + try { + return exportEntity(getTenantId(), entityId, toExportSettings(exportSettingsParams)); + } catch (Exception e) { + throw handleException(e); + } + } - @PostMapping("/export/{entityType}/{entityId}") - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public EntityExportData exportEntity(@ApiParam(allowableValues = "DEVICE, DEVICE_PROFILE, ASSET, RULE_CHAIN, DASHBOARD, CUSTOMER") - @PathVariable EntityType entityType, - @PathVariable("entityId") UUID entityUuid, - @RequestParam(defaultValue = "false") boolean exportInboundRelations) throws ThingsboardException { - EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); - checkEntityId(entityId, Operation.READ); + @PostMapping(value = "/export/{entityType}", params = "ids") + public List>> exportEntitiesByIds(@PathVariable EntityType entityType, + @RequestParam UUID[] ids, + @RequestParam Map exportSettingsParams) throws ThingsboardException { + List entitiesIds = Arrays.stream(ids) + .map(id -> EntityIdFactory.getByTypeAndUuid(entityType, id)) + .collect(Collectors.toList()); + try { + return exportEntitiesByIds(getTenantId(), entitiesIds, toExportSettings(exportSettingsParams)); + } catch (Exception e) { + throw handleException(e); + } + } - SecurityUser user = getCurrentUser(); - EntityExportSettings exportSettings = toExportSettings(exportInboundRelations); + @PostMapping(value = "/export/{entityType}", params = "ids") + public List>> exportAllEntitiesByEntityType(@PathVariable EntityType entityType, + @RequestParam Map exportSettingsParams, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "2147483647") int pageSize, + @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { + TenantId tenantId = getTenantId(); + CustomerId customerId = toCustomerId(customerUuid); - try { // FIXME [viacheslav]: check read permission for relation fromId - return exportImportService.exportEntity(user.getTenantId(), entityId, exportSettings); + EntityTypeFilter entityTypeFilter = new EntityTypeFilter(); + entityTypeFilter.setEntityType(entityType); + try { + return exportEntitiesByFilter(tenantId, customerId, entityTypeFilter, page, pageSize, toExportSettings(exportSettingsParams)); } catch (Exception e) { throw handleException(e); } } @PostMapping("/exportByFilter") - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public List> exportEntitiesByFilter(@RequestBody EntityFilter filter, // TODO [viacheslav]: exportInboundRelations, exportOutboundRelations - @RequestParam(defaultValue = "false") boolean exportInboundRelations, - @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "100") int pageSize) throws ThingsboardException { - EntityDataPageLink pageLink = new EntityDataPageLink(); - pageLink.setPage(page); - pageLink.setPageSize(pageSize); - pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ENTITY_FIELD, CREATED_TIME), EntityDataSortOrder.Direction.DESC)); - EntityDataQuery entityDataQuery = new EntityDataQuery(filter, pageLink, List.of(new EntityKey(EntityKeyType.ENTITY_FIELD, CREATED_TIME)), - Collections.emptyList(), Collections.emptyList()); - - SecurityUser user = getCurrentUser(); - EntityExportSettings exportSettings = toExportSettings(exportInboundRelations); + public List>> exportEntitiesByFilter(@RequestBody EntityFilter filter, + @RequestParam Map exportSettingsParams, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "2147483647") int pageSize, + @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { + TenantId tenantId = getTenantId(); + CustomerId customerId = toCustomerId(customerUuid); + try { + return exportEntitiesByFilter(tenantId, customerId, filter, page, pageSize, toExportSettings(exportSettingsParams)); + } catch (Exception e) { + throw handleException(e); + } + } + // FIXME: too aggressive + @PostMapping("/exportByFilters") + public List>> exportAllEntitiesByFilters(@RequestBody List filters, + @RequestParam Map exportSettingsParams, + @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { + TenantId tenantId = getTenantId(); + CustomerId customerId = toCustomerId(customerUuid); try { - // FIXME [viacheslav]: check read permission for relation fromId - return entityQueryService.findEntityDataByQuery(user, entityDataQuery).getData().stream() - .map(EntityData::getEntityId) - .map(entityId -> { - return exportImportService.exportEntity(user.getTenantId(), entityId, exportSettings); - }) - .collect(Collectors.toList()); + List>> exportDataList = new ArrayList<>(); + for (EntityFilter filter : filters) { + exportDataList.addAll(exportEntitiesByFilter(tenantId, customerId, filter, 0, Integer.MAX_VALUE, toExportSettings(exportSettingsParams))); + } + return exportDataList; } catch (Exception e) { throw handleException(e); } } @PostMapping("/exportByQuery") - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public List> exportEntitiesByQuery(@RequestBody EntityDataQuery query, - @RequestParam(defaultValue = "false") boolean exportInboundRelations) throws ThingsboardException { - SecurityUser user = getCurrentUser(); - EntityExportSettings exportSettings = toExportSettings(exportInboundRelations); -// FIXME [viacheslav]: check read permission for relation fromId + public List>> exportEntitiesByQuery(@RequestBody EntityDataQuery entitiesQuery, + @RequestParam Map exportSettingsParams, + @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { + TenantId tenantId = getTenantId(); + CustomerId customerId = toCustomerId(customerUuid); try { - return entityQueryService.findEntityDataByQuery(user, query).getData().stream() - .map(EntityData::getEntityId) - .map(entityId -> exportImportService.exportEntity(user.getTenantId(), entityId, exportSettings)) - .collect(Collectors.toList()); + return exportEntitiesByQuery(tenantId, customerId, entitiesQuery, toExportSettings(exportSettingsParams)); } catch (Exception e) { throw handleException(e); } } - @PostMapping("/import") - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public List>> importEntity(@RequestBody List>> exportDataList, - @RequestParam(defaultValue = "false") boolean importInboundRelations) throws ThingsboardException { - SecurityUser user = getCurrentUser(); - EntityImportSettings importSettings = toImportSettings(importInboundRelations); + private List>> exportEntitiesByFilter(TenantId tenantId, CustomerId customerId, EntityFilter filter, int page, int pageSize, EntityExportSettings exportSettings) throws ThingsboardException { + EntityDataPageLink pageLink = new EntityDataPageLink(); + pageLink.setPage(page); + pageLink.setPageSize(pageSize); + EntityKey sortProperty = new EntityKey(EntityKeyType.ENTITY_FIELD, CREATED_TIME); + pageLink.setSortOrder(new EntityDataSortOrder(sortProperty, EntityDataSortOrder.Direction.DESC)); - for (EntityExportData> exportData : exportDataList) { - checkPermissionsForImport(user, exportData, importSettings); - } + EntityDataQuery query = new EntityDataQuery(filter, pageLink, List.of(sortProperty), Collections.emptyList(), Collections.emptyList()); + return exportEntitiesByQuery(tenantId, customerId, query, exportSettings); + } - try { - List>> importResultList = exportImportService.importEntities(user.getTenantId(), exportDataList, importSettings); - importResultList.forEach(entityImportResult -> { - onEntityUpdatedOrCreated(user, entityImportResult.getSavedEntity(), entityImportResult.getOldEntity(), entityImportResult.getOldEntity() == null); - }); - return importResultList; - } catch (Exception e) { - throw handleException(e); + private List>> exportEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query, EntityExportSettings exportSettings) throws ThingsboardException { + List entitiesIds = entityService.findEntityDataByQuery(tenantId, customerId, query).getData().stream() + .map(EntityData::getEntityId) + .collect(Collectors.toList()); + return exportEntitiesByIds(tenantId, entitiesIds, exportSettings); + } + + private List>> exportEntitiesByIds(TenantId tenantId, List entitiesIds, EntityExportSettings exportSettings) throws ThingsboardException { + List>> exportDataList = new ArrayList<>(); + for (EntityId entityId : entitiesIds) { + exportDataList.add(exportEntity(tenantId, entityId, exportSettings)); } + return exportDataList; } - public void checkPermissionsForExport(SecurityUser user, EntityId entityId, EntityExportSettings exportSettings) throws ThingsboardException { + private , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId, EntityExportSettings exportSettings) throws ThingsboardException { checkEntityId(entityId, Operation.READ); + + List relations = new LinkedList<>(); if (exportSettings.isExportInboundRelations()) { - for (EntityRelation entityRelation : relationService.findByTo(user.getTenantId(), entityId, RelationTypeGroup.COMMON)) { - EntityId fromId = entityRelation.getFrom(); - checkEntityId(fromId, Operation.READ); + relations.addAll(relationService.findByTo(tenantId, entityId, RelationTypeGroup.COMMON)); + } + if (exportSettings.isExportOutboundRelations()) { + relations.addAll(relationService.findByFrom(tenantId, entityId, RelationTypeGroup.COMMON)); + } + for (EntityRelation relation : relations) { + if (!relation.getFrom().equals(entityId)) { + checkEntityId(relation.getFrom(), Operation.READ); + } else if (!relation.getTo().equals(entityId)) { + checkEntityId(relation.getTo(), Operation.READ); } } + + return exportImportService.exportEntity(tenantId, entityId, exportSettings); } - public void checkPermissionsForImport(SecurityUser user, EntityExportData> exportData, EntityImportSettings importSettings) throws ThingsboardException { - ExportableEntity existingEntity = exportImportService.findEntityByExternalId(user.getTenantId(), exportData.getMainEntity().getId()); - if (existingEntity != null) { - checkEntityId(existingEntity.getId(), Operation.WRITE); - } else { - accessControlService.checkPermission(user, Resource.of(exportData.getEntityType()), Operation.CREATE); + + @PostMapping("/import") + public List>> importEntity(@RequestBody List>> exportDataList, + @RequestParam Map importSettingsParams) throws ThingsboardException { + SecurityUser user = getCurrentUser(); + EntityImportSettings importSettings = toImportSettings(importSettingsParams); + + try { + return importEntities(user, exportDataList, importSettings) + .stream().peek(entityImportResult -> { + onEntityUpdatedOrCreated(user, entityImportResult.getSavedEntity(), entityImportResult.getOldEntity(), entityImportResult.getOldEntity() == null); + }) + .collect(Collectors.toList()); + } catch (Exception e) { + throw handleException(e); } + } + - if (importSettings.isImportInboundRelations() && CollectionUtils.isNotEmpty(exportData.getInboundRelations())) { - for (EntityRelation entityRelation : exportData.getInboundRelations()) { - ExportableEntity entityFrom = exportImportService.findEntityByExternalId(user.getTenantId(), entityRelation.getFrom()); - if (entityFrom != null) { - accessControlService.checkPermission(user, Resource.of(entityFrom.getId().getEntityType()), Operation.WRITE, entityFrom.getId(), entityFrom); - } else { - throw new ThingsboardException("Relation with non-existing entity", ThingsboardErrorCode.BAD_REQUEST_PARAMS); + public List>> importEntities(SecurityUser user, List>> exportDataList, EntityImportSettings importSettings) throws ThingsboardException { + for (EntityExportData> exportData : exportDataList) { + ExportableEntity existingEntity = exportableEntitiesService.findEntityByExternalId(user.getTenantId(), exportData.getMainEntity().getId()); + if (existingEntity != null) { + checkEntityId(existingEntity.getId(), Operation.WRITE); + } else { + accessControlService.checkPermission(user, Resource.of(exportData.getEntityType()), Operation.CREATE); + } + + List relations = new LinkedList<>(); + if (importSettings.isImportInboundRelations() && exportData.getInboundRelations() != null) { + relations.addAll(exportData.getInboundRelations()); + } + if (importSettings.isImportOutboundRelations() && exportData.getOutboundRelations() != null) { + relations.addAll(exportData.getOutboundRelations()); + } + for (EntityRelation relation : relations) { + EntityId otherEntityId = null; + if (!relation.getFrom().equals(exportData.getMainEntity().getId())) { + otherEntityId = relation.getFrom(); + } else if (!relation.getTo().equals(exportData.getMainEntity().getId())) { + otherEntityId = relation.getTo(); + } + if (otherEntityId != null) { + ExportableEntity otherEntity = exportableEntitiesService.findEntityByExternalId(user.getTenantId(), otherEntityId); + if (otherEntity != null) { + checkEntityId(otherEntity.getId(), Operation.WRITE); + } else { + throw new IllegalArgumentException("Relation is referencing non-existing entity"); + } } } } + + return exportImportService.importEntities(user.getTenantId(), exportDataList, importSettings); } - private EntityImportSettings toImportSettings(boolean importInboundRelations) { - return EntityImportSettings.builder() - .importInboundRelations(importInboundRelations) + private EntityExportSettings toExportSettings(Map exportSettingsParams) { + return EntityExportSettings.builder() + .exportInboundRelations(getParam(exportSettingsParams, "exportInboundRelations", false, Boolean::parseBoolean)) + .exportOutboundRelations(getParam(exportSettingsParams, "exportOutboundRelations", false, Boolean::parseBoolean)) .build(); } - private EntityExportSettings toExportSettings(boolean exportInboundRelations) { - return EntityExportSettings.builder() - .exportInboundRelations(exportInboundRelations) + private EntityImportSettings toImportSettings(Map importSettingsParams) { + return EntityImportSettings.builder() + .importInboundRelations(getParam(importSettingsParams, "importInboundRelations", false, Boolean::parseBoolean)) + .importOutboundRelations(getParam(importSettingsParams, "importOutboundRelations", false, Boolean::parseBoolean)) + .removeExistingRelations(getParam(importSettingsParams, "removeExistingRelations", true, Boolean::parseBoolean)) + .updateReferencesToOtherEntities(getParam(importSettingsParams, "updateReferencesToOtherEntities", true, Boolean::parseBoolean)) .build(); } + protected T getParam(Map requestParams, String key, T defaultValue, Function parsingFunction) { + return parsingFunction.apply(requestParams.getOrDefault(key, defaultValue.toString())); + } + + private CustomerId toCustomerId(UUID customerUuid) { + return new CustomerId(Optional.ofNullable(customerUuid).orElse(EntityId.NULL_UUID)); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/DefaultEntitiesExportImportService.java index 93fa370c94..8d85355849 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/DefaultEntitiesExportImportService.java @@ -39,10 +39,9 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; -// FIXME [viacheslav]: review packages and classes naming @Service @TbCoreComponent -public class DefaultEntitiesExportImportService implements EntitiesExportImportService { +public class DefaultEntitiesExportImportService implements EntitiesExportImportService, ExportableEntitiesService { private final Map> exportServices = new HashMap<>(); private final Map> importServices = new HashMap<>(); @@ -54,8 +53,6 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS ); - // TODO [viacheslav]: export and import of the whole tenant - // TODO [viacheslav]: export and import of the whole customer ? @Override public , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId, EntityExportSettings exportSettings) { EntityType entityType = entityId.getEntityType(); @@ -65,7 +62,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS } - // FIXME [viacheslav]: somehow validate export data + // TODO [viacheslav]: validate export data @Transactional @Override public , I extends EntityId> EntityImportResult importEntity(TenantId tenantId, EntityExportData exportData, EntityImportSettings importSettings) { @@ -80,6 +77,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS public , I extends EntityId> List> importEntities(TenantId tenantId, List> exportDataList, EntityImportSettings importSettings) { return exportDataList.stream() .sorted(Comparator.comparing(exportData -> SUPPORTED_ENTITY_TYPES.indexOf(exportData.getEntityType()))) + // TODO [viacheslav]: order for rule chains (depending on references) .map(exportData -> importEntity(tenantId, exportData, importSettings)) .collect(Collectors.toList()); } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/EntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/EntitiesExportImportService.java index e8d4a40efe..682db6ba21 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/EntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/EntitiesExportImportService.java @@ -25,7 +25,7 @@ import org.thingsboard.server.service.exportimport.importing.EntityImportSetting import java.util.List; -public interface EntitiesExportImportService extends ExportableEntitiesService { +public interface EntitiesExportImportService { , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId, EntityExportSettings exportSettings); diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AssetExportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AssetExportService.java index a48167053b..c22b345299 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AssetExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AssetExportService.java @@ -24,7 +24,7 @@ import org.thingsboard.server.service.exportimport.exporting.data.AssetExportDat @Service @TbCoreComponent -public class AssetExportService extends AbstractEntityExportService { +public class AssetExportService extends BaseEntityExportService { @Override protected AssetExportData newExportData() { diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AbstractEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/BaseEntityExportService.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AbstractEntityExportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/BaseEntityExportService.java index ee6437c7dc..68c78e87c3 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AbstractEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/BaseEntityExportService.java @@ -30,14 +30,13 @@ import org.thingsboard.server.service.exportimport.exporting.data.EntityExportDa import java.util.List; -public abstract class AbstractEntityExportService, D extends EntityExportData> implements EntityExportService { +public abstract class BaseEntityExportService, D extends EntityExportData> implements EntityExportService { @Autowired @Lazy private ExportableEntitiesService exportableEntitiesService; @Autowired private RelationService relationService; - @Override public final D getExportData(TenantId tenantId, I entityId, EntityExportSettings exportSettings) { D exportData = newExportData(); diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/CustomerExportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/CustomerExportService.java index 518a830a21..1f23e8e7d2 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/CustomerExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/CustomerExportService.java @@ -24,7 +24,7 @@ import org.thingsboard.server.service.exportimport.exporting.data.CustomerExport @Service @TbCoreComponent -public class CustomerExportService extends AbstractEntityExportService { +public class CustomerExportService extends BaseEntityExportService { @Override protected CustomerExportData newExportData() { diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DashboardExportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DashboardExportService.java index 167b57be75..17d593157b 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DashboardExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DashboardExportService.java @@ -24,7 +24,7 @@ import org.thingsboard.server.service.exportimport.exporting.data.DashboardExpor @Service @TbCoreComponent -public class DashboardExportService extends AbstractEntityExportService { +public class DashboardExportService extends BaseEntityExportService { @Override protected DashboardExportData newExportData() { diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceExportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceExportService.java index 8a8bc26820..b8c7e65b34 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceExportService.java @@ -28,11 +28,10 @@ import org.thingsboard.server.service.exportimport.exporting.data.DeviceExportDa @Service @TbCoreComponent @RequiredArgsConstructor -public class DeviceExportService extends AbstractEntityExportService { +public class DeviceExportService extends BaseEntityExportService { private final DeviceCredentialsService deviceCredentialsService; - @Override protected void setRelatedEntities(TenantId tenantId, Device device, DeviceExportData exportData) { exportData.setCredentials(deviceCredentialsService.findDeviceCredentialsByDeviceId(TenantId.SYS_TENANT_ID, device.getId())); diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceProfileExportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceProfileExportService.java index ce96e3b1fe..24b5f8891e 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceProfileExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceProfileExportService.java @@ -24,7 +24,7 @@ import org.thingsboard.server.service.exportimport.exporting.data.DeviceProfileE @Service @TbCoreComponent -public class DeviceProfileExportService extends AbstractEntityExportService { +public class DeviceProfileExportService extends BaseEntityExportService { @Override protected DeviceProfileExportData newExportData() { diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/RuleChainExportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/RuleChainExportService.java index 8c173fe2ce..f1b9a27f23 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/RuleChainExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/RuleChainExportService.java @@ -28,11 +28,10 @@ import org.thingsboard.server.service.exportimport.exporting.data.RuleChainExpor @Service @TbCoreComponent @RequiredArgsConstructor -public class RuleChainExportService extends AbstractEntityExportService { +public class RuleChainExportService extends BaseEntityExportService { private final RuleChainService ruleChainService; - @Override protected void setRelatedEntities(TenantId tenantId, RuleChain ruleChain, RuleChainExportData exportData) { exportData.setMetaData(ruleChainService.loadRuleChainMetaData(tenantId, ruleChain.getId())); diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportSettings.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportSettings.java index 22b67bcbb4..c3cf0b531d 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportSettings.java @@ -27,7 +27,6 @@ import lombok.NoArgsConstructor; public class EntityImportSettings { private boolean importInboundRelations; private boolean importOutboundRelations; - private boolean removeExistingRelationsAndSaveNew; - + private boolean removeExistingRelations; private boolean updateReferencesToOtherEntities; } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AssetImportService.java index 0d6e6d01b5..04e5a5eeca 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AssetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AssetImportService.java @@ -28,17 +28,13 @@ import org.thingsboard.server.service.exportimport.exporting.data.AssetExportDat @Service @TbCoreComponent @RequiredArgsConstructor -public class AssetImportService extends AbstractEntityImportService { +public class AssetImportService extends BaseEntityImportService { private final AssetService assetService; @Override - protected void setLinkedEntitiesIds(TenantId tenantId, Asset asset, IdProvider idProvider) { + protected Asset prepareAndSave(TenantId tenantId, Asset asset, AssetExportData exportData, NewIdProvider idProvider) { asset.setCustomerId(idProvider.get(tenantId, Asset::getCustomerId)); - } - - @Override - protected Asset saveEntity(TenantId tenantId, Asset asset, Asset existingAsset, AssetExportData exportData) { return assetService.saveAsset(asset); } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AbstractEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/BaseEntityImportService.java similarity index 68% rename from application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AbstractEntityImportService.java rename to application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/BaseEntityImportService.java index b521074bab..214e314ffd 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AbstractEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/BaseEntityImportService.java @@ -15,10 +15,12 @@ */ package org.thingsboard.server.service.exportimport.importing.impl; +import lombok.RequiredArgsConstructor; import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; @@ -32,12 +34,16 @@ import org.thingsboard.server.service.exportimport.importing.EntityImportResult; import org.thingsboard.server.service.exportimport.importing.EntityImportService; import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; +import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.BiConsumer; import java.util.function.Function; import java.util.stream.Collectors; -public abstract class AbstractEntityImportService, D extends EntityExportData> implements EntityImportService { +public abstract class BaseEntityImportService, D extends EntityExportData> implements EntityImportService { @Autowired @Lazy private ExportableEntitiesService exportableEntitiesService; @@ -59,18 +65,7 @@ public abstract class AbstractEntityImportService() { - @Override - public ID get(TenantId tenantId, Function idExtractor) { - if (existingEntity == null || importSettings.isUpdateReferencesToOtherEntities()) { - return getInternalId(tenantId, idExtractor.apply(entity)); - } else { - return idExtractor.apply(existingEntity); - } - } - }); - - E savedEntity = saveEntity(tenantId, entity, existingEntity, exportData); + E savedEntity = prepareAndSave(tenantId, entity, exportData, new NewIdProvider(entity, existingEntity, importSettings)); importRelations(tenantId, savedEntity, existingEntity, exportData, importSettings); EntityImportResult importResult = new EntityImportResult<>(); @@ -79,9 +74,7 @@ public abstract class AbstractEntityImportService idProvider) {} - - protected abstract E saveEntity(TenantId tenantId, E entity, E existingEntity, D exportData); + protected abstract E prepareAndSave(TenantId tenantId, E entity, D exportData, NewIdProvider idProvider); private void importRelations(TenantId tenantId, E savedEntity, E existingEntity, D exportData, EntityImportSettings importSettings) { @@ -94,7 +87,7 @@ public abstract class AbstractEntityImportService { relationService.deleteRelation(tenantId, existingRelation); }); @@ -107,14 +100,16 @@ public abstract class AbstractEntityImportService { relationService.deleteRelation(tenantId, existingRelation); }); } } - newRelations.forEach(relation -> relationService.saveRelation(tenantId, relation)); + newRelations.forEach(relation -> { + relationService.saveRelation(tenantId, relation); + }); } private ID getInternalId(TenantId tenantId, ID externalId) { @@ -128,8 +123,36 @@ public abstract class AbstractEntityImportService { - I get(TenantId tenantId, Function idExtractor); + @RequiredArgsConstructor + protected class NewIdProvider { + private final E entity; + private final E existingEntity; + private final EntityImportSettings importSettings; + + private final Set ALWAYS_UPDATE_REFERENCED_IDS = Set.of( + EntityType.RULE_CHAIN + ); + + public ID get(TenantId tenantId, Function idExtractor) { + if (existingEntity == null || importSettings.isUpdateReferencesToOtherEntities() + || ALWAYS_UPDATE_REFERENCED_IDS.contains(getEntityType())) { + return getInternalId(tenantId, idExtractor.apply(entity)); + } else { + return idExtractor.apply(existingEntity); + } + } + + public Set get(TenantId tenantId, Function> listExtractor, Function idGetter, BiConsumer idSetter) { + if (existingEntity == null || importSettings.isUpdateReferencesToOtherEntities()) { + return Optional.ofNullable(listExtractor.apply(entity)).orElse(Collections.emptySet()).stream() + .peek(t -> { + idSetter.accept(t, getInternalId(tenantId, idGetter.apply(t))); + }) + .collect(Collectors.toSet()); + } else { + return listExtractor.apply(existingEntity); + } + } } } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/CustomerImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/CustomerImportService.java index 22cdc10204..7b5b3e3248 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/CustomerImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/CustomerImportService.java @@ -28,12 +28,12 @@ import org.thingsboard.server.service.exportimport.exporting.data.CustomerExport @Service @TbCoreComponent @RequiredArgsConstructor -public class CustomerImportService extends AbstractEntityImportService { +public class CustomerImportService extends BaseEntityImportService { private final CustomerService customerService; @Override - protected Customer saveEntity(TenantId tenantId, Customer customer, Customer existingCustomer, CustomerExportData exportData) { + protected Customer prepareAndSave(TenantId tenantId, Customer customer, CustomerExportData exportData, NewIdProvider idProvider) { return customerService.saveCustomer(customer); } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DashboardImportService.java index d78f0b40f5..2bf9a7926a 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DashboardImportService.java @@ -19,31 +19,55 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.ShortCustomerInfo; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.exportimport.exporting.data.DashboardExportData; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + @Service @TbCoreComponent @RequiredArgsConstructor -public class DashboardImportService extends AbstractEntityImportService { +public class DashboardImportService extends BaseEntityImportService { private final DashboardService dashboardService; + // TODO [viacheslav]: improve the code @Override - protected void setLinkedEntitiesIds(TenantId tenantId, Dashboard dashboard, IdProvider idProvider) { -// if (existingDashboard == null) { -// dashboard.setAssignedCustomers(null); // FIXME [viacheslav]: need to assign dashboard to customers ? -// } else { -// dashboard.setAssignedCustomers(existingDashboard.getAssignedCustomers()); -// } - } + protected Dashboard prepareAndSave(TenantId tenantId, Dashboard dashboard, DashboardExportData exportData, NewIdProvider idProvider) { + if (dashboard.getId() == null) { + Set assignedCustomers = idProvider.get(tenantId, Dashboard::getAssignedCustomers, ShortCustomerInfo::getCustomerId, ShortCustomerInfo::setCustomerId); + dashboard.setAssignedCustomers(null); + dashboard = dashboardService.saveDashboard(dashboard); + for (ShortCustomerInfo customerInfo : assignedCustomers) { + dashboardService.assignDashboardToCustomer(tenantId, dashboard.getId(), customerInfo.getCustomerId()); + } + } else { + Set existingAssignedCustomers = dashboardService.findDashboardById(tenantId, dashboard.getId()).getAssignedCustomers().stream() + .map(ShortCustomerInfo::getCustomerId).collect(Collectors.toSet()); + Set newAssignedCustomers = idProvider.get(tenantId, Dashboard::getAssignedCustomers, ShortCustomerInfo::getCustomerId, ShortCustomerInfo::setCustomerId).stream() + .map(ShortCustomerInfo::getCustomerId).collect(Collectors.toSet()); - @Override - protected Dashboard saveEntity(TenantId tenantId, Dashboard dashboard, Dashboard existingDashboard, DashboardExportData exportData) { - return dashboardService.saveDashboard(dashboard); + Set toUnassign = new HashSet<>(existingAssignedCustomers); + toUnassign.removeAll(newAssignedCustomers); + for (CustomerId customerId : toUnassign) { + dashboardService.unassignDashboardFromCustomer(tenantId, dashboard.getId(), customerId); + } + Set toAssign = new HashSet<>(newAssignedCustomers); + toAssign.removeAll(existingAssignedCustomers); + for (CustomerId customerId : toAssign) { + dashboardService.assignDashboardToCustomer(tenantId, dashboard.getId(), customerId); + } + dashboard.setAssignedCustomers(dashboardService.findDashboardById(tenantId, dashboard.getId()).getAssignedCustomers()); + dashboard = dashboardService.saveDashboard(dashboard); + } + return dashboard; } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceImportService.java index 8c02edec05..ee6d89b774 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceImportService.java @@ -28,20 +28,16 @@ import org.thingsboard.server.service.exportimport.exporting.data.DeviceExportDa @Service @TbCoreComponent @RequiredArgsConstructor -public class DeviceImportService extends AbstractEntityImportService { +public class DeviceImportService extends BaseEntityImportService { private final DeviceService deviceService; @Override - protected void setLinkedEntitiesIds(TenantId tenantId, Device device, IdProvider idProvider) { + protected Device prepareAndSave(TenantId tenantId, Device device, DeviceExportData exportData, NewIdProvider idProvider) { device.setCustomerId(idProvider.get(tenantId, Device::getCustomerId)); device.setDeviceProfileId(idProvider.get(tenantId, Device::getDeviceProfileId)); device.setFirmwareId(idProvider.get(tenantId, Device::getFirmwareId)); device.setSoftwareId(idProvider.get(tenantId, Device::getSoftwareId)); - } - - @Override - protected Device saveEntity(TenantId tenantId, Device device, Device existingDevice, DeviceExportData exportData) { return deviceService.saveDeviceWithCredentials(device, exportData.getCredentials()); } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceProfileImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceProfileImportService.java index 9981607481..5d05bd06eb 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceProfileImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceProfileImportService.java @@ -28,20 +28,16 @@ import org.thingsboard.server.service.exportimport.exporting.data.DeviceProfileE @Service @TbCoreComponent @RequiredArgsConstructor -public class DeviceProfileImportService extends AbstractEntityImportService { +public class DeviceProfileImportService extends BaseEntityImportService { private final DeviceProfileService deviceProfileService; @Override - protected void setLinkedEntitiesIds(TenantId tenantId, DeviceProfile deviceProfile, IdProvider idProvider) { + protected DeviceProfile prepareAndSave(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfileExportData exportData, NewIdProvider idProvider) { deviceProfile.setDefaultRuleChainId(idProvider.get(tenantId, DeviceProfile::getDefaultRuleChainId)); deviceProfile.setDefaultDashboardId(idProvider.get(tenantId, DeviceProfile::getDefaultDashboardId)); deviceProfile.setFirmwareId(idProvider.get(tenantId, DeviceProfile::getFirmwareId)); deviceProfile.setSoftwareId(idProvider.get(tenantId, DeviceProfile::getSoftwareId)); - } - - @Override - protected DeviceProfile saveEntity(TenantId tenantId, DeviceProfile deviceProfile, DeviceProfile existingDeviceProfile, DeviceProfileExportData exportData) { return deviceProfileService.saveDeviceProfile(deviceProfile); } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/RuleChainImportService.java index 6be2fc4317..34d346ccc9 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/RuleChainImportService.java @@ -15,6 +15,9 @@ */ package org.thingsboard.server.service.exportimport.importing.impl; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; @@ -26,39 +29,47 @@ import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.exportimport.exporting.data.RuleChainExportData; +import java.util.Collections; +import java.util.Optional; +import java.util.UUID; + @Service @TbCoreComponent @RequiredArgsConstructor -public class RuleChainImportService extends AbstractEntityImportService { +public class RuleChainImportService extends BaseEntityImportService { private final RuleChainService ruleChainService; @Override - protected void setLinkedEntitiesIds(TenantId tenantId, RuleChain ruleChain, IdProvider idProvider) { - } + protected RuleChain prepareAndSave(TenantId tenantId, RuleChain ruleChain, RuleChainExportData exportData, NewIdProvider idProvider) { + RuleChainMetaData metaData = exportData.getMetaData(); + Optional.ofNullable(metaData.getNodes()).orElse(Collections.emptyList()) + .forEach(ruleNode -> { + ruleNode.setId(null); + ruleNode.setRuleChainId(null); + JsonNode ruleNodeConfig = ruleNode.getConfiguration(); + Optional.ofNullable(ruleNodeConfig) + .flatMap(config -> Optional.ofNullable(config.get("ruleChainId")).filter(JsonNode::isTextual)) + .map(JsonNode::asText).map(UUID::fromString) + .ifPresent(otherRuleChainUuid -> { + ((ObjectNode) ruleNodeConfig).set("ruleChainId", new TextNode( + idProvider.get(tenantId, rc -> new RuleChainId(otherRuleChainUuid)).toString() + )); + ruleNode.setConfiguration(ruleNodeConfig); + }); + }); + Optional.ofNullable(metaData.getRuleChainConnections()).orElse(Collections.emptyList()) + .forEach(ruleChainConnectionInfo -> { + ruleChainConnectionInfo.setTargetRuleChainId(idProvider.get(tenantId, rc -> ruleChainConnectionInfo.getTargetRuleChainId())); + }); + ruleChain.setFirstRuleNodeId(null); - @Override - protected RuleChain saveEntity(TenantId tenantId, RuleChain ruleChain, RuleChain existingRuleChain, RuleChainExportData exportData) { - ruleChain.setFirstRuleNodeId(null); // will be set during metadata persisting - if (existingRuleChain != null) { - ruleChainService.deleteRuleNodes(tenantId, existingRuleChain.getId()); + if (ruleChain.getId() != null) { + ruleChainService.deleteRuleNodes(tenantId, ruleChain.getId()); } - ruleChain = ruleChainService.saveRuleChain(ruleChain); - - RuleChainMetaData metaData = exportData.getMetaData(); - metaData.setRuleChainId(ruleChain.getId()); - metaData.getNodes().forEach(ruleNode -> { - ruleNode.setId(null); - ruleNode.setRuleChainId(null); - }); - metaData.getRuleChainConnections().forEach(ruleChainConnectionInfo -> { -// ruleChainConnectionInfo.setTargetRuleChainId(); - // TODO [viacheslav]: check if this thing is needed for "Other Rule Chain Node" - // TODO [viacheslav]: and check import of tenant rule chains - }); - ruleChainService.saveRuleChainMetaData(tenantId, metaData); - + exportData.getMetaData().setRuleChainId(ruleChain.getId()); + ruleChainService.saveRuleChainMetaData(tenantId, exportData.getMetaData()); return ruleChain; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java index f4fa052648..47ce08c4c1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java @@ -238,6 +238,8 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { entityTableMap.put(EntityType.TENANT, "tenant"); entityTableMap.put(EntityType.API_USAGE_STATE, SELECT_API_USAGE_STATE); entityTableMap.put(EntityType.EDGE, "edge"); + entityTableMap.put(EntityType.RULE_CHAIN, "rule_chain"); + entityTableMap.put(EntityType.DEVICE_PROFILE, "device_profile"); } public static EntityType[] RELATION_QUERY_ENTITY_TYPES = new EntityType[]{ diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java index 8df341fb0b..1499b7150f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/EntityKeyMapping.java @@ -102,6 +102,7 @@ public class EntityKeyMapping { allowedEntityFieldMap.put(EntityType.WIDGET_TYPE, new HashSet<>(widgetEntityFields)); allowedEntityFieldMap.put(EntityType.WIDGETS_BUNDLE, new HashSet<>(widgetEntityFields)); allowedEntityFieldMap.put(EntityType.API_USAGE_STATE, apiUsageStateEntityFields); + allowedEntityFieldMap.put(EntityType.DEVICE_PROFILE, Set.of(CREATED_TIME, NAME, TYPE)); entityFieldColumnMap.put(CREATED_TIME, ModelConstants.CREATED_TIME_PROPERTY); entityFieldColumnMap.put(ENTITY_TYPE, ModelConstants.ENTITY_TYPE_PROPERTY); From e37a075031ea450c8777048949815062b23b0687 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Fri, 1 Apr 2022 15:06:50 +0300 Subject: [PATCH 10/39] Export/import API: refactor, rename packages --- .../server/controller/AssetController.java | 4 +-- .../server/controller/BaseController.java | 12 ++++--- .../server/controller/DeviceController.java | 4 +-- .../server/controller/EdgeController.java | 4 +-- .../EntitiesExportImportController.java | 35 ++++++++++--------- .../controller/RuleChainController.java | 1 - .../service/asset/AssetBulkImportService.java | 4 +-- .../device/DeviceBulkImportService.java | 4 +-- .../service/edge/EdgeBulkImportService.java | 4 +-- .../DefaultEntitiesExportImportService.java | 15 ++++---- .../EntitiesExportImportService.java | 10 +++--- .../exporting/EntityExportService.java | 4 +-- .../exporting/EntityExportSettings.java | 2 +- .../exporting}/ExportableEntitiesService.java | 2 +- .../exporting/data/AssetExportData.java | 2 +- .../exporting/data/CustomerExportData.java | 2 +- .../exporting/data/DashboardExportData.java | 2 +- .../exporting/data/DeviceExportData.java | 2 +- .../data/DeviceProfileExportData.java | 2 +- .../exporting/data/EntityExportData.java | 6 ++-- .../exporting/data/RuleChainExportData.java | 2 +- .../exporting/impl/AssetExportService.java | 4 +-- .../impl/BaseEntityExportService.java | 16 ++++----- .../exporting/impl/CustomerExportService.java | 4 +-- .../impl/DashboardExportService.java | 4 +-- .../exporting/impl/DeviceExportService.java | 4 +-- .../impl/DeviceProfileExportService.java | 4 +-- .../impl/RuleChainExportService.java | 4 +-- .../importing/EntityImportResult.java | 2 +- .../importing/EntityImportService.java | 4 +-- .../importing/EntityImportSettings.java | 2 +- .../csv}/AbstractBulkImportService.java | 4 +-- .../importing/csv}/BulkImportColumnType.java | 2 +- .../importing/csv}/BulkImportRequest.java | 2 +- .../importing/csv}/BulkImportResult.java | 2 +- .../importing/csv}/ImportedEntityInfo.java | 2 +- .../importing/impl/AssetImportService.java | 4 +-- .../impl/BaseEntityImportService.java | 16 ++++----- .../importing/impl/CustomerImportService.java | 4 +-- .../impl/DashboardImportService.java | 4 +-- .../importing/impl/DeviceImportService.java | 4 +-- .../impl/DeviceProfileImportService.java | 4 +-- .../impl/RuleChainImportService.java | 6 ++-- .../server/dao/rule/RuleChainService.java | 1 + .../server/common/data/id/IdBased.java | 35 ++++++++++--------- .../server/common/data/rule/RuleChain.java | 4 +-- .../server/dao/ExportableEntityDao.java | 1 - 47 files changed, 136 insertions(+), 130 deletions(-) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/DefaultEntitiesExportImportService.java (91%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/EntitiesExportImportService.java (78%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/exporting/EntityExportService.java (88%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/exporting/EntityExportSettings.java (93%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync/exporting}/ExportableEntitiesService.java (95%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/exporting/data/AssetExportData.java (93%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/exporting/data/CustomerExportData.java (93%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/exporting/data/DashboardExportData.java (93%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/exporting/data/DeviceExportData.java (94%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/exporting/data/DeviceProfileExportData.java (93%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/exporting/data/EntityExportData.java (92%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/exporting/data/RuleChainExportData.java (94%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/exporting/impl/AssetExportService.java (88%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/exporting/impl/BaseEntityExportService.java (79%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/exporting/impl/CustomerExportService.java (88%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/exporting/impl/DashboardExportService.java (88%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/exporting/impl/DeviceExportService.java (91%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/exporting/impl/DeviceProfileExportService.java (88%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/exporting/impl/RuleChainExportService.java (91%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/importing/EntityImportResult.java (93%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/importing/EntityImportService.java (88%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/importing/EntityImportSettings.java (94%) rename application/src/main/java/org/thingsboard/server/service/{importing => sync/importing/csv}/AbstractBulkImportService.java (98%) rename application/src/main/java/org/thingsboard/server/service/{importing => sync/importing/csv}/BulkImportColumnType.java (97%) rename application/src/main/java/org/thingsboard/server/service/{importing => sync/importing/csv}/BulkImportRequest.java (94%) rename application/src/main/java/org/thingsboard/server/service/{importing => sync/importing/csv}/BulkImportResult.java (94%) rename application/src/main/java/org/thingsboard/server/service/{importing => sync/importing/csv}/ImportedEntityInfo.java (92%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/importing/impl/AssetImportService.java (91%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/importing/impl/BaseEntityImportService.java (91%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/importing/impl/CustomerImportService.java (91%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/importing/impl/DashboardImportService.java (95%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/importing/impl/DeviceImportService.java (92%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/importing/impl/DeviceProfileImportService.java (92%) rename application/src/main/java/org/thingsboard/server/service/{exportimport => sync}/importing/impl/RuleChainImportService.java (94%) diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index 94c714e24f..b05e32efe7 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -54,8 +54,8 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.asset.AssetBulkImportService; -import org.thingsboard.server.service.importing.BulkImportRequest; -import org.thingsboard.server.service.importing.BulkImportResult; +import org.thingsboard.server.service.sync.importing.csv.BulkImportRequest; +import org.thingsboard.server.service.sync.importing.csv.BulkImportResult; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 7416444104..382929e1e7 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -924,7 +924,7 @@ public abstract class BaseController { } } - protected & HasTenantId, I extends EntityId> void onEntityUpdatedOrCreated(User user, E savedEntity, E oldEntity, boolean isNewEntity) { + protected & HasTenantId, I extends EntityId> void onEntityUpdatedOrCreated(User user, E savedEntity, E oldEntity, boolean isNewEntity) { boolean notifyEdgeService = false; EntityType entityType = savedEntity.getId().getEntityType(); @@ -951,7 +951,7 @@ public abstract class BaseController { otaPackageStateService.update(deviceProfile, isFirmwareChanged, isSoftwareChanged); notifyEdgeService = true; break; - case RULE_CHAIN: // FIXME: events for rule chain metadata + case RULE_CHAIN: RuleChainType ruleChainType = ((RuleChain) savedEntity).getType(); if (RuleChainType.CORE.equals(ruleChainType)) { tbClusterService.broadcastEntityStateChangeEvent(savedEntity.getTenantId(), savedEntity.getId(), @@ -974,8 +974,12 @@ public abstract class BaseController { throw new UnsupportedOperationException(); } - entityActionService.logEntityAction(user, savedEntity.getId(), savedEntity, savedEntity instanceof HasCustomerId ? ((HasCustomerId) savedEntity).getCustomerId() : null, - isNewEntity ? ActionType.ADDED : ActionType.UPDATED, null); + try { + logEntityAction(user, savedEntity.getId(), savedEntity, savedEntity instanceof HasCustomerId ? ((HasCustomerId) savedEntity).getCustomerId() : null, + isNewEntity ? ActionType.ADDED : ActionType.UPDATED, null); + } catch (ThingsboardException e) { + log.error("Failed to log entity action", e); + } if (notifyEdgeService) { sendEntityNotificationMsg(savedEntity.getTenantId(), savedEntity.getId(), isNewEntity ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); } diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index d2ffafa73e..feb58edb7a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -74,8 +74,8 @@ import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.device.DeviceBulkImportService; import org.thingsboard.server.service.gateway_device.GatewayNotificationsService; -import org.thingsboard.server.service.importing.BulkImportRequest; -import org.thingsboard.server.service.importing.BulkImportResult; +import org.thingsboard.server.service.sync.importing.csv.BulkImportRequest; +import org.thingsboard.server.service.sync.importing.csv.BulkImportResult; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java index f3d73f13e5..8f45427603 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java @@ -55,8 +55,8 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.edge.EdgeBulkImportService; -import org.thingsboard.server.service.importing.BulkImportRequest; -import org.thingsboard.server.service.importing.BulkImportResult; +import org.thingsboard.server.service.sync.importing.csv.BulkImportRequest; +import org.thingsboard.server.service.sync.importing.csv.BulkImportResult; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index 411a5ec00e..f158ff15e7 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -42,12 +42,12 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.exportimport.EntitiesExportImportService; -import org.thingsboard.server.service.exportimport.ExportableEntitiesService; -import org.thingsboard.server.service.exportimport.exporting.EntityExportSettings; -import org.thingsboard.server.service.exportimport.exporting.data.EntityExportData; -import org.thingsboard.server.service.exportimport.importing.EntityImportResult; -import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; +import org.thingsboard.server.service.sync.EntitiesExportImportService; +import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; +import org.thingsboard.server.service.sync.exporting.EntityExportSettings; +import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.importing.EntityImportResult; +import org.thingsboard.server.service.sync.importing.EntityImportSettings; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; @@ -103,12 +103,12 @@ public class EntitiesExportImportController extends BaseController { } } - @PostMapping(value = "/export/{entityType}", params = "ids") - public List>> exportAllEntitiesByEntityType(@PathVariable EntityType entityType, - @RequestParam Map exportSettingsParams, - @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "2147483647") int pageSize, - @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { + @PostMapping(value = "/export/{entityType}") + public List>> exportEntitiesByEntityType(@PathVariable EntityType entityType, + @RequestParam Map exportSettingsParams, + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "2147483647") int pageSize, + @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { TenantId tenantId = getTenantId(); CustomerId customerId = toCustomerId(customerUuid); @@ -236,11 +236,12 @@ public class EntitiesExportImportController extends BaseController { public List>> importEntities(SecurityUser user, List>> exportDataList, EntityImportSettings importSettings) throws ThingsboardException { for (EntityExportData> exportData : exportDataList) { - ExportableEntity existingEntity = exportableEntitiesService.findEntityByExternalId(user.getTenantId(), exportData.getMainEntity().getId()); + ExportableEntity existingEntity = exportableEntitiesService.findEntityByExternalId(user.getTenantId(), exportData.getEntity().getId()); if (existingEntity != null) { - checkEntityId(existingEntity.getId(), Operation.WRITE); + accessControlService.checkPermission(user, Resource.of(exportData.getEntityType()), Operation.WRITE, existingEntity.getId(), existingEntity); } else { - accessControlService.checkPermission(user, Resource.of(exportData.getEntityType()), Operation.CREATE); + exportData.getEntity().setTenantId(user.getTenantId()); + accessControlService.checkPermission(user, Resource.of(exportData.getEntityType()), Operation.CREATE, null, exportData.getEntity()); } List relations = new LinkedList<>(); @@ -252,9 +253,9 @@ public class EntitiesExportImportController extends BaseController { } for (EntityRelation relation : relations) { EntityId otherEntityId = null; - if (!relation.getFrom().equals(exportData.getMainEntity().getId())) { + if (!relation.getFrom().equals(exportData.getEntity().getId())) { otherEntityId = relation.getFrom(); - } else if (!relation.getTo().equals(exportData.getMainEntity().getId())) { + } else if (!relation.getTo().equals(exportData.getEntity().getId())) { otherEntityId = relation.getTo(); } if (otherEntityId != null) { diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index aba074ac60..cb59951ac5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -279,7 +279,6 @@ public class RuleChainController extends BaseController { RuleChain savedRuleChain = installScripts.createDefaultRuleChain(getCurrentUser().getTenantId(), request.getName()); - tbClusterService.broadcastEntityStateChangeEvent(savedRuleChain.getTenantId(), savedRuleChain.getId(), ComponentLifecycleEvent.CREATED); logEntityAction(savedRuleChain.getId(), savedRuleChain, null, ActionType.ADDED, null); diff --git a/application/src/main/java/org/thingsboard/server/service/asset/AssetBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/asset/AssetBulkImportService.java index 4c75f7d45d..cfefcafbf4 100644 --- a/application/src/main/java/org/thingsboard/server/service/asset/AssetBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/asset/AssetBulkImportService.java @@ -25,8 +25,8 @@ import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.importing.AbstractBulkImportService; -import org.thingsboard.server.service.importing.BulkImportColumnType; +import org.thingsboard.server.service.sync.importing.csv.AbstractBulkImportService; +import org.thingsboard.server.service.sync.importing.csv.BulkImportColumnType; import org.thingsboard.server.service.security.model.SecurityUser; import java.util.Map; diff --git a/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java index cd655a2467..3aac9c3641 100644 --- a/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java @@ -49,8 +49,8 @@ import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.importing.AbstractBulkImportService; -import org.thingsboard.server.service.importing.BulkImportColumnType; +import org.thingsboard.server.service.sync.importing.csv.AbstractBulkImportService; +import org.thingsboard.server.service.sync.importing.csv.BulkImportColumnType; import org.thingsboard.server.service.security.model.SecurityUser; import java.util.Collection; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java index 615a7cddd2..b09ac532df 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java @@ -25,8 +25,8 @@ import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.importing.AbstractBulkImportService; -import org.thingsboard.server.service.importing.BulkImportColumnType; +import org.thingsboard.server.service.sync.importing.csv.AbstractBulkImportService; +import org.thingsboard.server.service.sync.importing.csv.BulkImportColumnType; import org.thingsboard.server.service.security.model.SecurityUser; import java.util.Map; diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java similarity index 91% rename from application/src/main/java/org/thingsboard/server/service/exportimport/DefaultEntitiesExportImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java index 8d85355849..f399dbdbea 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport; +package org.thingsboard.server.service.sync; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -24,12 +24,13 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.exportimport.exporting.EntityExportService; -import org.thingsboard.server.service.exportimport.exporting.EntityExportSettings; -import org.thingsboard.server.service.exportimport.exporting.data.EntityExportData; -import org.thingsboard.server.service.exportimport.importing.EntityImportResult; -import org.thingsboard.server.service.exportimport.importing.EntityImportService; -import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; +import org.thingsboard.server.service.sync.exporting.EntityExportService; +import org.thingsboard.server.service.sync.exporting.EntityExportSettings; +import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; +import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.importing.EntityImportResult; +import org.thingsboard.server.service.sync.importing.EntityImportService; +import org.thingsboard.server.service.sync.importing.EntityImportSettings; import java.util.Collection; import java.util.Comparator; diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/EntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java similarity index 78% rename from application/src/main/java/org/thingsboard/server/service/exportimport/EntitiesExportImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java index 682db6ba21..0fbe75a866 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/EntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport; +package org.thingsboard.server.service.sync; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.service.exportimport.exporting.EntityExportSettings; -import org.thingsboard.server.service.exportimport.exporting.data.EntityExportData; -import org.thingsboard.server.service.exportimport.importing.EntityImportResult; -import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; +import org.thingsboard.server.service.sync.exporting.EntityExportSettings; +import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.importing.EntityImportResult; +import org.thingsboard.server.service.sync.importing.EntityImportSettings; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/EntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java similarity index 88% rename from application/src/main/java/org/thingsboard/server/service/exportimport/exporting/EntityExportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java index 929092e90e..6b57cbb557 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/EntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.exporting; +package org.thingsboard.server.service.sync.exporting; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.service.exportimport.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exporting.data.EntityExportData; public interface EntityExportService, D extends EntityExportData> { diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/EntityExportSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportSettings.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/exportimport/exporting/EntityExportSettings.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportSettings.java index f4eea9913a..36552b2c12 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/EntityExportSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportSettings.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.exporting; +package org.thingsboard.server.service.sync.exporting; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/ExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java similarity index 95% rename from application/src/main/java/org/thingsboard/server/service/exportimport/ExportableEntitiesService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java index bb8f30c4e1..2135d2aca1 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/ExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport; +package org.thingsboard.server.service.sync.exporting; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/AssetExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/AssetExportData.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/AssetExportData.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/data/AssetExportData.java index 4c4872760b..4031ec7062 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/AssetExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/AssetExportData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.exporting.data; +package org.thingsboard.server.service.sync.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/CustomerExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/CustomerExportData.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/CustomerExportData.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/data/CustomerExportData.java index 1fc820814e..08888f4a35 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/CustomerExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/CustomerExportData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.exporting.data; +package org.thingsboard.server.service.sync.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DashboardExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DashboardExportData.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DashboardExportData.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DashboardExportData.java index 6ef45fc531..6f101816c9 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DashboardExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DashboardExportData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.exporting.data; +package org.thingsboard.server.service.sync.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DeviceExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceExportData.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DeviceExportData.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceExportData.java index 5f5d8e8f72..977bd22f82 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DeviceExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceExportData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.exporting.data; +package org.thingsboard.server.service.sync.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DeviceProfileExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceProfileExportData.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DeviceProfileExportData.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceProfileExportData.java index def6b7edbe..7b71749be4 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/DeviceProfileExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceProfileExportData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.exporting.data; +package org.thingsboard.server.service.sync.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/EntityExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java similarity index 92% rename from application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/EntityExportData.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java index b9cc8c71a3..51b5615c9e 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/EntityExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.exporting.data; +package org.thingsboard.server.service.sync.exporting.data; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.relation.EntityRelation; import java.util.List; @JsonIgnoreProperties(ignoreUnknown = true) -@JsonTypeInfo(property = "entityType", use = JsonTypeInfo.Id.NAME) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "entityType") @JsonSubTypes({ @Type(name = "DEVICE", value = DeviceExportData.class), @Type(name = "DEVICE_PROFILE", value = DeviceProfileExportData.class), @@ -43,7 +43,7 @@ import java.util.List; @Data public abstract class EntityExportData> { - private E mainEntity; + private E entity; private List inboundRelations; private List outboundRelations; diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/RuleChainExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/RuleChainExportData.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/RuleChainExportData.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/data/RuleChainExportData.java index f92616fead..9f59aa4401 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/data/RuleChainExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/RuleChainExportData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.exporting.data; +package org.thingsboard.server.service.sync.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AssetExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/AssetExportService.java similarity index 88% rename from application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AssetExportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/AssetExportService.java index c22b345299..b4f506a1f5 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/AssetExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/AssetExportService.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.exporting.impl; +package org.thingsboard.server.service.sync.exporting.impl; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.exportimport.exporting.data.AssetExportData; +import org.thingsboard.server.service.sync.exporting.data.AssetExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/BaseEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java similarity index 79% rename from application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/BaseEntityExportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java index 68c78e87c3..a3a83196bb 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/BaseEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.exporting.impl; +package org.thingsboard.server.service.sync.exporting.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; @@ -23,10 +23,10 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.service.exportimport.ExportableEntitiesService; -import org.thingsboard.server.service.exportimport.exporting.EntityExportService; -import org.thingsboard.server.service.exportimport.exporting.EntityExportSettings; -import org.thingsboard.server.service.exportimport.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; +import org.thingsboard.server.service.sync.exporting.EntityExportService; +import org.thingsboard.server.service.sync.exporting.EntityExportSettings; +import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import java.util.List; @@ -41,9 +41,9 @@ public abstract class BaseEntityExportService inboundRelations = relationService.findByTo(tenantId, entityId, RelationTypeGroup.COMMON); diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/CustomerExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/CustomerExportService.java similarity index 88% rename from application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/CustomerExportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/CustomerExportService.java index 1f23e8e7d2..c90d21e573 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/CustomerExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/CustomerExportService.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.exporting.impl; +package org.thingsboard.server.service.sync.exporting.impl; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.exportimport.exporting.data.CustomerExportData; +import org.thingsboard.server.service.sync.exporting.data.CustomerExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DashboardExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DashboardExportService.java similarity index 88% rename from application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DashboardExportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DashboardExportService.java index 17d593157b..32584ca917 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DashboardExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DashboardExportService.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.exporting.impl; +package org.thingsboard.server.service.sync.exporting.impl; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.exportimport.exporting.data.DashboardExportData; +import org.thingsboard.server.service.sync.exporting.data.DashboardExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceExportService.java similarity index 91% rename from application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceExportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceExportService.java index b8c7e65b34..d31a859013 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceExportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.exporting.impl; +package org.thingsboard.server.service.sync.exporting.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.exportimport.exporting.data.DeviceExportData; +import org.thingsboard.server.service.sync.exporting.data.DeviceExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceProfileExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceProfileExportService.java similarity index 88% rename from application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceProfileExportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceProfileExportService.java index 24b5f8891e..b3fd65363b 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/DeviceProfileExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceProfileExportService.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.exporting.impl; +package org.thingsboard.server.service.sync.exporting.impl; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.exportimport.exporting.data.DeviceProfileExportData; +import org.thingsboard.server.service.sync.exporting.data.DeviceProfileExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/RuleChainExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/RuleChainExportService.java similarity index 91% rename from application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/RuleChainExportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/RuleChainExportService.java index f1b9a27f23..846d4006d0 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/exporting/impl/RuleChainExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/RuleChainExportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.exporting.impl; +package org.thingsboard.server.service.sync.exporting.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.exportimport.exporting.data.RuleChainExportData; +import org.thingsboard.server.service.sync.exporting.data.RuleChainExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportResult.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportResult.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportResult.java rename to application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportResult.java index cc44670757..a79bfa2478 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportResult.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportResult.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.importing; +package org.thingsboard.server.service.sync.importing; import lombok.Data; import org.thingsboard.server.common.data.ExportableEntity; diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java similarity index 88% rename from application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java index 8697b04f59..19947e8917 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.importing; +package org.thingsboard.server.service.sync.importing; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.service.exportimport.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exporting.data.EntityExportData; public interface EntityImportService, D extends EntityExportData> { diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportSettings.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportSettings.java rename to application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportSettings.java index c3cf0b531d..9e4afcc560 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/EntityImportSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportSettings.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.importing; +package org.thingsboard.server.service.sync.importing; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/application/src/main/java/org/thingsboard/server/service/importing/AbstractBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/AbstractBulkImportService.java similarity index 98% rename from application/src/main/java/org/thingsboard/server/service/importing/AbstractBulkImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/importing/csv/AbstractBulkImportService.java index b7b740f190..06cd7a93be 100644 --- a/application/src/main/java/org/thingsboard/server/service/importing/AbstractBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/AbstractBulkImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.importing; +package org.thingsboard.server.service.sync.importing.csv; import com.google.common.util.concurrent.FutureCallback; import com.google.gson.JsonObject; @@ -44,7 +44,7 @@ import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.controller.BaseController; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.service.action.EntityActionService; -import org.thingsboard.server.service.importing.BulkImportRequest.ColumnMapping; +import org.thingsboard.server.service.sync.importing.csv.BulkImportRequest.ColumnMapping; import org.thingsboard.server.service.security.AccessValidator; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.AccessControlService; diff --git a/application/src/main/java/org/thingsboard/server/service/importing/BulkImportColumnType.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportColumnType.java similarity index 97% rename from application/src/main/java/org/thingsboard/server/service/importing/BulkImportColumnType.java rename to application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportColumnType.java index 96075eb4c8..beafe5e95f 100644 --- a/application/src/main/java/org/thingsboard/server/service/importing/BulkImportColumnType.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportColumnType.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.importing; +package org.thingsboard.server.service.sync.importing.csv; import lombok.Getter; import org.thingsboard.server.common.data.DataConstants; diff --git a/application/src/main/java/org/thingsboard/server/service/importing/BulkImportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportRequest.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/importing/BulkImportRequest.java rename to application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportRequest.java index 9f8195ca45..f600289ab6 100644 --- a/application/src/main/java/org/thingsboard/server/service/importing/BulkImportRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportRequest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.importing; +package org.thingsboard.server.service.sync.importing.csv; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/importing/BulkImportResult.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportResult.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/importing/BulkImportResult.java rename to application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportResult.java index 651aedeb0b..da12a6baeb 100644 --- a/application/src/main/java/org/thingsboard/server/service/importing/BulkImportResult.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportResult.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.importing; +package org.thingsboard.server.service.sync.importing.csv; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/importing/ImportedEntityInfo.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/ImportedEntityInfo.java similarity index 92% rename from application/src/main/java/org/thingsboard/server/service/importing/ImportedEntityInfo.java rename to application/src/main/java/org/thingsboard/server/service/sync/importing/csv/ImportedEntityInfo.java index 45c2551be2..68e7a49b18 100644 --- a/application/src/main/java/org/thingsboard/server/service/importing/ImportedEntityInfo.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/ImportedEntityInfo.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.importing; +package org.thingsboard.server.service.sync.importing.csv; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java similarity index 91% rename from application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AssetImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java index 04e5a5eeca..b8328a43c3 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/AssetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.importing.impl; +package org.thingsboard.server.service.sync.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.exportimport.exporting.data.AssetExportData; +import org.thingsboard.server.service.sync.exporting.data.AssetExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java similarity index 91% rename from application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/BaseEntityImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java index 214e314ffd..608ffe2acf 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.importing.impl; +package org.thingsboard.server.service.sync.importing.impl; import lombok.RequiredArgsConstructor; import org.apache.commons.collections.CollectionUtils; @@ -28,11 +28,11 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.service.exportimport.ExportableEntitiesService; -import org.thingsboard.server.service.exportimport.exporting.data.EntityExportData; -import org.thingsboard.server.service.exportimport.importing.EntityImportResult; -import org.thingsboard.server.service.exportimport.importing.EntityImportService; -import org.thingsboard.server.service.exportimport.importing.EntityImportSettings; +import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; +import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.importing.EntityImportResult; +import org.thingsboard.server.service.sync.importing.EntityImportService; +import org.thingsboard.server.service.sync.importing.EntityImportSettings; import java.util.Collections; import java.util.LinkedList; @@ -53,7 +53,7 @@ public abstract class BaseEntityImportService importEntity(TenantId tenantId, D exportData, EntityImportSettings importSettings) { - E entity = exportData.getMainEntity(); + E entity = exportData.getEntity(); E existingEntity = exportableEntitiesService.findEntityByExternalId(tenantId, entity.getId()); entity.setExternalId(entity.getId()); @@ -118,7 +118,7 @@ public abstract class BaseEntityImportService entity = exportableEntitiesService.findEntityByExternalId(tenantId, externalId); if (entity == null) { - throw new IllegalStateException("Cannot find " + externalId.getEntityType() + " by external id " + externalId); + throw new IllegalArgumentException("Cannot find " + externalId.getEntityType() + " by external id " + externalId); } return entity.getId(); } diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/CustomerImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java similarity index 91% rename from application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/CustomerImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java index 7b5b3e3248..5ad7161486 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/CustomerImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.importing.impl; +package org.thingsboard.server.service.sync.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.exportimport.exporting.data.CustomerExportData; +import org.thingsboard.server.service.sync.exporting.data.CustomerExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java similarity index 95% rename from application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DashboardImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java index 2bf9a7926a..9e7cffd272 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.importing.impl; +package org.thingsboard.server.service.sync.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -25,7 +25,7 @@ import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.exportimport.exporting.data.DashboardExportData; +import org.thingsboard.server.service.sync.exporting.data.DashboardExportData; import java.util.HashSet; import java.util.Set; diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java similarity index 92% rename from application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java index ee6d89b774..eafe9c0fdb 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.importing.impl; +package org.thingsboard.server.service.sync.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.exportimport.exporting.data.DeviceExportData; +import org.thingsboard.server.service.sync.exporting.data.DeviceExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceProfileImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java similarity index 92% rename from application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceProfileImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java index 5d05bd06eb..61b1ea16dc 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/DeviceProfileImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.importing.impl; +package org.thingsboard.server.service.sync.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.exportimport.exporting.data.DeviceProfileExportData; +import org.thingsboard.server.service.sync.exporting.data.DeviceProfileExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/RuleChainImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java index 34d346ccc9..5dfc31e97a 100644 --- a/application/src/main/java/org/thingsboard/server/service/exportimport/importing/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.exportimport.importing.impl; +package org.thingsboard.server.service.sync.importing.impl; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -27,7 +27,7 @@ import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.exportimport.exporting.data.RuleChainExportData; +import org.thingsboard.server.service.sync.exporting.data.RuleChainExportData; import java.util.Collections; import java.util.Optional; @@ -70,7 +70,7 @@ public class RuleChainImportService extends BaseEntityImportService implements HasId { protected I id; - - public IdBased() { - super(); - } - - public IdBased(I id) { - super(); - this.id = id; - } + + public IdBased() { + super(); + } + + public IdBased(I id) { + super(); + this.id = id; + } @JsonSetter - public void setId(I id) { - this.id = id; - } + public void setId(I id) { + this.id = id; + } - public I getId() { - return id; - } + public I getId() { + return id; + } - @JsonIgnore - public UUID getUuidId() { + @JsonIgnore + public UUID getUuidId() { if (id != null) { return id.getId(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java index 74186b49bf..eca9e331cd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/rule/RuleChain.java @@ -57,11 +57,11 @@ public class RuleChain extends SearchTextBasedWithAdditionalInfo im @ApiModelProperty(position = 9, value = "Reserved for future usage. The actual list of rule nodes and their relations is stored in the database separately.") private transient JsonNode configuration; + private RuleChainId externalId; + @JsonIgnore private byte[] configurationBytes; - private RuleChainId externalId; - public RuleChain() { super(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java index 2fb301c092..5b2b5a2366 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java @@ -25,7 +25,6 @@ public interface ExportableEntityDao { T findByTenantIdAndId(UUID tenantId, UUID id); - EntityType getEntityType(); } From 4dcd037a61eeadf576a59d3e8275596b3699a612 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Fri, 1 Apr 2022 15:24:22 +0300 Subject: [PATCH 11/39] Initial tests for export/import --- .../server/controller/AbstractWebTest.java | 1 - ...aseEntitiesExportImportControllerTest.java | 166 +++++++++++++ ...EntitiesExportImportControllerSqlTest.java | 228 ++++++++++++++++++ 3 files changed, 394 insertions(+), 1 deletion(-) create mode 100644 application/src/test/java/org/thingsboard/server/controller/BaseEntitiesExportImportControllerTest.java create mode 100644 application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index cc561e31a8..04a2c5f351 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -605,7 +605,6 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { protected T readResponse(ResultActions result, TypeReference type) throws Exception { byte[] content = result.andReturn().getResponse().getContentAsByteArray(); - ObjectMapper mapper = new ObjectMapper(); return mapper.readerFor(type).readValue(content); } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEntitiesExportImportControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEntitiesExportImportControllerTest.java new file mode 100644 index 0000000000..cf88cf3d0d --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEntitiesExportImportControllerTest.java @@ -0,0 +1,166 @@ +/** + * 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.controller; + +import com.fasterxml.jackson.databind.node.TextNode; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.rule.engine.metadata.TbGetAttributesNodeConfiguration; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.DeviceProfileType; +import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration; +import org.thingsboard.server.common.data.device.data.DeviceData; +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.device.profile.DeviceProfileData; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.rule.RuleChainType; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.dao.asset.AssetService; +import org.thingsboard.server.dao.customer.CustomerService; +import org.thingsboard.server.dao.dashboard.DashboardService; +import org.thingsboard.server.dao.device.DeviceProfileService; +import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.rule.RuleChainService; + +import java.util.Arrays; +import java.util.Collections; + +public abstract class BaseEntitiesExportImportControllerTest extends AbstractControllerTest { + + @Autowired + protected DeviceService deviceService; + @Autowired + protected DeviceProfileService deviceProfileService; + @Autowired + protected AssetService assetService; + @Autowired + protected CustomerService customerService; + @Autowired + protected RuleChainService ruleChainService; + @Autowired + protected DashboardService dashboardService; + + protected Device createDevice(TenantId tenantId, CustomerId customerId, DeviceProfileId deviceProfileId, String name) { + Device device = new Device(); + device.setTenantId(tenantId); + device.setCustomerId(customerId); + device.setName(name); + device.setLabel("lbl"); + device.setDeviceProfileId(deviceProfileId); + DeviceData deviceData = new DeviceData(); + deviceData.setTransportConfiguration(new DefaultDeviceTransportConfiguration()); + device.setDeviceData(deviceData); + return deviceService.saveDevice(device); + } + + protected DeviceProfile createDeviceProfile(TenantId tenantId, String name) { + DeviceProfile deviceProfile = new DeviceProfile(); + deviceProfile.setTenantId(tenantId); + deviceProfile.setName(name); + deviceProfile.setDescription("dscrptn"); + deviceProfile.setType(DeviceProfileType.DEFAULT); + deviceProfile.setTransportType(DeviceTransportType.DEFAULT); + DeviceProfileData profileData = new DeviceProfileData(); + profileData.setConfiguration(new DefaultDeviceProfileConfiguration()); + profileData.setTransportConfiguration(new DefaultDeviceProfileTransportConfiguration()); + deviceProfile.setProfileData(profileData); + return deviceProfileService.saveDeviceProfile(deviceProfile); + } + + protected Asset createAsset(TenantId tenantId, CustomerId customerId, String type, String name) { + Asset asset = new Asset(); + asset.setTenantId(tenantId); + asset.setCustomerId(customerId); + asset.setType(type); + asset.setName(name); + asset.setLabel("lbl"); + asset.setAdditionalInfo(JacksonUtil.newObjectNode().set("a", new TextNode("b"))); + return assetService.saveAsset(asset); + } + + protected Customer createCustomer(TenantId tenantId, String name) { + Customer customer = new Customer(); + customer.setTenantId(tenantId); + customer.setTitle(name); + customer.setCountry("ua"); + customer.setAddress("abb"); + customer.setEmail("ccc@aa.org"); + customer.setAdditionalInfo(JacksonUtil.newObjectNode().set("a", new TextNode("b"))); + return customerService.saveCustomer(customer); + } + + protected Dashboard createDashboard(TenantId tenantId, CustomerId customerId, String name) { + Dashboard dashboard = new Dashboard(); + dashboard.setTenantId(tenantId); + dashboard.setTitle(name); + dashboard.setConfiguration(JacksonUtil.newObjectNode().set("a", new TextNode("b"))); + dashboard.setImage("abvregewrg"); + dashboard.setMobileHide(true); + dashboard = dashboardService.saveDashboard(dashboard); + if (customerId != null) { + dashboardService.assignDashboardToCustomer(tenantId, dashboard.getId(), customerId); + return dashboardService.findDashboardById(tenantId, dashboard.getId()); + } + return dashboard; + } + + protected RuleChain createRuleChain(TenantId tenantId, String name) { + RuleChain ruleChain = new RuleChain(); + ruleChain.setTenantId(tenantId); + ruleChain.setName(name); + ruleChain.setType(RuleChainType.CORE); + ruleChain.setDebugMode(true); + ruleChain.setConfiguration(JacksonUtil.newObjectNode().set("a", new TextNode("b"))); + ruleChain = ruleChainService.saveRuleChain(ruleChain); + + RuleChainMetaData metaData = new RuleChainMetaData(); + metaData.setRuleChainId(ruleChain.getId()); + + RuleNode ruleNode1 = new RuleNode(); + ruleNode1.setName("Simple Rule Node 1"); + ruleNode1.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode1.setDebugMode(true); + TbGetAttributesNodeConfiguration configuration1 = new TbGetAttributesNodeConfiguration(); + configuration1.setServerAttributeNames(Collections.singletonList("serverAttributeKey1")); + ruleNode1.setConfiguration(mapper.valueToTree(configuration1)); + + RuleNode ruleNode2 = new RuleNode(); + ruleNode2.setName("Simple Rule Node 2"); + ruleNode2.setType(org.thingsboard.rule.engine.metadata.TbGetAttributesNode.class.getName()); + ruleNode2.setDebugMode(true); + TbGetAttributesNodeConfiguration configuration2 = new TbGetAttributesNodeConfiguration(); + configuration2.setServerAttributeNames(Collections.singletonList("serverAttributeKey2")); + ruleNode2.setConfiguration(mapper.valueToTree(configuration2)); + + metaData.setNodes(Arrays.asList(ruleNode1, ruleNode2)); + metaData.setFirstNodeIndex(0); + metaData.addConnectionInfo(0, 1, "Success"); + ruleChainService.saveRuleChainMetaData(tenantId, metaData); + + return ruleChainService.findRuleChainById(tenantId, ruleChain.getId()); + } + +} diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java new file mode 100644 index 0000000000..d986f5f9ad --- /dev/null +++ b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java @@ -0,0 +1,228 @@ +/** + * 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.controller.sql; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JavaType; +import lombok.SneakyThrows; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.web.servlet.ResultActions; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.security.DeviceCredentials; +import org.thingsboard.server.controller.BaseEntitiesExportImportControllerTest; +import org.thingsboard.server.dao.device.DeviceCredentialsService; +import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; +import org.thingsboard.server.service.sync.exporting.data.DeviceExportData; +import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exporting.data.RuleChainExportData; +import org.thingsboard.server.service.sync.importing.EntityImportResult; + +import java.util.List; +import java.util.function.Consumer; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@DaoSqlTest +public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImportControllerTest { + + @Autowired + private TenantService tenantService; + @Autowired + private DeviceCredentialsService deviceCredentialsService; + @Autowired + private ExportableEntitiesService exportableEntitiesService; + + private TenantId tenantId1; + private User tenantAdmin1; + + private TenantId tenantId2; + private User tenantAdmin2; + + @Before + public void beforeEach() throws Exception { + loginSysAdmin(); + Tenant tenant1 = new Tenant(); + tenant1.setTitle("Tenant 1"); + tenant1.setEmail("tenant1@thingsboard.org"); + this.tenantId1 = tenantService.saveTenant(tenant1).getId(); + User tenantAdmin1 = new User(); + tenantAdmin1.setTenantId(tenantId1); + tenantAdmin1.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin1.setEmail("tenant1-admin@thingsboard.org"); + this.tenantAdmin1 = createUser(tenantAdmin1, "12345678"); + Tenant tenant2 = new Tenant(); + tenant2.setTitle("Tenant 2"); + tenant2.setEmail("tenant2@thingsboard.org"); + this.tenantId2 = tenantService.saveTenant(tenant2).getId(); + User tenantAdmin2 = new User(); + tenantAdmin2.setTenantId(tenantId2); + tenantAdmin2.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin2.setEmail("tenant2-admin@thingsboard.org"); + this.tenantAdmin2 = createUser(tenantAdmin2, "12345678"); + } + + @After + public void afterEach() { + tenantService.deleteTenant(tenantId1); + tenantService.deleteTenant(tenantId2); + } + + @Test + public void testExportImport_singleEntityOneByOne_betweenTenants() throws Exception { + Asset asset = createAsset(tenantId1, null, "AB", "Asset of tenant 1"); + testExportImportBetweenTenants(asset, importedAsset -> { + assertThat(importedAsset.getName()).isEqualTo(asset.getName()); + assertThat(importedAsset.getType()).isEqualTo(asset.getType()); + assertThat(importedAsset.getLabel()).isEqualTo(asset.getLabel()); + assertThat(importedAsset.getAdditionalInfo()).isEqualTo(asset.getAdditionalInfo()); + }); + + Customer customer = createCustomer(tenantId1, "Customer of tenant 1"); + testExportImportBetweenTenants(customer, importedCustomer -> { + assertThat(importedCustomer.getTitle()).isEqualTo(customer.getTitle()); + assertThat(importedCustomer.getCountry()).isEqualTo(customer.getCountry()); + assertThat(importedCustomer.getAddress()).isEqualTo(customer.getAddress()); + assertThat(importedCustomer.getEmail()).isEqualTo(customer.getEmail()); + }); + + DeviceProfile deviceProfile = createDeviceProfile(tenantId1, "Device profile of tenant 1"); + DeviceProfileId importedDeviceProfileId = testExportImportBetweenTenants(deviceProfile, importedDeviceProfile -> { + assertThat(importedDeviceProfile.getName()).isEqualTo(deviceProfile.getName()); + assertThat(importedDeviceProfile.getType()).isEqualTo(deviceProfile.getType()); + assertThat(importedDeviceProfile.getTransportType()).isEqualTo(deviceProfile.getTransportType()); + assertThat(importedDeviceProfile.getProfileData()).isEqualTo(deviceProfile.getProfileData()); + assertThat(importedDeviceProfile.getDescription()).isEqualTo(deviceProfile.getDescription()); + }).getSavedEntity().getId(); + + Device device = createDevice(tenantId1, null, deviceProfile.getId(), "Device of tenant 1"); + DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId1, device.getId()); + this.testExportImportBetweenTenants(device, deviceExportData -> { + assertThat(deviceExportData.getCredentials()).isEqualTo(credentials); + }, importedDevice -> { + assertThat(importedDevice.getName()).isEqualTo(device.getName()); + assertThat(importedDevice.getType()).isEqualTo(device.getType()); + assertThat(importedDevice.getDeviceData()).isEqualTo(device.getDeviceData()); + assertThat(importedDevice.getDeviceProfileId()).isEqualTo(importedDeviceProfileId); + assertThat(importedDevice.getLabel()).isEqualTo(device.getLabel()); + + DeviceCredentials importedCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId2, importedDevice.getId()); + assertThat(importedCredentials.getCredentialsId()).isEqualTo(credentials.getCredentialsId()); + assertThat(importedCredentials.getCredentialsValue()).isEqualTo(credentials.getCredentialsValue()); + assertThat(importedCredentials.getCredentialsType()).isEqualTo(credentials.getCredentialsType()); + }); + + RuleChain ruleChain = createRuleChain(tenantId1, "Rule chain of tenant 1"); + RuleChainMetaData metaData = ruleChainService.loadRuleChainMetaData(tenantId1, ruleChain.getId()); + this.testExportImportBetweenTenants(ruleChain, ruleChainExportData -> { + assertThat(ruleChainExportData.getMetaData()).isEqualTo(metaData); + }, importedRuleChain -> { + assertThat(importedRuleChain.getType()).isEqualTo(ruleChain.getType()); + assertThat(importedRuleChain.getName()).isEqualTo(ruleChain.getName()); + assertThat(importedRuleChain.isDebugMode()).isEqualTo(ruleChain.isDebugMode()); + assertThat(importedRuleChain.getConfiguration()).isEqualTo(ruleChain.getConfiguration()); + RuleChainMetaData importedMetaData = ruleChainService.loadRuleChainMetaData(tenantId2, importedRuleChain.getId()); + assertThat(importedMetaData.getConnections()).isEqualTo(metaData.getConnections()); + assertThat(importedMetaData.getFirstNodeIndex()).isEqualTo(metaData.getFirstNodeIndex()); + for (int i = 0; i < metaData.getNodes().size(); i++) { + RuleNode initialNode = metaData.getNodes().get(i); + RuleNode importedNode = importedMetaData.getNodes().get(i); + assertThat(importedNode.getName()).isEqualTo(initialNode.getName()); + assertThat(importedNode.getType()).isEqualTo(initialNode.getType()); + assertThat(importedNode.getConfiguration()).isEqualTo(initialNode.getConfiguration()); + assertThat(importedNode.getAdditionalInfo()).isEqualTo(initialNode.getAdditionalInfo()); + } + }); + + Dashboard dashboard = createDashboard(tenantId1, null, "Dashboard of tenant 1"); + testExportImportBetweenTenants(dashboard, importedDashboard -> { + assertThat(importedDashboard.getTitle()).isEqualTo(dashboard.getTitle()); + assertThat(importedDashboard.getConfiguration()).isEqualTo(dashboard.getConfiguration()); + assertThat(importedDashboard.getImage()).isEqualTo(dashboard.getImage()); + assertThat(importedDashboard.isMobileHide()).isEqualTo(dashboard.isMobileHide()); + }); + } + + private , D extends EntityExportData> EntityImportResult testExportImportBetweenTenants(E entity, Consumer exportDataRequirements, Consumer importedEntityRequirements) throws Exception { + logInAsTenantAdmin1(); + D exportData = readResponse(doPost("/api/entities/export/" + entity.getId().getEntityType() + "/" + entity.getId()) + .andExpect(status().isOk()), new TypeReference() {}); + assertThat(exportData.getEntity()).isEqualTo(entity); + exportDataRequirements.accept(exportData); + + logInAsTenantAdmin2(); + EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + + E importedEntity = (E) exportableEntitiesService.findEntityById(tenantId2, importResult.getSavedEntity().getId()); + assertThat(importedEntity).isNotNull(); + assertThat(importResult.getSavedEntity()).isEqualTo(importedEntity); + assertThat(importResult.getOldEntity()).isNull(); + + assertThat(importedEntity.getId()).isNotEqualTo(entity.getId()); + assertThat(importedEntity.getExternalId()).isEqualTo(entity.getId()); + assertThat(importedEntity.getTenantId()).isEqualTo(tenantId2); + importedEntityRequirements.accept(importedEntity); + return importResult; + } + + private > EntityImportResult testExportImportBetweenTenants(E entity, Consumer importedEntityRequirements) throws Exception { + return testExportImportBetweenTenants(entity, exportData -> {}, importedEntityRequirements); + } + + + @SneakyThrows + private > List> importEntities(List> exportDataList) { + String requestJson = mapper.writerFor(new TypeReference>>() {}).writeValueAsString(exportDataList); + ResultActions resultActions = doPost("/api/entities/import", (Object) requestJson); + + try { + String responseJson = resultActions.andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); + JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, + mapper.getTypeFactory().constructParametricType(EntityImportResult.class, exportDataList.get(0).getEntity().getClass())); + return mapper.readValue(responseJson, type); + } catch (AssertionError e) { + throw new AssertionError(readResponse(resultActions, String.class), e); + } + } + + private void logInAsTenantAdmin1() throws Exception { + login(tenantAdmin1.getEmail(), "12345678"); + } + + private void logInAsTenantAdmin2() throws Exception { + login(tenantAdmin2.getEmail(), "12345678"); + } + +} From 2a77c90d13871ac091e9840b1ba91557727e47ca Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Mon, 4 Apr 2022 15:09:46 +0300 Subject: [PATCH 12/39] More tests for export/import api; refactoring --- .../DefaultEntitiesExportImportService.java | 29 +- .../exporting/ExportableEntitiesService.java | 6 +- .../sync/exporting/TenantEntitiesService.java | 27 ++ .../impl/DashboardImportService.java | 6 +- .../importing/impl/DeviceImportService.java | 8 +- ...EntitiesExportImportControllerSqlTest.java | 350 +++++++++++++----- .../server/dao/ExportableEntityDao.java | 8 +- .../dao/ExportableEntityRepository.java | 4 +- .../server/dao/TenantEntityDao.java | 11 +- .../server/dao/TenantEntityRepository.java | 26 ++ .../server/dao/asset/AssetDao.java | 2 +- .../server/dao/customer/CustomerDao.java | 2 +- .../server/dao/dashboard/DashboardDao.java | 2 +- .../server/dao/device/DeviceDao.java | 2 +- .../server/dao/ota/OtaPackageDao.java | 2 +- .../server/dao/rule/RuleChainDao.java | 2 +- .../dao/sql/customer/CustomerRepository.java | 1 - .../dao/sql/device/JpaDeviceProfileDao.java | 5 + .../server/dao/sql/ota/JpaOtaPackageDao.java | 18 + .../dao/sql/ota/OtaPackageRepository.java | 3 +- .../dao/sql/rule/RuleChainRepository.java | 2 - .../server/dao/sql/user/JpaUserDao.java | 13 + .../server/dao/sql/user/UserRepository.java | 5 +- .../thingsboard/server/dao/user/UserDao.java | 2 +- 24 files changed, 411 insertions(+), 125 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/TenantEntitiesService.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/TenantEntityRepository.java diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java index f399dbdbea..eeaef01f63 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java @@ -20,9 +20,12 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.ExportableEntityDao; +import org.thingsboard.server.dao.TenantEntityDao; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.sync.exporting.EntityExportService; import org.thingsboard.server.service.sync.exporting.EntityExportSettings; @@ -37,7 +40,6 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.stream.Collectors; @Service @@ -46,7 +48,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS private final Map> exportServices = new HashMap<>(); private final Map> importServices = new HashMap<>(); - private final Map> daos = new HashMap<>(); + private final Map> daos = new HashMap<>(); protected static final List SUPPORTED_ENTITY_TYPES = List.of( EntityType.CUSTOMER, EntityType.ASSET, EntityType.RULE_CHAIN, @@ -85,16 +87,22 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS @Override - public , I extends EntityId> E findEntityById(TenantId tenantId, I id) { - ExportableEntityDao dao = getDao(id.getEntityType()); + public & HasTenantId, I extends EntityId> E findEntityById(TenantId tenantId, I id) { + TenantEntityDao dao = (TenantEntityDao) getDao(id.getEntityType()); return dao.findByTenantIdAndId(tenantId.getId(), id.getId()); } @Override public , I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId) { - ExportableEntityDao dao = getDao(externalId.getEntityType()); - return Optional.ofNullable(dao.findByTenantIdAndExternalId(tenantId.getId(), externalId.getId())) - .orElseGet(() -> findEntityById(tenantId, externalId)); + EntityType entityType = externalId.getEntityType(); + if (SUPPORTED_ENTITY_TYPES.contains(entityType)) { + ExportableEntityDao dao = (ExportableEntityDao) getDao(entityType); + E entity = dao.findByTenantIdAndExternalId(tenantId.getId(), externalId.getId()); + if (entity != null) { + return entity; + } + } + return findEntityById(tenantId, externalId); } @@ -114,15 +122,14 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS return (EntityImportService) importServices.get(entityType); } - @SuppressWarnings("unchecked") - private ExportableEntityDao getDao(EntityType entityType) { - return (ExportableEntityDao) daos.get(entityType); + private TenantEntityDao getDao(EntityType entityType) { + return daos.get(entityType); } @Autowired private void setServices(Collection> exportServices, Collection> importServices, - Collection> daos) { + Collection> daos) { exportServices.forEach(entityExportService -> { this.exportServices.put(entityExportService.getEntityType(), entityExportService); }); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java index 2135d2aca1..1c1cf6bf7f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java @@ -16,12 +16,12 @@ package org.thingsboard.server.service.sync.exporting; import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; -public interface ExportableEntitiesService { - - , I extends EntityId> E findEntityById(TenantId tenantId, I id); +public interface ExportableEntitiesService extends TenantEntitiesService { , I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/TenantEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/TenantEntitiesService.java new file mode 100644 index 0000000000..e3e25c21e8 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/TenantEntitiesService.java @@ -0,0 +1,27 @@ +/** + * 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.sync.exporting; + +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.TenantId; + +public interface TenantEntitiesService { + + & HasTenantId, I extends EntityId> E findEntityById(TenantId tenantId, I id); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java index 9e7cffd272..5525ba8e6b 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java @@ -27,7 +27,9 @@ import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.sync.exporting.data.DashboardExportData; +import java.util.Collections; import java.util.HashSet; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -49,8 +51,8 @@ public class DashboardImportService extends BaseEntityImportService existingAssignedCustomers = dashboardService.findDashboardById(tenantId, dashboard.getId()).getAssignedCustomers().stream() - .map(ShortCustomerInfo::getCustomerId).collect(Collectors.toSet()); + Set existingAssignedCustomers = Optional.ofNullable(dashboardService.findDashboardById(tenantId, dashboard.getId()).getAssignedCustomers()) + .orElse(Collections.emptySet()).stream().map(ShortCustomerInfo::getCustomerId).collect(Collectors.toSet()); Set newAssignedCustomers = idProvider.get(tenantId, Dashboard::getAssignedCustomers, ShortCustomerInfo::getCustomerId, ShortCustomerInfo::setCustomerId).stream() .map(ShortCustomerInfo::getCustomerId).collect(Collectors.toSet()); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java index eafe9c0fdb..2dcf4f7e7f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java @@ -38,7 +38,13 @@ public class DeviceImportService extends BaseEntityImportService { - assertThat(importedAsset.getName()).isEqualTo(asset.getName()); - assertThat(importedAsset.getType()).isEqualTo(asset.getType()); - assertThat(importedAsset.getLabel()).isEqualTo(asset.getLabel()); - assertThat(importedAsset.getAdditionalInfo()).isEqualTo(asset.getAdditionalInfo()); - }); - - Customer customer = createCustomer(tenantId1, "Customer of tenant 1"); - testExportImportBetweenTenants(customer, importedCustomer -> { - assertThat(importedCustomer.getTitle()).isEqualTo(customer.getTitle()); - assertThat(importedCustomer.getCountry()).isEqualTo(customer.getCountry()); - assertThat(importedCustomer.getAddress()).isEqualTo(customer.getAddress()); - assertThat(importedCustomer.getEmail()).isEqualTo(customer.getEmail()); - }); + EntityExportData exportData = exportSingleEntity(asset.getId()); + assertThat(exportData.getEntity()).isEqualTo(asset); - DeviceProfile deviceProfile = createDeviceProfile(tenantId1, "Device profile of tenant 1"); - DeviceProfileId importedDeviceProfileId = testExportImportBetweenTenants(deviceProfile, importedDeviceProfile -> { - assertThat(importedDeviceProfile.getName()).isEqualTo(deviceProfile.getName()); - assertThat(importedDeviceProfile.getType()).isEqualTo(deviceProfile.getType()); - assertThat(importedDeviceProfile.getTransportType()).isEqualTo(deviceProfile.getTransportType()); - assertThat(importedDeviceProfile.getProfileData()).isEqualTo(deviceProfile.getProfileData()); - assertThat(importedDeviceProfile.getDescription()).isEqualTo(deviceProfile.getDescription()); - }).getSavedEntity().getId(); + logInAsTenantAdmin2(); + EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + + checkImportedEntity(tenantId1, asset, tenantId2, importResult); + checkImportedAssetData(asset, importResult.getSavedEntity()); + } + + @Test + public void testExportImportSingleAsset_sameTenant() throws Exception { + logInAsTenantAdmin1(); + Asset asset = createAsset(tenantId1, null, "AB", "Asset v1.0"); + EntityExportData exportData = exportSingleEntity(asset.getId()); + + EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + + checkImportedEntity(tenantId1, asset, tenantId1, importResult); + checkImportedAssetData(asset, importResult.getSavedEntity()); + } + + @Test + public void testExportImportAsset_withCustomer_betweenTenants() throws Exception { + logInAsTenantAdmin1(); + Customer customer = createCustomer(tenantId1, "My customer"); + Asset asset = createAsset(tenantId1, customer.getId(), "AB", "My asset"); + + EntityExportData customerExportData = exportSingleEntity(customer.getId()); + EntityExportData assetExportData = exportSingleEntity(asset.getId()); + + logInAsTenantAdmin2(); + Customer importedCustomer = importEntities(List.of(customerExportData)).get(0).getSavedEntity(); + Asset importedAsset = importEntities(List.of(assetExportData)).get(0).getSavedEntity(); + + assertThat(importedAsset.getCustomerId()).isEqualTo(importedCustomer.getId()); + } + + @Test + public void testExportImportAsset_withCustomer_sameTenant() throws Exception { + logInAsTenantAdmin1(); + Customer customer = createCustomer(tenantId1, "My customer"); + Asset asset = createAsset(tenantId1, customer.getId(), "AB", "My asset"); + + Asset importedAsset = this.importEntities(List.of(exportSingleEntity(asset.getId()))).get(0).getSavedEntity(); + + assertThat(importedAsset.getCustomerId()).isEqualTo(asset.getCustomerId()); + } + + private void checkImportedAssetData(Asset initialAsset, Asset importedAsset) { + assertThat(importedAsset.getName()).isEqualTo(initialAsset.getName()); + assertThat(importedAsset.getType()).isEqualTo(initialAsset.getType()); + assertThat(importedAsset.getLabel()).isEqualTo(initialAsset.getLabel()); + assertThat(importedAsset.getAdditionalInfo()).isEqualTo(initialAsset.getAdditionalInfo()); + } + + @Test + public void testExportImportSingleCustomer_betweenTenants() throws Exception { + logInAsTenantAdmin1(); + Customer customer = createCustomer(tenantAdmin1.getTenantId(), "Customer of tenant 1"); + EntityExportData exportData = exportSingleEntity(customer.getId()); + assertThat(exportData.getEntity()).isEqualTo(customer); + + logInAsTenantAdmin2(); + EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + + checkImportedEntity(tenantId1, customer, tenantId2, importResult); + checkImportedCustomerData(customer, importResult.getSavedEntity()); + } + + @Test + public void testExportImportSingleCustomer_sameTenant() throws Exception { + logInAsTenantAdmin1(); + Customer customer = createCustomer(tenantAdmin1.getTenantId(), "Customer v1.0"); + EntityExportData exportData = exportSingleEntity(customer.getId()); + + EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + + checkImportedEntity(tenantId1, customer, tenantId1, importResult); + checkImportedCustomerData(customer, importResult.getSavedEntity()); + } + + private void checkImportedCustomerData(Customer initialCustomer, Customer importedCustomer) { + assertThat(importedCustomer.getTitle()).isEqualTo(initialCustomer.getTitle()); + assertThat(importedCustomer.getCountry()).isEqualTo(initialCustomer.getCountry()); + assertThat(importedCustomer.getAddress()).isEqualTo(initialCustomer.getAddress()); + assertThat(importedCustomer.getEmail()).isEqualTo(initialCustomer.getEmail()); + } + + + @Test + public void testExportImportDeviceWithProfile_betweenTenants() throws Exception { + logInAsTenantAdmin1(); + DeviceProfile deviceProfile = createDeviceProfile(tenantId1, "Device profile of tenant 1"); Device device = createDevice(tenantId1, null, deviceProfile.getId(), "Device of tenant 1"); DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId1, device.getId()); - this.testExportImportBetweenTenants(device, deviceExportData -> { - assertThat(deviceExportData.getCredentials()).isEqualTo(credentials); - }, importedDevice -> { - assertThat(importedDevice.getName()).isEqualTo(device.getName()); - assertThat(importedDevice.getType()).isEqualTo(device.getType()); - assertThat(importedDevice.getDeviceData()).isEqualTo(device.getDeviceData()); - assertThat(importedDevice.getDeviceProfileId()).isEqualTo(importedDeviceProfileId); - assertThat(importedDevice.getLabel()).isEqualTo(device.getLabel()); - - DeviceCredentials importedCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId2, importedDevice.getId()); - assertThat(importedCredentials.getCredentialsId()).isEqualTo(credentials.getCredentialsId()); - assertThat(importedCredentials.getCredentialsValue()).isEqualTo(credentials.getCredentialsValue()); - assertThat(importedCredentials.getCredentialsType()).isEqualTo(credentials.getCredentialsType()); - }); + EntityExportData profileExportData = exportSingleEntity(deviceProfile.getId()); + assertThat(profileExportData.getEntity()).isEqualTo(deviceProfile); + + EntityExportData deviceExportData = exportSingleEntity(device.getId()); + assertThat(deviceExportData.getEntity()).isEqualTo(device); + assertThat(((DeviceExportData) deviceExportData).getCredentials()).isEqualTo(credentials); + DeviceCredentials exportedCredentials = ((DeviceExportData) deviceExportData).getCredentials(); + exportedCredentials.setCredentialsId(credentials.getCredentialsId() + "a"); + + logInAsTenantAdmin2(); + EntityImportResult profileImportResult = importEntities(List.of(profileExportData)).get(0); + checkImportedEntity(tenantId1, deviceProfile, tenantId2, profileImportResult); + checkImportedDeviceProfileData(deviceProfile, profileImportResult.getSavedEntity()); + + + EntityImportResult deviceImportResult = importEntities(List.of(deviceExportData)).get(0); + Device importedDevice = deviceImportResult.getSavedEntity(); + checkImportedEntity(tenantId1, device, tenantId2, deviceImportResult); + checkImportedDeviceData(device, importedDevice); + + assertThat(importedDevice.getDeviceProfileId()).isEqualTo(profileImportResult.getSavedEntity().getId()); + + DeviceCredentials importedCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId2, importedDevice.getId()); + assertThat(importedCredentials.getId()).isNotEqualTo(credentials.getId()); + assertThat(importedCredentials.getCredentialsId()).isEqualTo(exportedCredentials.getCredentialsId()); + assertThat(importedCredentials.getCredentialsValue()).isEqualTo(credentials.getCredentialsValue()); + assertThat(importedCredentials.getCredentialsType()).isEqualTo(credentials.getCredentialsType()); + } + + @Test + public void testExportImportDevice_sameTenant() throws Exception { + logInAsTenantAdmin1(); + DeviceProfile deviceProfile = createDeviceProfile(tenantId1, "Device profile v1.0"); + Device device = createDevice(tenantId1, null, deviceProfile.getId(), "Device v1.0"); + DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId1, device.getId()); + + EntityExportData deviceExportData = exportSingleEntity(device.getId()); + + EntityImportResult importResult = importEntities(List.of(deviceExportData)).get(0); + Device importedDevice = importResult.getSavedEntity(); + + checkImportedEntity(tenantId1, device, tenantId1, importResult); + assertThat(importedDevice.getDeviceProfileId()).isEqualTo(device.getDeviceProfileId()); + assertThat(deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId1, device.getId())).isEqualTo(credentials); + } + + private void checkImportedDeviceProfileData(DeviceProfile initialProfile, DeviceProfile importedProfile) { + assertThat(initialProfile.getName()).isEqualTo(importedProfile.getName()); + assertThat(initialProfile.getType()).isEqualTo(importedProfile.getType()); + assertThat(initialProfile.getTransportType()).isEqualTo(importedProfile.getTransportType()); + assertThat(initialProfile.getProfileData()).isEqualTo(importedProfile.getProfileData()); + assertThat(initialProfile.getDescription()).isEqualTo(importedProfile.getDescription()); + } + + private void checkImportedDeviceData(Device initialDevice, Device importedDevice) { + assertThat(importedDevice.getName()).isEqualTo(initialDevice.getName()); + assertThat(importedDevice.getType()).isEqualTo(initialDevice.getType()); + assertThat(importedDevice.getDeviceData()).isEqualTo(initialDevice.getDeviceData()); + assertThat(importedDevice.getLabel()).isEqualTo(initialDevice.getLabel()); + } + + + @Test + public void testExportImportSingleRuleChain_betweenTenants() throws Exception { + logInAsTenantAdmin1(); RuleChain ruleChain = createRuleChain(tenantId1, "Rule chain of tenant 1"); RuleChainMetaData metaData = ruleChainService.loadRuleChainMetaData(tenantId1, ruleChain.getId()); - this.testExportImportBetweenTenants(ruleChain, ruleChainExportData -> { - assertThat(ruleChainExportData.getMetaData()).isEqualTo(metaData); - }, importedRuleChain -> { - assertThat(importedRuleChain.getType()).isEqualTo(ruleChain.getType()); - assertThat(importedRuleChain.getName()).isEqualTo(ruleChain.getName()); - assertThat(importedRuleChain.isDebugMode()).isEqualTo(ruleChain.isDebugMode()); - assertThat(importedRuleChain.getConfiguration()).isEqualTo(ruleChain.getConfiguration()); - RuleChainMetaData importedMetaData = ruleChainService.loadRuleChainMetaData(tenantId2, importedRuleChain.getId()); - assertThat(importedMetaData.getConnections()).isEqualTo(metaData.getConnections()); - assertThat(importedMetaData.getFirstNodeIndex()).isEqualTo(metaData.getFirstNodeIndex()); - for (int i = 0; i < metaData.getNodes().size(); i++) { - RuleNode initialNode = metaData.getNodes().get(i); - RuleNode importedNode = importedMetaData.getNodes().get(i); - assertThat(importedNode.getName()).isEqualTo(initialNode.getName()); - assertThat(importedNode.getType()).isEqualTo(initialNode.getType()); - assertThat(importedNode.getConfiguration()).isEqualTo(initialNode.getConfiguration()); - assertThat(importedNode.getAdditionalInfo()).isEqualTo(initialNode.getAdditionalInfo()); - } - }); - - Dashboard dashboard = createDashboard(tenantId1, null, "Dashboard of tenant 1"); - testExportImportBetweenTenants(dashboard, importedDashboard -> { - assertThat(importedDashboard.getTitle()).isEqualTo(dashboard.getTitle()); - assertThat(importedDashboard.getConfiguration()).isEqualTo(dashboard.getConfiguration()); - assertThat(importedDashboard.getImage()).isEqualTo(dashboard.getImage()); - assertThat(importedDashboard.isMobileHide()).isEqualTo(dashboard.isMobileHide()); - }); - } - - private , D extends EntityExportData> EntityImportResult testExportImportBetweenTenants(E entity, Consumer exportDataRequirements, Consumer importedEntityRequirements) throws Exception { + + EntityExportData exportData = exportSingleEntity(ruleChain.getId()); + assertThat(exportData.getEntity()).isEqualTo(ruleChain); + assertThat(((RuleChainExportData) exportData).getMetaData()).isEqualTo(metaData); + + logInAsTenantAdmin2(); + EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + RuleChain importedRuleChain = importResult.getSavedEntity(); + RuleChainMetaData importedMetaData = ruleChainService.loadRuleChainMetaData(tenantId2, importedRuleChain.getId()); + + checkImportedEntity(tenantId1, ruleChain, tenantId2, importResult); + checkImportedRuleChainData(ruleChain, metaData, importedRuleChain, importedMetaData); + } + + @Test + public void testExportImportSingleRuleChain_sameTenant() throws Exception { logInAsTenantAdmin1(); - D exportData = readResponse(doPost("/api/entities/export/" + entity.getId().getEntityType() + "/" + entity.getId()) - .andExpect(status().isOk()), new TypeReference() {}); - assertThat(exportData.getEntity()).isEqualTo(entity); - exportDataRequirements.accept(exportData); + RuleChain ruleChain = createRuleChain(tenantId1, "Rule chain v1.0"); + RuleChainMetaData metaData = ruleChainService.loadRuleChainMetaData(tenantId1, ruleChain.getId()); + + EntityExportData exportData = exportSingleEntity(ruleChain.getId()); + + EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + RuleChain importedRuleChain = importResult.getSavedEntity(); + RuleChainMetaData importedMetaData = ruleChainService.loadRuleChainMetaData(tenantId1, importedRuleChain.getId()); + + checkImportedEntity(tenantId1, ruleChain, tenantId1, importResult); + checkImportedRuleChainData(ruleChain, metaData, importedRuleChain, importedMetaData); + } + + private void checkImportedRuleChainData(RuleChain initialRuleChain, RuleChainMetaData initialMetaData, RuleChain importedRuleChain, RuleChainMetaData importedMetaData) { + assertThat(importedRuleChain.getType()).isEqualTo(initialRuleChain.getType()); + assertThat(importedRuleChain.getName()).isEqualTo(initialRuleChain.getName()); + assertThat(importedRuleChain.isDebugMode()).isEqualTo(initialRuleChain.isDebugMode()); + assertThat(importedRuleChain.getConfiguration()).isEqualTo(initialRuleChain.getConfiguration()); + + assertThat(importedMetaData.getConnections()).isEqualTo(initialMetaData.getConnections()); + assertThat(importedMetaData.getFirstNodeIndex()).isEqualTo(initialMetaData.getFirstNodeIndex()); + for (int i = 0; i < initialMetaData.getNodes().size(); i++) { + RuleNode initialNode = initialMetaData.getNodes().get(i); + RuleNode importedNode = importedMetaData.getNodes().get(i); + assertThat(importedNode.getRuleChainId()).isEqualTo(importedRuleChain.getId()); + assertThat(importedNode.getName()).isEqualTo(initialNode.getName()); + assertThat(importedNode.getType()).isEqualTo(initialNode.getType()); + assertThat(importedNode.getConfiguration()).isEqualTo(initialNode.getConfiguration()); + assertThat(importedNode.getAdditionalInfo()).isEqualTo(initialNode.getAdditionalInfo()); + } + } + + + @Test + public void testExportImportSingleDashboard_betweenTenants() throws Exception { + logInAsTenantAdmin1(); + Dashboard dashboard = createDashboard(tenantAdmin1.getTenantId(), null, "Dashboard of tenant 1"); + + EntityExportData exportData = exportSingleEntity(dashboard.getId()); + assertThat(exportData.getEntity()).isEqualTo(dashboard); logInAsTenantAdmin2(); - EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + checkImportedEntity(tenantId1, dashboard, tenantId2, importResult); + checkImportedDashboardData(dashboard, importResult.getSavedEntity()); + } - E importedEntity = (E) exportableEntitiesService.findEntityById(tenantId2, importResult.getSavedEntity().getId()); - assertThat(importedEntity).isNotNull(); - assertThat(importResult.getSavedEntity()).isEqualTo(importedEntity); - assertThat(importResult.getOldEntity()).isNull(); + @Test + public void testExportImportSingleDashboard_sameTenant() throws Exception { + logInAsTenantAdmin1(); + Dashboard dashboard = createDashboard(tenantAdmin1.getTenantId(), null, "Dashboard v1.0"); - assertThat(importedEntity.getId()).isNotEqualTo(entity.getId()); - assertThat(importedEntity.getExternalId()).isEqualTo(entity.getId()); - assertThat(importedEntity.getTenantId()).isEqualTo(tenantId2); - importedEntityRequirements.accept(importedEntity); - return importResult; + EntityExportData exportData = exportSingleEntity(dashboard.getId()); + + EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + checkImportedEntity(tenantId1, dashboard, tenantId1, importResult); + checkImportedDashboardData(dashboard, importResult.getSavedEntity()); } - private > EntityImportResult testExportImportBetweenTenants(E entity, Consumer importedEntityRequirements) throws Exception { - return testExportImportBetweenTenants(entity, exportData -> {}, importedEntityRequirements); + private void checkImportedDashboardData(Dashboard initialDashboard, Dashboard importedDashboard) { + assertThat(importedDashboard.getTitle()).isEqualTo(initialDashboard.getTitle()); + assertThat(importedDashboard.getConfiguration()).isEqualTo(initialDashboard.getConfiguration()); + assertThat(importedDashboard.getImage()).isEqualTo(initialDashboard.getImage()); + assertThat(importedDashboard.isMobileHide()).isEqualTo(initialDashboard.isMobileHide()); + if (initialDashboard.getAssignedCustomers() != null) { + assertThat(importedDashboard.getAssignedCustomers()).containsAll(initialDashboard.getAssignedCustomers()); + } + } + + + private > void checkImportedEntity(TenantId tenantId1, E initialEntity, TenantId tenantId2, EntityImportResult importResult) { + E importedEntity = importResult.getSavedEntity(); + + assertThat(initialEntity.getTenantId()).isEqualTo(tenantId1); + assertThat(importedEntity.getTenantId()).isEqualTo(tenantId2); + + assertThat(importedEntity.getExternalId()).isEqualTo(initialEntity.getId()); + + boolean sameTenant = tenantId1.equals(tenantId2); + if (!sameTenant) { + assertThat(importedEntity.getId()).isNotEqualTo(initialEntity.getId()); + } else { + assertThat(importedEntity.getId()).isEqualTo(initialEntity.getId()); + assertThat(importResult.getOldEntity()).isEqualTo(initialEntity); + } } @@ -217,6 +391,12 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp } } + private , I extends EntityId> EntityExportData exportSingleEntity(I entityId) throws Exception { + return readResponse(doPost("/api/entities/export/" + entityId.getEntityType() + "/" + entityId.getId().toString()) + .andExpect(status().isOk()), new TypeReference>() {}); + } + + private void logInAsTenantAdmin1() throws Exception { login(tenantAdmin1.getEmail(), "12345678"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java index 5b2b5a2366..9f2cd51952 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java @@ -15,16 +15,12 @@ */ package org.thingsboard.server.dao; -import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.ExportableEntity; import java.util.UUID; -public interface ExportableEntityDao { +public interface ExportableEntityDao> extends TenantEntityDao { T findByTenantIdAndExternalId(UUID tenantId, UUID externalId); - T findByTenantIdAndId(UUID tenantId, UUID id); - - EntityType getEntityType(); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityRepository.java b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityRepository.java index b6a303a941..1ce43ffe12 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityRepository.java @@ -17,10 +17,8 @@ package org.thingsboard.server.dao; import java.util.UUID; -public interface ExportableEntityRepository { +public interface ExportableEntityRepository extends TenantEntityRepository { D findByTenantIdAndExternalId(UUID tenantId, UUID externalId); - D findByTenantIdAndId(UUID tenantId, UUID id); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java b/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java index 17367c2cc7..53ffb7882a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java @@ -15,9 +15,18 @@ */ package org.thingsboard.server.dao; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.TenantId; -public interface TenantEntityDao { +import java.util.UUID; + +public interface TenantEntityDao { Long countByTenantId(TenantId tenantId); + + T findByTenantIdAndId(UUID tenantId, UUID id); + + EntityType getEntityType(); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/TenantEntityRepository.java b/dao/src/main/java/org/thingsboard/server/dao/TenantEntityRepository.java new file mode 100644 index 0000000000..8997f65ca8 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/TenantEntityRepository.java @@ -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.dao; + +import java.util.UUID; + +public interface TenantEntityRepository { + + D findByTenantIdAndId(UUID tenantId, UUID id); + + Long countByTenantId(UUID tenantId); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java index 495886bcc4..c7cf6f38e9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java @@ -34,7 +34,7 @@ import java.util.UUID; * The Interface AssetDao. * */ -public interface AssetDao extends Dao, TenantEntityDao, ExportableEntityDao { +public interface AssetDao extends Dao, ExportableEntityDao { /** * Find asset info by id. diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java index 89672d2a73..df589711b9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java @@ -29,7 +29,7 @@ import java.util.UUID; /** * The Interface CustomerDao. */ -public interface CustomerDao extends Dao, TenantEntityDao, ExportableEntityDao { +public interface CustomerDao extends Dao, ExportableEntityDao { /** * Save or update customer object diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java index 90353f49b3..ef6a739391 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java @@ -24,7 +24,7 @@ import org.thingsboard.server.dao.TenantEntityDao; /** * The Interface DashboardDao. */ -public interface DashboardDao extends Dao, TenantEntityDao, ExportableEntityDao { +public interface DashboardDao extends Dao, ExportableEntityDao { /** * Save or update dashboard object diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java index 90f2b218a7..688e7246d4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java @@ -36,7 +36,7 @@ import java.util.UUID; * The Interface DeviceDao. * */ -public interface DeviceDao extends Dao, TenantEntityDao, ExportableEntityDao { +public interface DeviceDao extends Dao, ExportableEntityDao { /** * Find device info by id. diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java index 445210ca3b..a24df5794c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java @@ -21,6 +21,6 @@ import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.TenantEntityDao; import org.thingsboard.server.dao.TenantEntityWithDataDao; -public interface OtaPackageDao extends Dao, TenantEntityWithDataDao { +public interface OtaPackageDao extends Dao, TenantEntityWithDataDao, TenantEntityDao { Long sumDataSizeByTenantId(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java index 59f287eadd..47f69cc99d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java @@ -30,7 +30,7 @@ import java.util.UUID; /** * Created by igor on 3/12/18. */ -public interface RuleChainDao extends Dao, TenantEntityDao, ExportableEntityDao { +public interface RuleChainDao extends Dao, ExportableEntityDao { /** * Find rule chains by tenantId and page link. diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java index 1300fcdfa5..893f22503d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java @@ -38,5 +38,4 @@ public interface CustomerRepository extends JpaRepository, CustomerEntity findByTenantIdAndTitle(UUID tenantId, String title); - Long countByTenantId(UUID tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java index 539377a647..26e49e9c66 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java @@ -116,6 +116,11 @@ public class JpaDeviceProfileDao extends JpaAbstractSearchTextDao { +public interface OtaPackageRepository extends JpaRepository, TenantEntityRepository { @Query(value = "SELECT COALESCE(SUM(ota.data_size), 0) FROM ota_package ota WHERE ota.tenant_id = :tenantId AND ota.data IS NOT NULL", nativeQuery = true) Long sumDataSizeByTenantId(@Param("tenantId") UUID tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java index 888951778c..2b1bca32b3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/RuleChainRepository.java @@ -63,8 +63,6 @@ public interface RuleChainRepository extends JpaRepository findByTenantIdAndTypeAndName(UUID tenantId, RuleChainType type, String name); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java index 6b2de9872a..42d331cab5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java @@ -18,6 +18,8 @@ package org.thingsboard.server.dao.sql.user; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -96,4 +98,15 @@ public class JpaUserDao extends JpaAbstractSearchTextDao imple public Long countByTenantId(TenantId tenantId) { return userRepository.countByTenantId(tenantId.getId()); } + + @Override + public User findByTenantIdAndId(UUID tenantId, UUID id) { + return DaoUtil.getData(userRepository.findByTenantIdAndId(tenantId, id)); + } + + @Override + public EntityType getEntityType() { + return EntityType.USER; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java index 51f6138cc1..98b7cf0d00 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java @@ -21,6 +21,8 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.dao.TenantEntityDao; +import org.thingsboard.server.dao.TenantEntityRepository; import org.thingsboard.server.dao.model.sql.UserEntity; import java.util.UUID; @@ -28,7 +30,7 @@ import java.util.UUID; /** * @author Valerii Sosliuk */ -public interface UserRepository extends JpaRepository { +public interface UserRepository extends JpaRepository, TenantEntityRepository { UserEntity findByEmail(String email); @@ -47,5 +49,4 @@ public interface UserRepository extends JpaRepository { @Param("searchText") String searchText, Pageable pageable); - Long countByTenantId(UUID tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java index b906b3278d..7e27c1bf00 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java @@ -24,7 +24,7 @@ import org.thingsboard.server.dao.TenantEntityDao; import java.util.UUID; -public interface UserDao extends Dao, TenantEntityDao { +public interface UserDao extends Dao, TenantEntityDao { /** * Save or update user object From 9b8fca99733b70e732f17085cd646c085a120703 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Mon, 4 Apr 2022 19:22:16 +0300 Subject: [PATCH 13/39] Export/import api improvements --- .../server/controller/BaseController.java | 6 ++--- .../DefaultEntitiesExportImportService.java | 23 +++++++++------- .../exporting/ExportableEntitiesService.java | 5 ++-- .../sync/exporting/TenantEntitiesService.java | 27 ------------------- .../importing/impl/AssetImportService.java | 1 + .../impl/BaseEntityImportService.java | 1 - .../importing/impl/CustomerImportService.java | 1 + .../impl/DashboardImportService.java | 1 + .../importing/impl/DeviceImportService.java | 1 + .../impl/DeviceProfileImportService.java | 1 + .../impl/RuleChainImportService.java | 1 + ...EntitiesExportImportControllerSqlTest.java | 6 ++--- .../server/common/data/ExportableEntity.java | 6 +---- .../java/org/thingsboard/server/dao/Dao.java | 3 +++ .../server/dao/ExportableEntityDao.java | 2 +- .../dao/ExportableEntityRepository.java | 2 +- .../server/dao/TenantEntityDao.java | 11 +------- .../server/dao/TenantEntityRepository.java | 26 ------------------ .../server/dao/asset/AssetDao.java | 2 +- .../server/dao/customer/CustomerDao.java | 2 +- .../server/dao/dashboard/DashboardDao.java | 2 +- .../server/dao/device/DeviceDao.java | 2 +- .../server/dao/ota/OtaPackageDao.java | 2 +- .../server/dao/rule/RuleChainDao.java | 4 +-- .../server/dao/sql/asset/JpaAssetDao.java | 5 ---- .../dao/sql/customer/CustomerRepository.java | 1 + .../dao/sql/customer/JpaCustomerDao.java | 5 ---- .../dao/sql/dashboard/JpaDashboardDao.java | 5 ---- .../server/dao/sql/device/JpaDeviceDao.java | 5 ---- .../dao/sql/device/JpaDeviceProfileDao.java | 10 ------- .../server/dao/sql/ota/JpaOtaPackageDao.java | 11 -------- .../dao/sql/ota/OtaPackageRepository.java | 3 +-- .../server/dao/sql/rule/JpaRuleChainDao.java | 5 ---- .../dao/sql/rule/RuleChainRepository.java | 2 ++ .../server/dao/sql/user/JpaUserDao.java | 13 --------- .../server/dao/sql/user/UserRepository.java | 5 ++-- .../thingsboard/server/dao/user/UserDao.java | 2 +- 37 files changed, 47 insertions(+), 163 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/TenantEntitiesService.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/TenantEntityRepository.java diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 382929e1e7..1f683c7874 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -924,7 +924,7 @@ public abstract class BaseController { } } - protected & HasTenantId, I extends EntityId> void onEntityUpdatedOrCreated(User user, E savedEntity, E oldEntity, boolean isNewEntity) { + protected , I extends EntityId> void onEntityUpdatedOrCreated(User user, E savedEntity, E oldEntity, boolean isNewEntity) { boolean notifyEdgeService = false; EntityType entityType = savedEntity.getId().getEntityType(); @@ -954,7 +954,7 @@ public abstract class BaseController { case RULE_CHAIN: RuleChainType ruleChainType = ((RuleChain) savedEntity).getType(); if (RuleChainType.CORE.equals(ruleChainType)) { - tbClusterService.broadcastEntityStateChangeEvent(savedEntity.getTenantId(), savedEntity.getId(), + tbClusterService.broadcastEntityStateChangeEvent(user.getTenantId(), savedEntity.getId(), isNewEntity ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); } if (RuleChainType.EDGE.equals(ruleChainType)) { @@ -981,7 +981,7 @@ public abstract class BaseController { log.error("Failed to log entity action", e); } if (notifyEdgeService) { - sendEntityNotificationMsg(savedEntity.getTenantId(), savedEntity.getId(), isNewEntity ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); + sendEntityNotificationMsg(user.getTenantId(), savedEntity.getId(), isNewEntity ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java index eeaef01f63..ef9da1f89d 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.dao.TenantEntityDao; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -48,7 +49,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS private final Map> exportServices = new HashMap<>(); private final Map> importServices = new HashMap<>(); - private final Map> daos = new HashMap<>(); + private final Map> daos = new HashMap<>(); protected static final List SUPPORTED_ENTITY_TYPES = List.of( EntityType.CUSTOMER, EntityType.ASSET, EntityType.RULE_CHAIN, @@ -86,12 +87,6 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS } - @Override - public & HasTenantId, I extends EntityId> E findEntityById(TenantId tenantId, I id) { - TenantEntityDao dao = (TenantEntityDao) getDao(id.getEntityType()); - return dao.findByTenantIdAndId(tenantId.getId(), id.getId()); - } - @Override public , I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId) { EntityType entityType = externalId.getEntityType(); @@ -105,6 +100,12 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS return findEntityById(tenantId, externalId); } + @Override + public , I extends EntityId> E findEntityById(TenantId tenantId, I id) { + Dao dao = (Dao) getDao(id.getEntityType()); + return dao.findById(tenantId, id.getId()); + } + @SuppressWarnings("unchecked") private , D extends EntityExportData> EntityExportService getExportService(EntityType entityType) { @@ -122,14 +123,14 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS return (EntityImportService) importServices.get(entityType); } - private TenantEntityDao getDao(EntityType entityType) { + private Dao getDao(EntityType entityType) { return daos.get(entityType); } @Autowired private void setServices(Collection> exportServices, Collection> importServices, - Collection> daos) { + Collection> daos) { exportServices.forEach(entityExportService -> { this.exportServices.put(entityExportService.getEntityType(), entityExportService); }); @@ -137,7 +138,9 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS this.importServices.put(entityImportService.getEntityType(), entityImportService); }); daos.forEach(dao -> { - this.daos.put(dao.getEntityType(), dao); + if (dao.getEntityType() != null) { + this.daos.put(dao.getEntityType(), dao); + } }); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java index 1c1cf6bf7f..9a47e98920 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java @@ -16,13 +16,14 @@ package org.thingsboard.server.service.sync.exporting; import org.thingsboard.server.common.data.ExportableEntity; -import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; -public interface ExportableEntitiesService extends TenantEntitiesService { +public interface ExportableEntitiesService { , I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId); + , I extends EntityId> E findEntityById(TenantId tenantId, I id); + } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/TenantEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/TenantEntitiesService.java deleted file mode 100644 index e3e25c21e8..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/TenantEntitiesService.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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.sync.exporting; - -import org.thingsboard.server.common.data.HasTenantId; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.HasId; -import org.thingsboard.server.common.data.id.TenantId; - -public interface TenantEntitiesService { - - & HasTenantId, I extends EntityId> E findEntityById(TenantId tenantId, I id); - -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java index b8328a43c3..b9013fd549 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java @@ -34,6 +34,7 @@ public class AssetImportService extends BaseEntityImportService assignedCustomers = idProvider.get(tenantId, Dashboard::getAssignedCustomers, ShortCustomerInfo::getCustomerId, ShortCustomerInfo::setCustomerId); dashboard.setAssignedCustomers(null); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java index 2dcf4f7e7f..f35a20e312 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java @@ -34,6 +34,7 @@ public class DeviceImportService extends BaseEntityImportService { diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java index 464b5da401..f114af6625 100644 --- a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java @@ -28,11 +28,10 @@ import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.ExportableEntity; +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.asset.Asset; -import org.thingsboard.server.common.data.id.AssetId; -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.rule.RuleChain; @@ -52,7 +51,6 @@ import org.thingsboard.server.service.sync.importing.EntityImportResult; import java.util.List; -import static org.assertj.core.api.Assertions.as; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -358,7 +356,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp } - private > void checkImportedEntity(TenantId tenantId1, E initialEntity, TenantId tenantId2, EntityImportResult importResult) { + private & HasTenantId> void checkImportedEntity(TenantId tenantId1, E initialEntity, TenantId tenantId2, EntityImportResult importResult) { E importedEntity = importResult.getSavedEntity(); assertThat(initialEntity.getTenantId()).isEqualTo(tenantId1); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java b/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java index f1e8c3754e..50569b7a02 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java @@ -17,9 +17,8 @@ package org.thingsboard.server.common.data; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; -import org.thingsboard.server.common.data.id.TenantId; -public interface ExportableEntity extends HasId, HasTenantId, HasName { +public interface ExportableEntity extends HasId, HasName { I getId(); void setId(I id); @@ -27,7 +26,4 @@ public interface ExportableEntity extends HasId, HasTenan I getExternalId(); void setExternalId(I externalId); - TenantId getTenantId(); - void setTenantId(TenantId tenantId); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/Dao.java b/dao/src/main/java/org/thingsboard/server/dao/Dao.java index 932a6e5ec1..038be61d21 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/Dao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/Dao.java @@ -16,6 +16,7 @@ package org.thingsboard.server.dao; import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; import java.util.Collection; @@ -40,4 +41,6 @@ public interface Dao { void removeAllByIds(Collection ids); + default EntityType getEntityType() { return null; } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java index 9f2cd51952..7e4d00286c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java @@ -19,7 +19,7 @@ import org.thingsboard.server.common.data.ExportableEntity; import java.util.UUID; -public interface ExportableEntityDao> extends TenantEntityDao { +public interface ExportableEntityDao> extends Dao { T findByTenantIdAndExternalId(UUID tenantId, UUID externalId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityRepository.java b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityRepository.java index 1ce43ffe12..6e2d2e115a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityRepository.java @@ -17,7 +17,7 @@ package org.thingsboard.server.dao; import java.util.UUID; -public interface ExportableEntityRepository extends TenantEntityRepository { +public interface ExportableEntityRepository { D findByTenantIdAndExternalId(UUID tenantId, UUID externalId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java b/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java index 53ffb7882a..17367c2cc7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/TenantEntityDao.java @@ -15,18 +15,9 @@ */ package org.thingsboard.server.dao; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.TenantId; -import java.util.UUID; - -public interface TenantEntityDao { +public interface TenantEntityDao { Long countByTenantId(TenantId tenantId); - - T findByTenantIdAndId(UUID tenantId, UUID id); - - EntityType getEntityType(); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/TenantEntityRepository.java b/dao/src/main/java/org/thingsboard/server/dao/TenantEntityRepository.java deleted file mode 100644 index 8997f65ca8..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/TenantEntityRepository.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 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.dao; - -import java.util.UUID; - -public interface TenantEntityRepository { - - D findByTenantIdAndId(UUID tenantId, UUID id); - - Long countByTenantId(UUID tenantId); - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java index c7cf6f38e9..495886bcc4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/AssetDao.java @@ -34,7 +34,7 @@ import java.util.UUID; * The Interface AssetDao. * */ -public interface AssetDao extends Dao, ExportableEntityDao { +public interface AssetDao extends Dao, TenantEntityDao, ExportableEntityDao { /** * Find asset info by id. diff --git a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java index df589711b9..89672d2a73 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/customer/CustomerDao.java @@ -29,7 +29,7 @@ import java.util.UUID; /** * The Interface CustomerDao. */ -public interface CustomerDao extends Dao, ExportableEntityDao { +public interface CustomerDao extends Dao, TenantEntityDao, ExportableEntityDao { /** * Save or update customer object diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java index ef6a739391..90353f49b3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java @@ -24,7 +24,7 @@ import org.thingsboard.server.dao.TenantEntityDao; /** * The Interface DashboardDao. */ -public interface DashboardDao extends Dao, ExportableEntityDao { +public interface DashboardDao extends Dao, TenantEntityDao, ExportableEntityDao { /** * Save or update dashboard object diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java index 688e7246d4..90f2b218a7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java @@ -36,7 +36,7 @@ import java.util.UUID; * The Interface DeviceDao. * */ -public interface DeviceDao extends Dao, ExportableEntityDao { +public interface DeviceDao extends Dao, TenantEntityDao, ExportableEntityDao { /** * Find device info by id. diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java index a24df5794c..445210ca3b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java @@ -21,6 +21,6 @@ import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.TenantEntityDao; import org.thingsboard.server.dao.TenantEntityWithDataDao; -public interface OtaPackageDao extends Dao, TenantEntityWithDataDao, TenantEntityDao { +public interface OtaPackageDao extends Dao, TenantEntityWithDataDao { Long sumDataSizeByTenantId(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java index 47f69cc99d..1d6c1bf29a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/RuleChainDao.java @@ -30,7 +30,7 @@ import java.util.UUID; /** * Created by igor on 3/12/18. */ -public interface RuleChainDao extends Dao, ExportableEntityDao { +public interface RuleChainDao extends Dao, TenantEntityDao, ExportableEntityDao { /** * Find rule chains by tenantId and page link. @@ -80,6 +80,4 @@ public interface RuleChainDao extends Dao, ExportableEntityDao findByTenantIdAndTypeAndName(TenantId tenantId, RuleChainType type, String name); - RuleChain findByTenantIdAndExternalId(UUID tenantId, UUID externalId); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java index cd4fefac48..14788f8b38 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java @@ -214,11 +214,6 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao im return DaoUtil.getData(assetRepository.findByTenantIdAndExternalId(tenantId, externalId)); } - @Override - public Asset findByTenantIdAndId(UUID tenantId, UUID id) { - return DaoUtil.getData(assetRepository.findByTenantIdAndId(tenantId, id)); - } - @Override public EntityType getEntityType() { return EntityType.ASSET; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java index 893f22503d..1300fcdfa5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/CustomerRepository.java @@ -38,4 +38,5 @@ public interface CustomerRepository extends JpaRepository, CustomerEntity findByTenantIdAndTitle(UUID tenantId, String title); + Long countByTenantId(UUID tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java index 9cf74f1f33..17e1625a29 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java @@ -75,11 +75,6 @@ public class JpaCustomerDao extends JpaAbstractSearchTextDao return DaoUtil.getData(deviceRepository.findByTenantIdAndExternalId(tenantId, externalId)); } - @Override - public Device findByTenantIdAndId(UUID tenantId, UUID id) { - return findDeviceByTenantIdAndId(TenantId.fromUUID(tenantId), id); - } - @Override public EntityType getEntityType() { return EntityType.DEVICE; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java index 26e49e9c66..23e564b735 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java @@ -116,16 +116,6 @@ public class JpaDeviceProfileDao extends JpaAbstractSearchTextDao, TenantEntityRepository { +public interface OtaPackageRepository extends JpaRepository { @Query(value = "SELECT COALESCE(SUM(ota.data_size), 0) FROM ota_package ota WHERE ota.tenant_id = :tenantId AND ota.data IS NOT NULL", nativeQuery = true) Long sumDataSizeByTenantId(@Param("tenantId") UUID tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java index e2d8aafef3..0d5fea6f98 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java @@ -114,11 +114,6 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao findByTenantIdAndTypeAndName(UUID tenantId, RuleChainType type, String name); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java index 42d331cab5..6b2de9872a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/JpaUserDao.java @@ -18,8 +18,6 @@ package org.thingsboard.server.dao.sql.user; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -98,15 +96,4 @@ public class JpaUserDao extends JpaAbstractSearchTextDao imple public Long countByTenantId(TenantId tenantId) { return userRepository.countByTenantId(tenantId.getId()); } - - @Override - public User findByTenantIdAndId(UUID tenantId, UUID id) { - return DaoUtil.getData(userRepository.findByTenantIdAndId(tenantId, id)); - } - - @Override - public EntityType getEntityType() { - return EntityType.USER; - } - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java index 98b7cf0d00..51f6138cc1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/user/UserRepository.java @@ -21,8 +21,6 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.thingsboard.server.common.data.security.Authority; -import org.thingsboard.server.dao.TenantEntityDao; -import org.thingsboard.server.dao.TenantEntityRepository; import org.thingsboard.server.dao.model.sql.UserEntity; import java.util.UUID; @@ -30,7 +28,7 @@ import java.util.UUID; /** * @author Valerii Sosliuk */ -public interface UserRepository extends JpaRepository, TenantEntityRepository { +public interface UserRepository extends JpaRepository { UserEntity findByEmail(String email); @@ -49,4 +47,5 @@ public interface UserRepository extends JpaRepository, TenantE @Param("searchText") String searchText, Pageable pageable); + Long countByTenantId(UUID tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java index 7e27c1bf00..b906b3278d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/user/UserDao.java @@ -24,7 +24,7 @@ import org.thingsboard.server.dao.TenantEntityDao; import java.util.UUID; -public interface UserDao extends Dao, TenantEntityDao { +public interface UserDao extends Dao, TenantEntityDao { /** * Save or update user object From 18b2fd4664b2cb5a54b671421d577fd6e638d5a3 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Tue, 5 Apr 2022 15:01:26 +0300 Subject: [PATCH 14/39] Fix permission checks for export/import api --- .../EntitiesExportImportController.java | 94 +++++-------------- .../DefaultEntitiesExportImportService.java | 69 +++++++++----- .../sync/EntitiesExportImportService.java | 9 +- .../sync/exporting/EntityExportService.java | 5 +- .../exporting/ExportableEntitiesService.java | 14 ++- .../impl/BaseEntityExportService.java | 27 ++++-- .../sync/importing/EntityImportService.java | 5 +- .../importing/impl/AssetImportService.java | 8 +- .../impl/BaseEntityImportService.java | 92 ++++++++++++------ .../importing/impl/CustomerImportService.java | 6 +- .../impl/DashboardImportService.java | 10 +- .../importing/impl/DeviceImportService.java | 14 ++- .../impl/DeviceProfileImportService.java | 14 ++- .../impl/RuleChainImportService.java | 11 ++- .../server/dao/sql/alarm/JpaAlarmDao.java | 7 ++ .../server/dao/sql/edge/JpaEdgeDao.java | 5 + .../dao/sql/entityview/JpaEntityViewDao.java | 6 ++ .../dao/sql/resource/JpaTbResourceDao.java | 7 ++ .../server/dao/sql/rpc/JpaRpcDao.java | 7 ++ .../server/dao/sql/rule/JpaRuleNodeDao.java | 7 ++ .../server/dao/sql/tenant/JpaTenantDao.java | 6 ++ .../sql/usagerecord/JpaApiUsageStateDao.java | 7 ++ .../server/dao/sql/user/JpaUserDao.java | 7 ++ .../dao/sql/widget/JpaWidgetTypeDao.java | 7 ++ .../dao/sql/widget/JpaWidgetsBundleDao.java | 7 ++ 25 files changed, 291 insertions(+), 160 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index f158ff15e7..f4abd1b5f7 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -39,7 +39,6 @@ import org.thingsboard.server.common.data.query.EntityKey; import org.thingsboard.server.common.data.query.EntityKeyType; import org.thingsboard.server.common.data.query.EntityTypeFilter; import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.sync.EntitiesExportImportService; @@ -81,9 +80,10 @@ public class EntitiesExportImportController extends BaseController { public EntityExportData> exportSingleEntity(@PathVariable EntityType entityType, @PathVariable UUID id, @RequestParam Map exportSettingsParams) throws ThingsboardException { + SecurityUser user = getCurrentUser(); EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, id); try { - return exportEntity(getTenantId(), entityId, toExportSettings(exportSettingsParams)); + return exportEntity(user, entityId, toExportSettings(exportSettingsParams)); } catch (Exception e) { throw handleException(e); } @@ -93,11 +93,12 @@ public class EntitiesExportImportController extends BaseController { public List>> exportEntitiesByIds(@PathVariable EntityType entityType, @RequestParam UUID[] ids, @RequestParam Map exportSettingsParams) throws ThingsboardException { + SecurityUser user = getCurrentUser(); List entitiesIds = Arrays.stream(ids) .map(id -> EntityIdFactory.getByTypeAndUuid(entityType, id)) .collect(Collectors.toList()); try { - return exportEntitiesByIds(getTenantId(), entitiesIds, toExportSettings(exportSettingsParams)); + return exportEntitiesByIds(user, entitiesIds, toExportSettings(exportSettingsParams)); } catch (Exception e) { throw handleException(e); } @@ -109,13 +110,13 @@ public class EntitiesExportImportController extends BaseController { @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "2147483647") int pageSize, @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { - TenantId tenantId = getTenantId(); + SecurityUser user = getCurrentUser(); CustomerId customerId = toCustomerId(customerUuid); EntityTypeFilter entityTypeFilter = new EntityTypeFilter(); entityTypeFilter.setEntityType(entityType); try { - return exportEntitiesByFilter(tenantId, customerId, entityTypeFilter, page, pageSize, toExportSettings(exportSettingsParams)); + return exportEntitiesByFilter(user, customerId, entityTypeFilter, page, pageSize, toExportSettings(exportSettingsParams)); } catch (Exception e) { throw handleException(e); } @@ -127,10 +128,10 @@ public class EntitiesExportImportController extends BaseController { @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "2147483647") int pageSize, @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { - TenantId tenantId = getTenantId(); + SecurityUser user = getCurrentUser(); CustomerId customerId = toCustomerId(customerUuid); try { - return exportEntitiesByFilter(tenantId, customerId, filter, page, pageSize, toExportSettings(exportSettingsParams)); + return exportEntitiesByFilter(user, customerId, filter, page, pageSize, toExportSettings(exportSettingsParams)); } catch (Exception e) { throw handleException(e); } @@ -141,12 +142,12 @@ public class EntitiesExportImportController extends BaseController { public List>> exportAllEntitiesByFilters(@RequestBody List filters, @RequestParam Map exportSettingsParams, @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { - TenantId tenantId = getTenantId(); + SecurityUser user = getCurrentUser(); CustomerId customerId = toCustomerId(customerUuid); try { List>> exportDataList = new ArrayList<>(); for (EntityFilter filter : filters) { - exportDataList.addAll(exportEntitiesByFilter(tenantId, customerId, filter, 0, Integer.MAX_VALUE, toExportSettings(exportSettingsParams))); + exportDataList.addAll(exportEntitiesByFilter(user, customerId, filter, 0, Integer.MAX_VALUE, toExportSettings(exportSettingsParams))); } return exportDataList; } catch (Exception e) { @@ -158,17 +159,17 @@ public class EntitiesExportImportController extends BaseController { public List>> exportEntitiesByQuery(@RequestBody EntityDataQuery entitiesQuery, @RequestParam Map exportSettingsParams, @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { - TenantId tenantId = getTenantId(); + SecurityUser user = getCurrentUser(); CustomerId customerId = toCustomerId(customerUuid); try { - return exportEntitiesByQuery(tenantId, customerId, entitiesQuery, toExportSettings(exportSettingsParams)); + return exportEntitiesByQuery(user, customerId, entitiesQuery, toExportSettings(exportSettingsParams)); } catch (Exception e) { throw handleException(e); } } - private List>> exportEntitiesByFilter(TenantId tenantId, CustomerId customerId, EntityFilter filter, int page, int pageSize, EntityExportSettings exportSettings) throws ThingsboardException { + private List>> exportEntitiesByFilter(SecurityUser user, CustomerId customerId, EntityFilter filter, int page, int pageSize, EntityExportSettings exportSettings) throws ThingsboardException { EntityDataPageLink pageLink = new EntityDataPageLink(); pageLink.setPage(page); pageLink.setPageSize(pageSize); @@ -176,43 +177,26 @@ public class EntitiesExportImportController extends BaseController { pageLink.setSortOrder(new EntityDataSortOrder(sortProperty, EntityDataSortOrder.Direction.DESC)); EntityDataQuery query = new EntityDataQuery(filter, pageLink, List.of(sortProperty), Collections.emptyList(), Collections.emptyList()); - return exportEntitiesByQuery(tenantId, customerId, query, exportSettings); + return exportEntitiesByQuery(user, customerId, query, exportSettings); } - private List>> exportEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query, EntityExportSettings exportSettings) throws ThingsboardException { - List entitiesIds = entityService.findEntityDataByQuery(tenantId, customerId, query).getData().stream() + private List>> exportEntitiesByQuery(SecurityUser user, CustomerId customerId, EntityDataQuery query, EntityExportSettings exportSettings) throws ThingsboardException { + List entitiesIds = entityService.findEntityDataByQuery(user.getTenantId(), customerId, query).getData().stream() .map(EntityData::getEntityId) .collect(Collectors.toList()); - return exportEntitiesByIds(tenantId, entitiesIds, exportSettings); + return exportEntitiesByIds(user, entitiesIds, exportSettings); } - private List>> exportEntitiesByIds(TenantId tenantId, List entitiesIds, EntityExportSettings exportSettings) throws ThingsboardException { + private List>> exportEntitiesByIds(SecurityUser user, List entitiesIds, EntityExportSettings exportSettings) throws ThingsboardException { List>> exportDataList = new ArrayList<>(); for (EntityId entityId : entitiesIds) { - exportDataList.add(exportEntity(tenantId, entityId, exportSettings)); + exportDataList.add(exportEntity(user, entityId, exportSettings)); } return exportDataList; } - private , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId, EntityExportSettings exportSettings) throws ThingsboardException { - checkEntityId(entityId, Operation.READ); - - List relations = new LinkedList<>(); - if (exportSettings.isExportInboundRelations()) { - relations.addAll(relationService.findByTo(tenantId, entityId, RelationTypeGroup.COMMON)); - } - if (exportSettings.isExportOutboundRelations()) { - relations.addAll(relationService.findByFrom(tenantId, entityId, RelationTypeGroup.COMMON)); - } - for (EntityRelation relation : relations) { - if (!relation.getFrom().equals(entityId)) { - checkEntityId(relation.getFrom(), Operation.READ); - } else if (!relation.getTo().equals(entityId)) { - checkEntityId(relation.getTo(), Operation.READ); - } - } - - return exportImportService.exportEntity(tenantId, entityId, exportSettings); + private , I extends EntityId> EntityExportData exportEntity(SecurityUser user, I entityId, EntityExportSettings exportSettings) throws ThingsboardException { + return exportImportService.exportEntity(user, entityId, exportSettings); } @@ -235,41 +219,7 @@ public class EntitiesExportImportController extends BaseController { public List>> importEntities(SecurityUser user, List>> exportDataList, EntityImportSettings importSettings) throws ThingsboardException { - for (EntityExportData> exportData : exportDataList) { - ExportableEntity existingEntity = exportableEntitiesService.findEntityByExternalId(user.getTenantId(), exportData.getEntity().getId()); - if (existingEntity != null) { - accessControlService.checkPermission(user, Resource.of(exportData.getEntityType()), Operation.WRITE, existingEntity.getId(), existingEntity); - } else { - exportData.getEntity().setTenantId(user.getTenantId()); - accessControlService.checkPermission(user, Resource.of(exportData.getEntityType()), Operation.CREATE, null, exportData.getEntity()); - } - - List relations = new LinkedList<>(); - if (importSettings.isImportInboundRelations() && exportData.getInboundRelations() != null) { - relations.addAll(exportData.getInboundRelations()); - } - if (importSettings.isImportOutboundRelations() && exportData.getOutboundRelations() != null) { - relations.addAll(exportData.getOutboundRelations()); - } - for (EntityRelation relation : relations) { - EntityId otherEntityId = null; - if (!relation.getFrom().equals(exportData.getEntity().getId())) { - otherEntityId = relation.getFrom(); - } else if (!relation.getTo().equals(exportData.getEntity().getId())) { - otherEntityId = relation.getTo(); - } - if (otherEntityId != null) { - ExportableEntity otherEntity = exportableEntitiesService.findEntityByExternalId(user.getTenantId(), otherEntityId); - if (otherEntity != null) { - checkEntityId(otherEntity.getId(), Operation.WRITE); - } else { - throw new IllegalArgumentException("Relation is referencing non-existing entity"); - } - } - } - } - - return exportImportService.importEntities(user.getTenantId(), exportDataList, importSettings); + return exportImportService.importEntities(user, exportDataList, importSettings); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java index ef9da1f89d..7d98ae9004 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java @@ -15,19 +15,23 @@ */ package org.thingsboard.server.service.sync; +import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; -import org.thingsboard.server.dao.TenantEntityDao; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.permission.AccessControlService; +import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.sync.exporting.EntityExportService; import org.thingsboard.server.service.sync.exporting.EntityExportSettings; import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; @@ -36,7 +40,10 @@ import org.thingsboard.server.service.sync.importing.EntityImportResult; import org.thingsboard.server.service.sync.importing.EntityImportService; import org.thingsboard.server.service.sync.importing.EntityImportSettings; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -45,12 +52,15 @@ import java.util.stream.Collectors; @Service @TbCoreComponent +@RequiredArgsConstructor public class DefaultEntitiesExportImportService implements EntitiesExportImportService, ExportableEntitiesService { private final Map> exportServices = new HashMap<>(); private final Map> importServices = new HashMap<>(); private final Map> daos = new HashMap<>(); + private final AccessControlService accessControlService; + protected static final List SUPPORTED_ENTITY_TYPES = List.of( EntityType.CUSTOMER, EntityType.ASSET, EntityType.RULE_CHAIN, EntityType.DEVICE_PROFILE, EntityType.DEVICE, EntityType.DASHBOARD @@ -58,52 +68,67 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS @Override - public , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId, EntityExportSettings exportSettings) { + public , I extends EntityId> EntityExportData exportEntity(SecurityUser user, I entityId, EntityExportSettings exportSettings) throws ThingsboardException { EntityType entityType = entityId.getEntityType(); EntityExportService> exportService = getExportService(entityType); - return exportService.getExportData(tenantId, entityId, exportSettings); + return exportService.getExportData(user, entityId, exportSettings); } // TODO [viacheslav]: validate export data - @Transactional + @Transactional(rollbackFor = Exception.class) @Override - public , I extends EntityId> EntityImportResult importEntity(TenantId tenantId, EntityExportData exportData, EntityImportSettings importSettings) { + public , I extends EntityId> EntityImportResult importEntity(SecurityUser user, EntityExportData exportData, EntityImportSettings importSettings) throws ThingsboardException { EntityType entityType = exportData.getEntityType(); EntityImportService> importService = getImportService(entityType); - return importService.importEntity(tenantId, exportData, importSettings); + return importService.importEntity(user, exportData, importSettings); } - @Transactional + @Transactional(rollbackFor = Exception.class) @Override - public , I extends EntityId> List> importEntities(TenantId tenantId, List> exportDataList, EntityImportSettings importSettings) { - return exportDataList.stream() - .sorted(Comparator.comparing(exportData -> SUPPORTED_ENTITY_TYPES.indexOf(exportData.getEntityType()))) - // TODO [viacheslav]: order for rule chains (depending on references) - .map(exportData -> importEntity(tenantId, exportData, importSettings)) - .collect(Collectors.toList()); + public List>> importEntities(SecurityUser user, List>> exportDataList, EntityImportSettings importSettings) throws ThingsboardException { + exportDataList.sort(Comparator.comparing(exportData -> SUPPORTED_ENTITY_TYPES.indexOf(exportData.getEntityType()))); + + List>> importResults = new ArrayList<>(); + for (EntityExportData> exportData : exportDataList) { + importResults.add(importEntity(user, exportData, importSettings)); + } + return importResults; } @Override - public , I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId) { + public , I extends EntityId> E findEntityByExternalId(SecurityUser user, I externalId) { EntityType entityType = externalId.getEntityType(); if (SUPPORTED_ENTITY_TYPES.contains(entityType)) { ExportableEntityDao dao = (ExportableEntityDao) getDao(entityType); - E entity = dao.findByTenantIdAndExternalId(tenantId.getId(), externalId.getId()); - if (entity != null) { - return entity; - } + return dao.findByTenantIdAndExternalId(user.getTenantId().getId(), externalId.getId()); } - return findEntityById(tenantId, externalId); + return findEntityById(user, externalId); } @Override - public , I extends EntityId> E findEntityById(TenantId tenantId, I id) { + public , I extends EntityId> E findEntityById(SecurityUser user, I id) { Dao dao = (Dao) getDao(id.getEntityType()); - return dao.findById(tenantId, id.getId()); + return dao.findById(user.getTenantId(), id.getId()); + } + + + @Override + public void checkPermission(SecurityUser user, HasId entity, Operation operation) throws ThingsboardException { + if (entity instanceof HasTenantId) { + accessControlService.checkPermission(user, Resource.of(entity.getId().getEntityType()), operation, entity.getId(), (HasTenantId) entity); + } else if (entity != null) { + accessControlService.checkPermission(user, Resource.of(entity.getId().getEntityType()), operation); + } + } + + @Override + public void checkPermission(SecurityUser user, EntityId entityId, Operation operation) throws ThingsboardException { + HasId entity = findEntityById(user, entityId); + checkPermission(user, entity, operation); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java index 0fbe75a866..447d514c68 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java @@ -16,8 +16,9 @@ package org.thingsboard.server.service.sync; import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.EntityExportSettings; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import org.thingsboard.server.service.sync.importing.EntityImportResult; @@ -27,10 +28,10 @@ import java.util.List; public interface EntitiesExportImportService { - , I extends EntityId> EntityExportData exportEntity(TenantId tenantId, I entityId, EntityExportSettings exportSettings); + , I extends EntityId> EntityExportData exportEntity(SecurityUser user, I entityId, EntityExportSettings exportSettings) throws ThingsboardException; - , I extends EntityId> EntityImportResult importEntity(TenantId tenantId, EntityExportData exportData, EntityImportSettings importSettings); + , I extends EntityId> EntityImportResult importEntity(SecurityUser user, EntityExportData exportData, EntityImportSettings importSettings) throws ThingsboardException; - , I extends EntityId> List> importEntities(TenantId tenantId, List> exportDataList, EntityImportSettings importSettings); + List>> importEntities(SecurityUser user, List>> exportDataList, EntityImportSettings importSettings) throws ThingsboardException; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java index 6b57cbb557..681a398720 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java @@ -17,13 +17,14 @@ package org.thingsboard.server.service.sync.exporting; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; public interface EntityExportService, D extends EntityExportData> { - D getExportData(TenantId tenantId, I entityId, EntityExportSettings exportSettings); + D getExportData(SecurityUser user, I entityId, EntityExportSettings exportSettings) throws ThingsboardException; EntityType getEntityType(); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java index 9a47e98920..c46ea14b10 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java @@ -16,14 +16,22 @@ package org.thingsboard.server.service.sync.exporting; import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; -import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.permission.Operation; public interface ExportableEntitiesService { - , I extends EntityId> E findEntityByExternalId(TenantId tenantId, I externalId); + , I extends EntityId> E findEntityByExternalId(SecurityUser user, I externalId); - , I extends EntityId> E findEntityById(TenantId tenantId, I id); + , I extends EntityId> E findEntityById(SecurityUser user, I id); + + + void checkPermission(SecurityUser user, HasId entity, Operation operation) throws ThingsboardException; + + void checkPermission(SecurityUser user, EntityId entityId, Operation operation) throws ThingsboardException; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java index a3a83196bb..66558634a4 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java @@ -18,14 +18,17 @@ package org.thingsboard.server.service.sync.exporting.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.relation.RelationService; -import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.sync.exporting.EntityExportService; import org.thingsboard.server.service.sync.exporting.EntityExportSettings; +import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import java.util.List; @@ -38,19 +41,31 @@ public abstract class BaseEntityExportService inboundRelations = relationService.findByTo(tenantId, entityId, RelationTypeGroup.COMMON); + List inboundRelations = relationService.findByTo(user.getTenantId(), entityId, RelationTypeGroup.COMMON); + if (inboundRelations != null) { + for (EntityRelation relation : inboundRelations) { + exportableEntitiesService.checkPermission(user, relation.getFrom(), Operation.READ); + } + } exportData.setInboundRelations(inboundRelations); } if (exportSettings.isExportOutboundRelations()) { - List outboundRelations = relationService.findByFrom(tenantId, entityId, RelationTypeGroup.COMMON); + List outboundRelations = relationService.findByFrom(user.getTenantId(), entityId, RelationTypeGroup.COMMON); + if (outboundRelations != null) { + for (EntityRelation relation : outboundRelations) { + exportableEntitiesService.checkPermission(user, relation.getTo(), Operation.READ); + } + } exportData.setOutboundRelations(outboundRelations); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java index 19947e8917..e3e40eea2a 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java @@ -17,13 +17,14 @@ package org.thingsboard.server.service.sync.importing; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; public interface EntityImportService, D extends EntityExportData> { - EntityImportResult importEntity(TenantId tenantId, D exportData, EntityImportSettings importSettings); + EntityImportResult importEntity(SecurityUser user, D exportData, EntityImportSettings importSettings) throws ThingsboardException; EntityType getEntityType(); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java index b9013fd549..326e15a91b 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java @@ -33,9 +33,13 @@ public class AssetImportService extends BaseEntityImportService importEntity(TenantId tenantId, D exportData, EntityImportSettings importSettings) { + public EntityImportResult importEntity(SecurityUser user, D exportData, EntityImportSettings importSettings) throws ThingsboardException { E entity = exportData.getEntity(); - E existingEntity = exportableEntitiesService.findEntityByExternalId(tenantId, entity.getId()); + E existingEntity = exportableEntitiesService.findEntityByExternalId(user, entity.getId()); entity.setExternalId(entity.getId()); + NewIdProvider idProvider = new NewIdProvider(user, entity, existingEntity, importSettings); + setOwner(user.getTenantId(), entity, idProvider); if (existingEntity == null) { entity.setId(null); + exportableEntitiesService.checkPermission(user, entity, Operation.CREATE); } else { entity.setId(existingEntity.getId()); + exportableEntitiesService.checkPermission(user, existingEntity, Operation.WRITE); } - E savedEntity = prepareAndSave(tenantId, entity, exportData, new NewIdProvider(entity, existingEntity, importSettings)); - importRelations(tenantId, savedEntity, existingEntity, exportData, importSettings); + E savedEntity = prepareAndSave(user.getTenantId(), entity, exportData, idProvider); + importRelations(user, savedEntity, existingEntity, exportData, importSettings); EntityImportResult importResult = new EntityImportResult<>(); importResult.setSavedEntity(savedEntity); @@ -73,57 +80,71 @@ public abstract class BaseEntityImportService newRelations = new LinkedList<>(); if (importSettings.isImportInboundRelations() && CollectionUtils.isNotEmpty(exportData.getInboundRelations())) { newRelations.addAll(exportData.getInboundRelations().stream() - .peek(relation -> { - relation.setTo(savedEntity.getId()); - relation.setFrom(getInternalId(tenantId, relation.getFrom())); - }) + .peek(relation -> relation.setTo(savedEntity.getId())) .collect(Collectors.toList())); + if (importSettings.isRemoveExistingRelations() && existingEntity != null) { - relationService.findByTo(tenantId, savedEntity.getId(), RelationTypeGroup.COMMON).forEach(existingRelation -> { - relationService.deleteRelation(tenantId, existingRelation); - }); + for (EntityRelation existingRelation : relationService.findByTo(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)) { + exportableEntitiesService.checkPermission(user, existingRelation.getFrom(), Operation.WRITE); + relationService.deleteRelation(user.getTenantId(), existingRelation); + } } } if (importSettings.isImportOutboundRelations() && CollectionUtils.isNotEmpty(exportData.getOutboundRelations())) { newRelations.addAll(exportData.getOutboundRelations().stream() - .peek(relation -> { - relation.setTo(getInternalId(tenantId, relation.getTo())); - relation.setFrom(savedEntity.getId()); - }) + .peek(relation -> relation.setFrom(savedEntity.getId())) .collect(Collectors.toList())); + if (importSettings.isRemoveExistingRelations() && existingEntity != null) { - relationService.findByFrom(tenantId, savedEntity.getId(), RelationTypeGroup.COMMON).forEach(existingRelation -> { - relationService.deleteRelation(tenantId, existingRelation); - }); + for (EntityRelation existingRelation : relationService.findByFrom(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)) { + exportableEntitiesService.checkPermission(user, existingRelation.getTo(), Operation.WRITE); + relationService.deleteRelation(user.getTenantId(), existingRelation); + } } } - newRelations.forEach(relation -> { - relationService.saveRelation(tenantId, relation); - }); + for (EntityRelation relation : newRelations) { + HasId otherEntity = null; + if (!relation.getTo().equals(savedEntity.getId())) { + otherEntity = findInternalEntity(user, relation.getTo()); + relation.setTo(otherEntity.getId()); + } + if (!relation.getFrom().equals(savedEntity.getId())) { + otherEntity = findInternalEntity(user, relation.getFrom()); + relation.setFrom(otherEntity.getId()); + } + if (otherEntity != null) { + exportableEntitiesService.checkPermission(user, otherEntity, Operation.WRITE); + } + + relationService.saveRelation(user.getTenantId(), relation); + } } - private ID getInternalId(TenantId tenantId, ID externalId) { + private , ID extends EntityId> IE findInternalEntity(SecurityUser user, ID externalId) { if (externalId == null || externalId.isNullUid()) { return null; } - HasId entity = exportableEntitiesService.findEntityByExternalId(tenantId, externalId); + IE entity = exportableEntitiesService.findEntityByExternalId(user, externalId); if (entity == null) { throw new IllegalArgumentException("Cannot find " + externalId.getEntityType() + " by external id " + externalId); } - return entity.getId(); + return entity; } @RequiredArgsConstructor protected class NewIdProvider { + private final SecurityUser user; private final E entity; private final E existingEntity; private final EntityImportSettings importSettings; @@ -132,26 +153,37 @@ public abstract class BaseEntityImportService ID get(TenantId tenantId, Function idExtractor) { + public ID get(Function idExtractor) { if (existingEntity == null || importSettings.isUpdateReferencesToOtherEntities() || ALWAYS_UPDATE_REFERENCED_IDS.contains(getEntityType())) { - return getInternalId(tenantId, idExtractor.apply(entity)); + return getInternalId(idExtractor.apply(this.entity)); } else { return idExtractor.apply(existingEntity); } } - public Set get(TenantId tenantId, Function> listExtractor, Function idGetter, BiConsumer idSetter) { + public Set get(Function> listExtractor, Function idGetter, BiConsumer idSetter) { if (existingEntity == null || importSettings.isUpdateReferencesToOtherEntities()) { return Optional.ofNullable(listExtractor.apply(entity)).orElse(Collections.emptySet()).stream() .peek(t -> { - idSetter.accept(t, getInternalId(tenantId, idGetter.apply(t))); + idSetter.accept(t, getInternalId(idGetter.apply(t))); }) .collect(Collectors.toSet()); } else { return listExtractor.apply(existingEntity); } } + + private ID getInternalId(ID externalId) { + try { + HasId entity = findInternalEntity(user, externalId); + exportableEntitiesService.checkPermission(user, entity, Operation.READ); + return entity.getId(); + } catch (ThingsboardException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } + } + } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java index 92f7371d50..d17e6c8b41 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java @@ -33,8 +33,12 @@ public class CustomerImportService extends BaseEntityImportService assignedCustomers = idProvider.get(tenantId, Dashboard::getAssignedCustomers, ShortCustomerInfo::getCustomerId, ShortCustomerInfo::setCustomerId); + Set assignedCustomers = idProvider.get(Dashboard::getAssignedCustomers, ShortCustomerInfo::getCustomerId, ShortCustomerInfo::setCustomerId); dashboard.setAssignedCustomers(null); dashboard = dashboardService.saveDashboard(dashboard); for (ShortCustomerInfo customerInfo : assignedCustomers) { @@ -54,7 +58,7 @@ public class DashboardImportService extends BaseEntityImportService existingAssignedCustomers = Optional.ofNullable(dashboardService.findDashboardById(tenantId, dashboard.getId()).getAssignedCustomers()) .orElse(Collections.emptySet()).stream().map(ShortCustomerInfo::getCustomerId).collect(Collectors.toSet()); - Set newAssignedCustomers = idProvider.get(tenantId, Dashboard::getAssignedCustomers, ShortCustomerInfo::getCustomerId, ShortCustomerInfo::setCustomerId).stream() + Set newAssignedCustomers = idProvider.get(Dashboard::getAssignedCustomers, ShortCustomerInfo::getCustomerId, ShortCustomerInfo::setCustomerId).stream() .map(ShortCustomerInfo::getCustomerId).collect(Collectors.toSet()); Set toUnassign = new HashSet<>(existingAssignedCustomers); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java index f35a20e312..3b844f8657 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java @@ -33,12 +33,16 @@ public class DeviceImportService extends BaseEntityImportService { @@ -54,18 +58,19 @@ public class RuleChainImportService extends BaseEntityImportService { ((ObjectNode) ruleNodeConfig).set("ruleChainId", new TextNode( - idProvider.get(tenantId, rc -> new RuleChainId(otherRuleChainUuid)).toString() + idProvider.get(rc -> new RuleChainId(otherRuleChainUuid)).toString() )); ruleNode.setConfiguration(ruleNodeConfig); }); }); Optional.ofNullable(metaData.getRuleChainConnections()).orElse(Collections.emptyList()) .forEach(ruleChainConnectionInfo -> { - ruleChainConnectionInfo.setTargetRuleChainId(idProvider.get(tenantId, rc -> ruleChainConnectionInfo.getTargetRuleChainId())); + ruleChainConnectionInfo.setTargetRuleChainId(idProvider.get(rc -> ruleChainConnectionInfo.getTargetRuleChainId())); }); ruleChain.setFirstRuleNodeId(null); if (ruleChain.getId() != null) { + // FIXME [viacheslav]: maybe no need to delete ruleChainService.deleteRuleNodes(tenantId, ruleChain.getId()); } ruleChain = ruleChainService.saveRuleChain(ruleChain); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java index cdf04f47c5..8e165898a2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java @@ -21,6 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmQuery; @@ -188,4 +189,10 @@ public class JpaAlarmDao extends JpaAbstractDao implements A log.trace("[{}] Try to delete entity alarm records using [{}]", tenantId, entityId); entityAlarmRepository.deleteByEntityId(entityId.getId()); } + + @Override + public EntityType getEntityType() { + return EntityType.ALARM; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java index 4be9c08849..802c9b99b8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/edge/JpaEdgeDao.java @@ -193,4 +193,9 @@ public class JpaEdgeDao extends JpaAbstractSearchTextDao imple return list; } + @Override + public EntityType getEntityType() { + return EntityType.EDGE; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java index 8c515f13c0..52371706e3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/entityview/JpaEntityViewDao.java @@ -200,4 +200,10 @@ public class JpaEntityViewDao extends JpaAbstractSearchTextDao implements RpcDao public Long deleteOutdatedRpcByTenantId(TenantId tenantId, Long expirationTime) { return rpcRepository.deleteOutdatedRpcByTenantId(tenantId.getId(), expirationTime); } + + @Override + public EntityType getEntityType() { + return EntityType.RPC; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java index a499e68da8..d49b54434f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleNodeDao.java @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.dao.DaoUtil; @@ -50,4 +51,10 @@ public class JpaRuleNodeDao extends JpaAbstractSearchTextDao findRuleNodesByTenantIdAndType(TenantId tenantId, String type, String search) { return DaoUtil.convertDataList(ruleNodeRepository.findRuleNodesByTenantIdAndType(tenantId.getId(), type, search)); } + + @Override + public EntityType getEntityType() { + return EntityType.RULE_NODE; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java index b985d45e8c..56a75f4d3b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sql.tenant; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantInfo; import org.thingsboard.server.common.data.id.TenantId; @@ -78,4 +79,9 @@ public class JpaTenantDao extends JpaAbstractSearchTextDao return DaoUtil.pageToPageData(tenantRepository.findTenantsIds(DaoUtil.toPageable(pageLink))).mapData(TenantId::fromUUID); } + @Override + public EntityType getEntityType() { + return EntityType.TENANT; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/JpaApiUsageStateDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/JpaApiUsageStateDao.java index 7c6801042d..96a0ecaede 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/JpaApiUsageStateDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/usagerecord/JpaApiUsageStateDao.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sql.usagerecord; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.ApiUsageState; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.DaoUtil; @@ -68,4 +69,10 @@ public class JpaApiUsageStateDao extends JpaAbstractDao imple public Long countByTenantId(TenantId tenantId) { return userRepository.countByTenantId(tenantId.getId()); } + + @Override + public EntityType getEntityType() { + return EntityType.USER; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java index bbaf2529a7..eac0a15b1e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/widget/JpaWidgetTypeDao.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sql.widget; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; @@ -73,4 +74,10 @@ public class JpaWidgetTypeDao extends JpaAbstractDao Date: Tue, 5 Apr 2022 17:12:51 +0300 Subject: [PATCH 15/39] Add option to find existing entity by name when importing; refactoring --- .../EntitiesExportImportController.java | 35 ++++++----- .../DefaultEntitiesExportImportService.java | 35 ++++++----- .../exporting/ExportableEntitiesService.java | 11 ++-- .../impl/BaseEntityExportService.java | 7 ++- .../sync/importing/EntityImportSettings.java | 1 + .../impl/BaseEntityImportService.java | 58 ++++++++++++------- .../server/dao/ExportableEntityDao.java | 2 + .../server/dao/asset/BaseAssetService.java | 2 + .../server/dao/sql/asset/JpaAssetDao.java | 5 ++ .../dao/sql/customer/JpaCustomerDao.java | 5 ++ .../sql/dashboard/DashboardRepository.java | 2 + .../dao/sql/dashboard/JpaDashboardDao.java | 5 ++ .../server/dao/sql/device/JpaDeviceDao.java | 5 ++ .../dao/sql/device/JpaDeviceProfileDao.java | 5 ++ .../server/dao/sql/rule/JpaRuleChainDao.java | 5 ++ .../dao/sql/rule/RuleChainRepository.java | 2 + 16 files changed, 127 insertions(+), 58 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index f4abd1b5f7..25d48c54a7 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -29,7 +29,6 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.query.EntityData; import org.thingsboard.server.common.data.query.EntityDataPageLink; import org.thingsboard.server.common.data.query.EntityDataQuery; @@ -38,23 +37,18 @@ import org.thingsboard.server.common.data.query.EntityFilter; import org.thingsboard.server.common.data.query.EntityKey; import org.thingsboard.server.common.data.query.EntityKeyType; import org.thingsboard.server.common.data.query.EntityTypeFilter; -import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.EntitiesExportImportService; -import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; import org.thingsboard.server.service.sync.exporting.EntityExportSettings; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import org.thingsboard.server.service.sync.importing.EntityImportResult; import org.thingsboard.server.service.sync.importing.EntityImportSettings; -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.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; @@ -72,7 +66,6 @@ import static org.thingsboard.server.dao.sql.query.EntityKeyMapping.CREATED_TIME public class EntitiesExportImportController extends BaseController { private final EntitiesExportImportService exportImportService; - private final ExportableEntitiesService exportableEntitiesService; private final EntityService entityService; @@ -201,8 +194,8 @@ public class EntitiesExportImportController extends BaseController { @PostMapping("/import") - public List>> importEntity(@RequestBody List>> exportDataList, - @RequestParam Map importSettingsParams) throws ThingsboardException { + public List>> importEntities(@RequestBody List>> exportDataList, + @RequestParam Map importSettingsParams) throws ThingsboardException { SecurityUser user = getCurrentUser(); EntityImportSettings importSettings = toImportSettings(importSettingsParams); @@ -225,25 +218,31 @@ public class EntitiesExportImportController extends BaseController { private EntityExportSettings toExportSettings(Map exportSettingsParams) { return EntityExportSettings.builder() - .exportInboundRelations(getParam(exportSettingsParams, "exportInboundRelations", false, Boolean::parseBoolean)) - .exportOutboundRelations(getParam(exportSettingsParams, "exportOutboundRelations", false, Boolean::parseBoolean)) + .exportInboundRelations(getBooleanParam(exportSettingsParams, "exportInboundRelations", false)) + .exportOutboundRelations(getBooleanParam(exportSettingsParams, "exportOutboundRelations", false)) .build(); } private EntityImportSettings toImportSettings(Map importSettingsParams) { return EntityImportSettings.builder() - .importInboundRelations(getParam(importSettingsParams, "importInboundRelations", false, Boolean::parseBoolean)) - .importOutboundRelations(getParam(importSettingsParams, "importOutboundRelations", false, Boolean::parseBoolean)) - .removeExistingRelations(getParam(importSettingsParams, "removeExistingRelations", true, Boolean::parseBoolean)) - .updateReferencesToOtherEntities(getParam(importSettingsParams, "updateReferencesToOtherEntities", true, Boolean::parseBoolean)) + .findExistingByName(getBooleanParam(importSettingsParams, "findExistingByName", false)) + .importInboundRelations(getBooleanParam(importSettingsParams, "importInboundRelations", false)) + .importOutboundRelations(getBooleanParam(importSettingsParams, "importOutboundRelations", false)) + .removeExistingRelations(getBooleanParam(importSettingsParams, "removeExistingRelations", true)) + .updateReferencesToOtherEntities(getBooleanParam(importSettingsParams, "updateReferencesToOtherEntities", true)) .build(); } - protected T getParam(Map requestParams, String key, T defaultValue, Function parsingFunction) { + + protected static boolean getBooleanParam(Map requestParams, String key, boolean defaultValue) { + return getParam(requestParams, key, defaultValue, Boolean::parseBoolean); + } + + protected static T getParam(Map requestParams, String key, T defaultValue, Function parsingFunction) { return parsingFunction.apply(requestParams.getOrDefault(key, defaultValue.toString())); } - private CustomerId toCustomerId(UUID customerUuid) { + private static CustomerId toCustomerId(UUID customerUuid) { return new CustomerId(Optional.ofNullable(customerUuid).orElse(EntityId.NULL_UUID)); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java index 7d98ae9004..8bfaa9fe80 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -41,14 +42,11 @@ import org.thingsboard.server.service.sync.importing.EntityImportService; import org.thingsboard.server.service.sync.importing.EntityImportSettings; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; @Service @TbCoreComponent @@ -100,35 +98,46 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS @Override - public , I extends EntityId> E findEntityByExternalId(SecurityUser user, I externalId) { + public , I extends EntityId> E findEntityByTenantIdAndExternalId(TenantId tenantId, I externalId) { EntityType entityType = externalId.getEntityType(); if (SUPPORTED_ENTITY_TYPES.contains(entityType)) { ExportableEntityDao dao = (ExportableEntityDao) getDao(entityType); - return dao.findByTenantIdAndExternalId(user.getTenantId().getId(), externalId.getId()); + return dao.findByTenantIdAndExternalId(tenantId.getId(), externalId.getId()); + } else { + return null; } - return findEntityById(user, externalId); } @Override - public , I extends EntityId> E findEntityById(SecurityUser user, I id) { + public , I extends EntityId> E findEntityByTenantIdAndId(TenantId tenantId, I id) { Dao dao = (Dao) getDao(id.getEntityType()); - return dao.findById(user.getTenantId(), id.getId()); + E entity = dao.findById(tenantId, id.getId()); + if (entity instanceof HasTenantId && !((HasTenantId) entity).getTenantId().equals(tenantId)) { + return null; + } + return entity; + } + + @Override + public , I extends EntityId> E findEntityByTenantIdAndName(TenantId tenantId, EntityType entityType, String name) { + ExportableEntityDao dao = (ExportableEntityDao) getDao(entityType); + return dao.findFirstByTenantIdAndName(tenantId.getId(), name); } @Override - public void checkPermission(SecurityUser user, HasId entity, Operation operation) throws ThingsboardException { + public void checkPermission(SecurityUser user, HasId entity, EntityType entityType, Operation operation) throws ThingsboardException { if (entity instanceof HasTenantId) { - accessControlService.checkPermission(user, Resource.of(entity.getId().getEntityType()), operation, entity.getId(), (HasTenantId) entity); + accessControlService.checkPermission(user, Resource.of(entityType), operation, entity.getId(), (HasTenantId) entity); } else if (entity != null) { - accessControlService.checkPermission(user, Resource.of(entity.getId().getEntityType()), operation); + accessControlService.checkPermission(user, Resource.of(entityType), operation); } } @Override public void checkPermission(SecurityUser user, EntityId entityId, Operation operation) throws ThingsboardException { - HasId entity = findEntityById(user, entityId); - checkPermission(user, entity, operation); + HasId entity = findEntityByTenantIdAndId(user.getTenantId(), entityId); + checkPermission(user, entity, entityId.getEntityType(), operation); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java index c46ea14b10..5cf31042e0 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java @@ -15,22 +15,25 @@ */ package org.thingsboard.server.service.sync.exporting; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; -import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; public interface ExportableEntitiesService { - , I extends EntityId> E findEntityByExternalId(SecurityUser user, I externalId); + , I extends EntityId> E findEntityByTenantIdAndExternalId(TenantId tenantId, I externalId); - , I extends EntityId> E findEntityById(SecurityUser user, I id); + , I extends EntityId> E findEntityByTenantIdAndId(TenantId tenantId, I id); + , I extends EntityId> E findEntityByTenantIdAndName(TenantId tenantId, EntityType entityType, String name); - void checkPermission(SecurityUser user, HasId entity, Operation operation) throws ThingsboardException; + + void checkPermission(SecurityUser user, HasId entity, EntityType entityType, Operation operation) throws ThingsboardException; void checkPermission(SecurityUser user, EntityId entityId, Operation operation) throws ThingsboardException; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java index 66558634a4..f098485fd6 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java @@ -44,8 +44,11 @@ public abstract class BaseEntityExportService importEntity(SecurityUser user, D exportData, EntityImportSettings importSettings) throws ThingsboardException { E entity = exportData.getEntity(); - E existingEntity = exportableEntitiesService.findEntityByExternalId(user, entity.getId()); + E existingEntity = findExistingEntity(user.getTenantId(), entity, importSettings); entity.setExternalId(entity.getId()); @@ -65,10 +65,10 @@ public abstract class BaseEntityImportService Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndId(tenantId, entity.getId()))) + .or(() -> { + if (importSettings.isFindExistingByName()) { + return Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndName(tenantId, getEntityType(), entity.getName())); + } else { + return Optional.empty(); + } + }) + .orElse(null); + } + + private HasId findInternalEntity(TenantId tenantId, ID externalId) { + if (externalId == null || externalId.isNullUid()) return null; + + return (HasId) Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndExternalId(tenantId, externalId)) + .or(() -> Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndId(tenantId, externalId))) + .orElseThrow(() -> new IllegalArgumentException("Cannot find " + externalId.getEntityType() + " by external id " + externalId)); + } + + private void importRelations(SecurityUser user, E savedEntity, E existingEntity, D exportData, EntityImportSettings importSettings) throws ThingsboardException { List newRelations = new LinkedList<>(); @@ -116,31 +138,21 @@ public abstract class BaseEntityImportService otherEntity = null; if (!relation.getTo().equals(savedEntity.getId())) { - otherEntity = findInternalEntity(user, relation.getTo()); + otherEntity = findInternalEntity(user.getTenantId(), relation.getTo()); relation.setTo(otherEntity.getId()); } if (!relation.getFrom().equals(savedEntity.getId())) { - otherEntity = findInternalEntity(user, relation.getFrom()); + otherEntity = findInternalEntity(user.getTenantId(), relation.getFrom()); relation.setFrom(otherEntity.getId()); } if (otherEntity != null) { - exportableEntitiesService.checkPermission(user, otherEntity, Operation.WRITE); + exportableEntitiesService.checkPermission(user, otherEntity, otherEntity.getId().getEntityType(), Operation.WRITE); } relationService.saveRelation(user.getTenantId(), relation); } } - private , ID extends EntityId> IE findInternalEntity(SecurityUser user, ID externalId) { - if (externalId == null || externalId.isNullUid()) { - return null; - } - IE entity = exportableEntitiesService.findEntityByExternalId(user, externalId); - if (entity == null) { - throw new IllegalArgumentException("Cannot find " + externalId.getEntityType() + " by external id " + externalId); - } - return entity; - } @RequiredArgsConstructor protected class NewIdProvider { @@ -175,12 +187,16 @@ public abstract class BaseEntityImportService ID getInternalId(ID externalId) { - try { - HasId entity = findInternalEntity(user, externalId); - exportableEntitiesService.checkPermission(user, entity, Operation.READ); + HasId entity = findInternalEntity(user.getTenantId(), externalId); + if (entity != null) { + try { + exportableEntitiesService.checkPermission(user, entity, entity.getId().getEntityType(), Operation.READ); + } catch (ThingsboardException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } return entity.getId(); - } catch (ThingsboardException e) { - throw new IllegalArgumentException(e.getMessage(), e); + } else { + return null; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java index 7e4d00286c..467389d9d0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java @@ -23,4 +23,6 @@ public interface ExportableEntityDao> extends Dao< T findByTenantIdAndExternalId(UUID tenantId, UUID externalId); + T findFirstByTenantIdAndName(UUID tenantId, String name); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index bf8d75d27a..4f31f3e8c1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired; 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.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; @@ -110,6 +111,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ .orElse(null); } + @Transactional @CacheEvict(cacheNames = ASSET_CACHE, key = "{#asset.tenantId, #asset.name}") @Override public Asset saveAsset(Asset asset) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java index 14788f8b38..5988c2d2db 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java @@ -214,6 +214,11 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao im return DaoUtil.getData(assetRepository.findByTenantIdAndExternalId(tenantId, externalId)); } + @Override + public Asset findFirstByTenantIdAndName(UUID tenantId, String name) { + return findAssetsByTenantIdAndName(tenantId, name).orElse(null); + } + @Override public EntityType getEntityType() { return EntityType.ASSET; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java index 17e1625a29..fa09304427 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java @@ -75,6 +75,11 @@ public class JpaCustomerDao extends JpaAbstractSearchTextDao return DaoUtil.getData(deviceRepository.findByTenantIdAndExternalId(tenantId, externalId)); } + @Override + public Device findFirstByTenantIdAndName(UUID tenantId, String name) { + return findDeviceByTenantIdAndName(tenantId, name).orElse(null); + } + @Override public EntityType getEntityType() { return EntityType.DEVICE; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java index 23e564b735..b21a07442f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java @@ -116,6 +116,11 @@ public class JpaDeviceProfileDao extends JpaAbstractSearchTextDao findByTenantIdAndTypeAndName(UUID tenantId, RuleChainType type, String name); + RuleChainEntity findFirstByTenantIdAndName(UUID tenantId, String name); + } From cecf49a67dc60119ad9cd5b65fd49240d5ef5447 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Tue, 5 Apr 2022 18:08:26 +0300 Subject: [PATCH 16/39] Fix dashboard import to update entity aliases --- .../DefaultEntitiesExportImportService.java | 7 +++- .../impl/DashboardImportService.java | 33 ++++++++++++++++++- .../importing/impl/DeviceImportService.java | 2 +- .../server/dao/asset/BaseAssetService.java | 2 -- .../query/DefaultEntityQueryRepository.java | 2 +- 5 files changed, 40 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java index 8bfaa9fe80..90f6f1c778 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java @@ -28,6 +28,7 @@ import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; +import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.AccessControlService; @@ -74,13 +75,17 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS } - // TODO [viacheslav]: validate export data @Transactional(rollbackFor = Exception.class) @Override public , I extends EntityId> EntityImportResult importEntity(SecurityUser user, EntityExportData exportData, EntityImportSettings importSettings) throws ThingsboardException { + if (exportData.getEntity() == null || exportData.getEntity().getId() == null) { + throw new DataValidationException("Invalid entity data"); + } + EntityType entityType = exportData.getEntityType(); EntityImportService> importService = getImportService(entityType); + // TODO [viacheslav]: might throw DataIntegrityViolationException with cause of ConstraintViolationException: need to give normal error return importService.importEntity(user, exportData, importSettings); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java index 7d0ff17ead..67f4ffc4a7 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java @@ -15,15 +15,22 @@ */ package org.thingsboard.server.service.sync.importing.impl; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ShortCustomerInfo; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.query.EntityFilter; import org.thingsboard.server.dao.dashboard.DashboardService; +import org.thingsboard.server.dao.sql.query.DefaultEntityQueryRepository; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.sync.exporting.data.DashboardExportData; @@ -31,6 +38,8 @@ import java.util.Collections; import java.util.HashSet; import java.util.Optional; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; @Service @@ -40,14 +49,36 @@ public class DashboardImportService extends BaseEntityImportService Optional.ofNullable(configuration.get("entityAliases"))) + .filter(JsonNode::isObject) + .ifPresent(entityAliases -> entityAliases.forEach(entityAlias -> { + Optional.ofNullable(entityAlias.get("filter")) + .filter(JsonNode::isObject) + .ifPresent(filter -> { + EntityFilter entityFilter = JacksonUtil.treeToValue(filter, EntityFilter.class); + EntityType entityType = DefaultEntityQueryRepository.resolveEntityType(entityFilter); + + String filterJson = filter.toString(); + String newFilterJson = UUID_PATTERN.matcher(filterJson).replaceAll(matchResult -> { + String uuid = matchResult.group(); + EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, uuid); + return idProvider.get(d -> entityId).toString(); + }); + ((ObjectNode) entityAlias).set("filter", JacksonUtil.toJsonNode(newFilterJson)); + }); + })); + + // TODO [viacheslav]: improve the code below if (dashboard.getId() == null) { Set assignedCustomers = idProvider.get(Dashboard::getAssignedCustomers, ShortCustomerInfo::getCustomerId, ShortCustomerInfo::setCustomerId); dashboard.setAssignedCustomers(null); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java index 3b844f8657..ccff72dcdb 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java @@ -47,7 +47,7 @@ public class DeviceImportService extends BaseEntityImportService Date: Wed, 6 Apr 2022 00:29:22 +0300 Subject: [PATCH 17/39] Refactoring --- .../server/controller/AssetController.java | 6 +- .../server/controller/BaseController.java | 66 ------------------- .../server/controller/CustomerController.java | 10 ++- .../controller/DashboardController.java | 4 +- .../server/controller/DeviceController.java | 14 ++-- .../controller/DeviceProfileController.java | 25 ++++++- .../EntitiesExportImportController.java | 12 ++-- .../controller/RuleChainController.java | 6 +- .../service/action/EntityActionService.java | 59 ++++++++++++++++- .../impl/BaseEntityExportService.java | 15 +++-- .../sync/importing/EntityImportResult.java | 5 ++ .../importing/impl/AssetImportService.java | 9 +++ .../impl/BaseEntityImportService.java | 65 ++++++++++-------- .../importing/impl/CustomerImportService.java | 11 ++++ .../impl/DashboardImportService.java | 11 ++++ .../importing/impl/DeviceImportService.java | 13 ++++ .../impl/DeviceProfileImportService.java | 9 +++ .../impl/RuleChainImportService.java | 9 +++ .../server/utils/ThrowingRunnable.java | 24 +++++++ 19 files changed, 245 insertions(+), 128 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/utils/ThrowingRunnable.java diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index b05e32efe7..5e44e1433a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -155,9 +155,7 @@ public class AssetController extends BaseController { checkEntity(asset.getId(), asset, Resource.ASSET); Asset savedAsset = checkNotNull(assetService.saveAsset(asset)); - - onEntityUpdatedOrCreated(getCurrentUser(), savedAsset, null, asset.getId() == null); - + entityActionService.onAssetCreatedOrUpdated(getCurrentUser(), savedAsset, asset.getId() == null); return savedAsset; } catch (Exception e) { logEntityAction(emptyId(EntityType.ASSET), asset, @@ -667,7 +665,7 @@ public class AssetController extends BaseController { public BulkImportResult processAssetsBulkImport(@RequestBody BulkImportRequest request) throws Exception { SecurityUser user = getCurrentUser(); return assetBulkImportService.processBulkImport(request, user, importedAssetInfo -> { - onEntityUpdatedOrCreated(user, importedAssetInfo.getEntity(), importedAssetInfo.getOldEntity(), !importedAssetInfo.isUpdated()); + entityActionService.onAssetCreatedOrUpdated(user, importedAssetInfo.getEntity(), !importedAssetInfo.isUpdated()); }); } diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 1f683c7874..f945cc30b7 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -37,7 +37,6 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; 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.HasTenantId; import org.thingsboard.server.common.data.OtaPackage; @@ -69,7 +68,6 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; 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.RpcId; import org.thingsboard.server.common.data.id.RuleChainId; @@ -85,7 +83,6 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.TimePageLink; 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.relation.EntityRelation; import org.thingsboard.server.common.data.rpc.Rpc; @@ -144,7 +141,6 @@ import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; @@ -923,66 +919,4 @@ public abstract class BaseController { return MediaType.APPLICATION_OCTET_STREAM; } } - - protected , I extends EntityId> void onEntityUpdatedOrCreated(User user, E savedEntity, E oldEntity, boolean isNewEntity) { - boolean notifyEdgeService = 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); - notifyEdgeService = true; - break; - case RULE_CHAIN: - RuleChainType ruleChainType = ((RuleChain) savedEntity).getType(); - if (RuleChainType.CORE.equals(ruleChainType)) { - tbClusterService.broadcastEntityStateChangeEvent(user.getTenantId(), savedEntity.getId(), - isNewEntity ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); - } - if (RuleChainType.EDGE.equals(ruleChainType)) { - if (!isNewEntity) { - notifyEdgeService = true; - } - } - break; - case ASSET: - case CUSTOMER: - case DASHBOARD: - if (!isNewEntity) { - notifyEdgeService = true; - } - break; - default: - throw new UnsupportedOperationException(); - } - - try { - logEntityAction(user, savedEntity.getId(), savedEntity, savedEntity instanceof HasCustomerId ? ((HasCustomerId) savedEntity).getCustomerId() : null, - isNewEntity ? ActionType.ADDED : ActionType.UPDATED, null); - } catch (ThingsboardException e) { - log.error("Failed to log entity action", e); - } - if (notifyEdgeService) { - sendEntityNotificationMsg(user.getTenantId(), savedEntity.getId(), isNewEntity ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); - } - } - } diff --git a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java index 27037f36e7..b419121459 100644 --- a/application/src/main/java/org/thingsboard/server/controller/CustomerController.java +++ b/application/src/main/java/org/thingsboard/server/controller/CustomerController.java @@ -33,6 +33,7 @@ import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; 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.id.CustomerId; import org.thingsboard.server.common.data.id.EdgeId; @@ -150,7 +151,14 @@ public class CustomerController extends BaseController { checkEntity(customer.getId(), customer, Resource.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; } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index 4ea1ca83c7..b4b6d6be89 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -186,9 +186,7 @@ public class DashboardController extends BaseController { checkEntity(dashboard.getId(), dashboard, Resource.DASHBOARD); Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard)); - - onEntityUpdatedOrCreated(getCurrentUser(), savedDashboard, null, dashboard.getId() == null); - + entityActionService.onDashboardCreatedOrUpdated(getCurrentUser(), savedDashboard, dashboard.getId() == null); return savedDashboard; } catch (Exception e) { logEntityAction(emptyId(EntityType.DASHBOARD), dashboard, diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index feb58edb7a..2dff62aec6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -74,11 +74,11 @@ import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.device.DeviceBulkImportService; import org.thingsboard.server.service.gateway_device.GatewayNotificationsService; -import org.thingsboard.server.service.sync.importing.csv.BulkImportRequest; -import org.thingsboard.server.service.sync.importing.csv.BulkImportResult; 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 org.thingsboard.server.service.sync.importing.csv.BulkImportRequest; +import org.thingsboard.server.service.sync.importing.csv.BulkImportResult; import javax.annotation.Nullable; import java.io.IOException; @@ -194,9 +194,7 @@ public class DeviceController extends BaseController { } Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken)); - - onEntityUpdatedOrCreated(getCurrentUser(), savedDevice, oldDevice, created); - + entityActionService.onDeviceCreatedOrUpdated(getCurrentUser(), savedDevice, oldDevice, created); return savedDevice; } catch (Exception e) { logEntityAction(emptyId(EntityType.DEVICE), device, @@ -224,9 +222,7 @@ public class DeviceController extends BaseController { checkEntity(device.getId(), device, Resource.DEVICE); Device savedDevice = deviceService.saveDeviceWithCredentials(device, credentials); checkNotNull(savedDevice); - - onEntityUpdatedOrCreated(getCurrentUser(), savedDevice, device, device.getId() == null); - + entityActionService.onDeviceCreatedOrUpdated(getCurrentUser(), savedDevice, device, created); return savedDevice; } catch (Exception e) { logEntityAction(emptyId(EntityType.DEVICE), device, @@ -1001,7 +997,7 @@ public class DeviceController extends BaseController { public BulkImportResult processDevicesBulkImport(@RequestBody BulkImportRequest request) throws Exception { SecurityUser user = getCurrentUser(); return deviceBulkImportService.processBulkImport(request, user, importedDeviceInfo -> { - onEntityUpdatedOrCreated(user, importedDeviceInfo.getEntity(), importedDeviceInfo.getOldEntity(), !importedDeviceInfo.isUpdated()); + entityActionService.onDeviceCreatedOrUpdated(user, importedDeviceInfo.getEntity(), importedDeviceInfo.getOldEntity(), !importedDeviceInfo.isUpdated()); }); } diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java index 240e60be2c..25a35e77fa 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java @@ -46,6 +46,7 @@ import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import java.util.List; +import java.util.Objects; import java.util.UUID; import static org.thingsboard.server.controller.ControllerConstants.DEVICE_PROFILE_DATA; @@ -206,14 +207,32 @@ public class DeviceProfileController extends BaseController { checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE); - DeviceProfile oldDeviceProfile = null; + boolean isFirmwareChanged = false; + boolean isSoftwareChanged = false; + if (!created) { - oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId()); + DeviceProfile 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)); - onEntityUpdatedOrCreated(getCurrentUser(), deviceProfile, oldDeviceProfile, created); + tbClusterService.onDeviceProfileChange(savedDeviceProfile, null); + 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; } catch (Exception e) { logEntityAction(emptyId(EntityType.DEVICE_PROFILE), deviceProfile, diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index 25d48c54a7..6d6a2e4619 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -200,11 +200,13 @@ public class EntitiesExportImportController extends BaseController { EntityImportSettings importSettings = toImportSettings(importSettingsParams); try { - return importEntities(user, exportDataList, importSettings) - .stream().peek(entityImportResult -> { - onEntityUpdatedOrCreated(user, entityImportResult.getSavedEntity(), entityImportResult.getOldEntity(), entityImportResult.getOldEntity() == null); - }) - .collect(Collectors.toList()); + List>> importResults = importEntities(user, exportDataList, importSettings); + for (EntityImportResult> entityImportResult : importResults) { + if (entityImportResult.getCallback() != null) { + entityImportResult.getCallback().run(); + } + } + return importResults; } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index cb59951ac5..e27090c700 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -78,9 +78,11 @@ import org.thingsboard.server.service.security.permission.Resource; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeSet; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -247,12 +249,10 @@ public class RuleChainController extends BaseController { try { boolean created = ruleChain.getId() == null; ruleChain.setTenantId(getCurrentUser().getTenantId()); - checkEntity(ruleChain.getId(), ruleChain, Resource.RULE_CHAIN); RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain)); - - onEntityUpdatedOrCreated(getCurrentUser(), savedRuleChain, null, created); + entityActionService.onRuleChainCreatedOrUpdated(getCurrentUser(), savedRuleChain, created); return savedRuleChain; } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java index b8c0656bd2..d692e24bbe 100644 --- a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java +++ b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java @@ -22,12 +22,17 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.Device; 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.User; +import org.thingsboard.server.common.data.asset.Asset; 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.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; @@ -36,13 +41,20 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.DataType; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.rule.RuleChainType; +import org.thingsboard.server.common.data.rule.RuleChainUpdateResult; 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.audit.AuditLogService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.service.security.model.SecurityUser; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -212,7 +224,7 @@ public class EntityActionService { } } - public void logEntityAction(User user, I entityId, E entity, CustomerId customerId, + public void logEntityAction(User user, I entityId, E entity, CustomerId customerId, ActionType actionType, Exception e, Object... additionalInfo) { if (customerId == null || customerId.isNullUid()) { customerId = user.getCustomerId(); @@ -267,4 +279,49 @@ public class EntityActionService { entityNode.put(kvEntry.getKey(), kvEntry.getValueAsString()); } } + + + public void onDeviceCreatedOrUpdated(SecurityUser user, Device savedDevice, Device oldDevice, boolean newEntity) { + tbClusterService.onDeviceUpdated(savedDevice, oldDevice); + logEntityAction(user, savedDevice.getId(), savedDevice, savedDevice.getCustomerId(), + newEntity ? ActionType.ADDED : ActionType.UPDATED, null); + } + + public void onAssetCreatedOrUpdated(SecurityUser user, Asset savedAsset, boolean newEntity) { + logEntityAction(user, savedAsset.getId(), savedAsset, savedAsset.getCustomerId(), + newEntity ? ActionType.ADDED : ActionType.UPDATED, null); + if (!newEntity) { + tbClusterService.sendNotificationMsgToEdgeService(user.getTenantId(), null, savedAsset.getId(), + null, null, EdgeEventActionType.UPDATED); + } + } + + public void onDashboardCreatedOrUpdated(SecurityUser user, Dashboard savedDashboard, boolean newEntity) { + logEntityAction(user, savedDashboard.getId(), savedDashboard, null, + newEntity ? ActionType.ADDED : ActionType.UPDATED, null); + if (!newEntity) { + tbClusterService.sendNotificationMsgToEdgeService(user.getTenantId(), null, savedDashboard.getId(), + null, null, EdgeEventActionType.UPDATED); + } + } + + public void onRuleChainCreatedOrUpdated(SecurityUser user, RuleChain savedRuleChain, boolean newEntity) { + if (RuleChainType.CORE.equals(savedRuleChain.getType())) { + tbClusterService.broadcastEntityStateChangeEvent(user.getTenantId(), savedRuleChain.getId(), + newEntity ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); + } + logEntityAction(user, savedRuleChain.getId(), savedRuleChain, null, + newEntity ? ActionType.ADDED : ActionType.UPDATED, null); + if (RuleChainType.EDGE.equals(savedRuleChain.getType())) { + if (!newEntity) { + tbClusterService.sendNotificationMsgToEdgeService(user.getTenantId(), null, savedRuleChain.getId(), + null, null, EdgeEventActionType.UPDATED); + } + } + } + + public void onCustomerCreatedOrUpdated(SecurityUser user, Customer savedCustomer) { + + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java index f098485fd6..772e010b2b 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java @@ -52,9 +52,16 @@ public abstract class BaseEntityExportService inboundRelations = relationService.findByTo(user.getTenantId(), entityId, RelationTypeGroup.COMMON); + List inboundRelations = relationService.findByTo(user.getTenantId(), entity.getId(), RelationTypeGroup.COMMON); if (inboundRelations != null) { for (EntityRelation relation : inboundRelations) { exportableEntitiesService.checkPermission(user, relation.getFrom(), Operation.READ); @@ -63,7 +70,7 @@ public abstract class BaseEntityExportService outboundRelations = relationService.findByFrom(user.getTenantId(), entityId, RelationTypeGroup.COMMON); + List outboundRelations = relationService.findByFrom(user.getTenantId(), entity.getId(), RelationTypeGroup.COMMON); if (outboundRelations != null) { for (EntityRelation relation : outboundRelations) { exportableEntitiesService.checkPermission(user, relation.getTo(), Operation.READ); @@ -71,12 +78,8 @@ public abstract class BaseEntityExportService> { private E savedEntity; private E oldEntity; + @JsonIgnore + private transient ThrowingRunnable callback; // to call when entity is successfully saved and transaction is committed } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java index 326e15a91b..1c031f50e4 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java @@ -23,7 +23,9 @@ import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.AssetExportData; +import org.thingsboard.server.utils.ThrowingRunnable; @Service @TbCoreComponent @@ -43,6 +45,13 @@ public class AssetImportService extends BaseEntityImportService { + entityActionService.onAssetCreatedOrUpdated(user, savedAsset, oldAsset == null); + }; + } + @Override public EntityType getEntityType() { return EntityType.ASSET; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java index 42e9263e77..bf3c126c92 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.service.action.EntityActionService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; @@ -36,6 +37,7 @@ import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import org.thingsboard.server.service.sync.importing.EntityImportResult; import org.thingsboard.server.service.sync.importing.EntityImportService; import org.thingsboard.server.service.sync.importing.EntityImportSettings; +import org.thingsboard.server.utils.ThrowingRunnable; import java.util.Collections; import java.util.LinkedList; @@ -52,6 +54,8 @@ public abstract class BaseEntityImportService importResult = new EntityImportResult<>(); importResult.setSavedEntity(savedEntity); importResult.setOldEntity(existingEntity); + importResult.setCallback(callback); return importResult; } @@ -84,30 +89,8 @@ public abstract class BaseEntityImportService Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndId(tenantId, entity.getId()))) - .or(() -> { - if (importSettings.isFindExistingByName()) { - return Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndName(tenantId, getEntityType(), entity.getName())); - } else { - return Optional.empty(); - } - }) - .orElse(null); - } - - private HasId findInternalEntity(TenantId tenantId, ID externalId) { - if (externalId == null || externalId.isNullUid()) return null; - - return (HasId) Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndExternalId(tenantId, externalId)) - .or(() -> Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndId(tenantId, externalId))) - .orElseThrow(() -> new IllegalArgumentException("Cannot find " + externalId.getEntityType() + " by external id " + externalId)); - } - - - private void importRelations(SecurityUser user, E savedEntity, E existingEntity, D exportData, EntityImportSettings importSettings) throws ThingsboardException { + protected ThrowingRunnable processAfterSavedAndGetCallback(SecurityUser user, E savedEntity, E oldEntity, D exportData, + EntityImportSettings importSettings, NewIdProvider idProvider) throws ThingsboardException { List newRelations = new LinkedList<>(); if (importSettings.isImportInboundRelations() && CollectionUtils.isNotEmpty(exportData.getInboundRelations())) { @@ -115,7 +98,7 @@ public abstract class BaseEntityImportService relation.setTo(savedEntity.getId())) .collect(Collectors.toList())); - if (importSettings.isRemoveExistingRelations() && existingEntity != null) { + if (importSettings.isRemoveExistingRelations() && oldEntity != null) { for (EntityRelation existingRelation : relationService.findByTo(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)) { exportableEntitiesService.checkPermission(user, existingRelation.getFrom(), Operation.WRITE); relationService.deleteRelation(user.getTenantId(), existingRelation); @@ -127,7 +110,7 @@ public abstract class BaseEntityImportService relation.setFrom(savedEntity.getId())) .collect(Collectors.toList())); - if (importSettings.isRemoveExistingRelations() && existingEntity != null) { + if (importSettings.isRemoveExistingRelations() && oldEntity != null) { for (EntityRelation existingRelation : relationService.findByFrom(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)) { exportableEntitiesService.checkPermission(user, existingRelation.getTo(), Operation.WRITE); relationService.deleteRelation(user.getTenantId(), existingRelation); @@ -151,6 +134,34 @@ public abstract class BaseEntityImportService {}; + } + + + private E findExistingEntity(TenantId tenantId, E entity, EntityImportSettings importSettings) { + return (E) Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndExternalId(tenantId, entity.getId())) + .or(() -> Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndId(tenantId, entity.getId()))) + .or(() -> { + if (importSettings.isFindExistingByName()) { + return Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndName(tenantId, getEntityType(), entity.getName())); + } else { + return Optional.empty(); + } + }) + .orElse(null); + } + + private HasId findInternalEntity(TenantId tenantId, ID externalId) { + if (externalId == null || externalId.isNullUid()) return null; + + return (HasId) Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndExternalId(tenantId, externalId)) + .or(() -> Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndId(tenantId, externalId))) + .orElseThrow(() -> new IllegalArgumentException("Cannot find " + externalId.getEntityType() + " by external id " + externalId)); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java index d17e6c8b41..336d9f68f2 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java @@ -19,11 +19,15 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.CustomerExportData; +import org.thingsboard.server.service.sync.importing.EntityImportSettings; +import org.thingsboard.server.utils.ThrowingRunnable; @Service @TbCoreComponent @@ -42,6 +46,13 @@ public class CustomerImportService extends BaseEntityImportService { + entityActionService.onCustomerCreatedOrUpdated(user, savedCustomer); + }; + } + @Override public EntityType getEntityType() { return EntityType.CUSTOMER; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java index 67f4ffc4a7..af24576d34 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java @@ -23,6 +23,7 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ShortCustomerInfo; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.EntityId; @@ -32,7 +33,10 @@ import org.thingsboard.server.common.data.query.EntityFilter; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.sql.query.DefaultEntityQueryRepository; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.DashboardExportData; +import org.thingsboard.server.service.sync.importing.EntityImportSettings; +import org.thingsboard.server.utils.ThrowingRunnable; import java.util.Collections; import java.util.HashSet; @@ -108,6 +112,13 @@ public class DashboardImportService extends BaseEntityImportService { + entityActionService.onDashboardCreatedOrUpdated(user, savedDashboard, oldDashboard == null); + }; + } + @Override public EntityType getEntityType() { return EntityType.DASHBOARD; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java index ccff72dcdb..4212083dea 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java @@ -17,13 +17,18 @@ package org.thingsboard.server.service.sync.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.DeviceExportData; +import org.thingsboard.server.service.sync.importing.EntityImportSettings; +import org.thingsboard.server.utils.ThrowingRunnable; @Service @TbCoreComponent @@ -31,6 +36,7 @@ import org.thingsboard.server.service.sync.exporting.data.DeviceExportData; public class DeviceImportService extends BaseEntityImportService { private final DeviceService deviceService; + private final TbClusterService clusterService; @Override protected void setOwner(TenantId tenantId, Device device, NewIdProvider idProvider) { @@ -52,6 +58,13 @@ public class DeviceImportService extends BaseEntityImportService { + clusterService.onDeviceUpdated(savedDevice, oldDevice); + }; + } + @Override public EntityType getEntityType() { return EntityType.DEVICE; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java index b59d4fa23e..f9d8d2ddd1 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java @@ -23,7 +23,9 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.DeviceProfileExportData; +import org.thingsboard.server.utils.ThrowingRunnable; @Service @TbCoreComponent @@ -46,6 +48,13 @@ public class DeviceProfileImportService extends BaseEntityImportService { + + }; + } + @Override public EntityType getEntityType() { return EntityType.DEVICE_PROFILE; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java index 8f0ff5dc7e..f6a96f24bf 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java @@ -27,7 +27,9 @@ import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.RuleChainExportData; +import org.thingsboard.server.utils.ThrowingRunnable; import java.util.Collections; import java.util.Optional; @@ -79,6 +81,13 @@ public class RuleChainImportService extends BaseEntityImportService { + + }; + } + @Override public EntityType getEntityType() { return EntityType.RULE_CHAIN; diff --git a/application/src/main/java/org/thingsboard/server/utils/ThrowingRunnable.java b/application/src/main/java/org/thingsboard/server/utils/ThrowingRunnable.java new file mode 100644 index 0000000000..6965a79d19 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/utils/ThrowingRunnable.java @@ -0,0 +1,24 @@ +/** + * 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.utils; + +import org.thingsboard.server.common.data.exception.ThingsboardException; + +public interface ThrowingRunnable { + + void run() throws ThingsboardException; + +} From 12840c81e997ea0084292f608e9d7b522e786ece Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Wed, 6 Apr 2022 21:39:26 +0300 Subject: [PATCH 18/39] Refactor events sending on import; export api with ExportRequest of different types --- .../server/controller/AssetController.java | 20 +- .../controller/DashboardController.java | 10 +- .../server/controller/DeviceController.java | 28 ++- .../EntitiesExportImportController.java | 199 ++++++------------ .../controller/RuleChainController.java | 17 +- .../service/action/EntityActionService.java | 61 +----- .../DefaultEntitiesExportImportService.java | 2 +- .../sync/EntitiesExportImportService.java | 2 +- .../sync/exporting/EntityExportService.java | 1 + .../request}/EntityExportSettings.java | 8 +- .../request/EntityFilterExportRequest.java | 37 ++++ .../data/request/EntityListExportRequest.java | 35 +++ .../request/EntityQueryExportRequest.java | 35 +++ .../data/request/EntityTypeExportRequest.java | 37 ++++ .../exporting/data/request/ExportRequest.java | 41 ++++ .../data/request/ExportRequestType.java | 25 +++ .../request/SingleEntityExportRequest.java | 33 +++ .../impl/BaseEntityExportService.java | 2 +- .../importing/data/request/ImportRequest.java | 32 +++ .../importing/impl/AssetImportService.java | 9 +- .../impl/BaseEntityImportService.java | 10 +- .../importing/impl/CustomerImportService.java | 9 +- .../impl/DashboardImportService.java | 12 +- .../importing/impl/DeviceImportService.java | 8 +- .../impl/DeviceProfileImportService.java | 19 +- .../impl/RuleChainImportService.java | 14 +- .../server/utils/ThrowingRunnable.java | 7 + ...EntitiesExportImportControllerSqlTest.java | 2 + 28 files changed, 479 insertions(+), 236 deletions(-) rename application/src/main/java/org/thingsboard/server/service/sync/exporting/{ => data/request}/EntityExportSettings.java (79%) create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityFilterExportRequest.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityListExportRequest.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityQueryExportRequest.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityTypeExportRequest.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequest.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequestType.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/SingleEntityExportRequest.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/importing/data/request/ImportRequest.java diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index 5e44e1433a..e48f2dcf23 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -155,7 +155,9 @@ public class AssetController extends BaseController { checkEntity(asset.getId(), asset, Resource.ASSET); Asset savedAsset = checkNotNull(assetService.saveAsset(asset)); - entityActionService.onAssetCreatedOrUpdated(getCurrentUser(), savedAsset, asset.getId() == null); + + onAssetCreatedOrUpdated(savedAsset, asset.getId() != null, getCurrentUser()); + return savedAsset; } catch (Exception e) { logEntityAction(emptyId(EntityType.ASSET), asset, @@ -164,6 +166,20 @@ 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)", 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')") @@ -665,7 +681,7 @@ public class AssetController extends BaseController { public BulkImportResult processAssetsBulkImport(@RequestBody BulkImportRequest request) throws Exception { SecurityUser user = getCurrentUser(); return assetBulkImportService.processBulkImport(request, user, importedAssetInfo -> { - entityActionService.onAssetCreatedOrUpdated(user, importedAssetInfo.getEntity(), !importedAssetInfo.isUpdated()); + onAssetCreatedOrUpdated(importedAssetInfo.getEntity(), importedAssetInfo.isUpdated(), user); }); } diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index b4b6d6be89..49c33045a5 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -186,7 +186,15 @@ public class DashboardController extends BaseController { checkEntity(dashboard.getId(), dashboard, Resource.DASHBOARD); Dashboard savedDashboard = checkNotNull(dashboardService.saveDashboard(dashboard)); - entityActionService.onDashboardCreatedOrUpdated(getCurrentUser(), savedDashboard, dashboard.getId() == null); + + logEntityAction(savedDashboard.getId(), savedDashboard, + null, + dashboard.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); + + if (dashboard.getId() != null) { + sendEntityNotificationMsg(savedDashboard.getTenantId(), savedDashboard.getId(), EdgeEventActionType.UPDATED); + } + return savedDashboard; } catch (Exception e) { logEntityAction(emptyId(EntityType.DASHBOARD), dashboard, diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 2dff62aec6..03f0996ea9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -74,11 +74,11 @@ import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.device.DeviceBulkImportService; import org.thingsboard.server.service.gateway_device.GatewayNotificationsService; +import org.thingsboard.server.service.sync.importing.csv.BulkImportRequest; +import org.thingsboard.server.service.sync.importing.csv.BulkImportResult; 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 org.thingsboard.server.service.sync.importing.csv.BulkImportRequest; -import org.thingsboard.server.service.sync.importing.csv.BulkImportResult; import javax.annotation.Nullable; import java.io.IOException; @@ -194,7 +194,9 @@ public class DeviceController extends BaseController { } Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken)); - entityActionService.onDeviceCreatedOrUpdated(getCurrentUser(), savedDevice, oldDevice, created); + + onDeviceCreatedOrUpdated(savedDevice, oldDevice, !created, getCurrentUser()); + return savedDevice; } catch (Exception e) { logEntityAction(emptyId(EntityType.DEVICE), device, @@ -222,7 +224,11 @@ public class DeviceController extends BaseController { checkEntity(device.getId(), device, Resource.DEVICE); Device savedDevice = deviceService.saveDeviceWithCredentials(device, credentials); checkNotNull(savedDevice); - entityActionService.onDeviceCreatedOrUpdated(getCurrentUser(), savedDevice, device, created); + tbClusterService.onDeviceUpdated(savedDevice, device); + logEntityAction(savedDevice.getId(), savedDevice, + savedDevice.getCustomerId(), + device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); + return savedDevice; } catch (Exception e) { logEntityAction(emptyId(EntityType.DEVICE), device, @@ -231,6 +237,18 @@ 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)", 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')") @@ -997,7 +1015,7 @@ public class DeviceController extends BaseController { public BulkImportResult processDevicesBulkImport(@RequestBody BulkImportRequest request) throws Exception { SecurityUser user = getCurrentUser(); return deviceBulkImportService.processBulkImport(request, user, importedDeviceInfo -> { - entityActionService.onDeviceCreatedOrUpdated(user, importedDeviceInfo.getEntity(), importedDeviceInfo.getOldEntity(), !importedDeviceInfo.isUpdated()); + onDeviceCreatedOrUpdated(importedDeviceInfo.getEntity(), importedDeviceInfo.getOldEntity(), importedDeviceInfo.isUpdated(), user); }); } diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index 6d6a2e4619..366eaec41a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -17,18 +17,16 @@ package org.thingsboard.server.controller; import lombok.RequiredArgsConstructor; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.query.EntityData; import org.thingsboard.server.common.data.query.EntityDataPageLink; import org.thingsboard.server.common.data.query.EntityDataQuery; @@ -41,13 +39,17 @@ import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.EntitiesExportImportService; -import org.thingsboard.server.service.sync.exporting.EntityExportSettings; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exporting.data.request.EntityFilterExportRequest; +import org.thingsboard.server.service.sync.exporting.data.request.EntityListExportRequest; +import org.thingsboard.server.service.sync.exporting.data.request.EntityQueryExportRequest; +import org.thingsboard.server.service.sync.exporting.data.request.EntityTypeExportRequest; +import org.thingsboard.server.service.sync.exporting.data.request.ExportRequest; +import org.thingsboard.server.service.sync.exporting.data.request.SingleEntityExportRequest; import org.thingsboard.server.service.sync.importing.EntityImportResult; -import org.thingsboard.server.service.sync.importing.EntityImportSettings; +import org.thingsboard.server.service.sync.importing.data.request.ImportRequest; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -60,7 +62,6 @@ import static org.thingsboard.server.dao.sql.query.EntityKeyMapping.CREATED_TIME @RestController @RequestMapping("/api/entities") -@PreAuthorize("hasAuthority('TENANT_ADMIN')") @TbCoreComponent @RequiredArgsConstructor public class EntitiesExportImportController extends BaseController { @@ -68,101 +69,79 @@ public class EntitiesExportImportController extends BaseController { private final EntitiesExportImportService exportImportService; private final EntityService entityService; - - @PostMapping("/export/{entityType}/{id}") - public EntityExportData> exportSingleEntity(@PathVariable EntityType entityType, - @PathVariable UUID id, - @RequestParam Map exportSettingsParams) throws ThingsboardException { + @PostMapping("/export") + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + public List>> exportEntities(@RequestBody ExportRequest exportRequest) throws ThingsboardException { SecurityUser user = getCurrentUser(); - EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, id); try { - return exportEntity(user, entityId, toExportSettings(exportSettingsParams)); + return exportEntitiesByRequest(user, exportRequest); } catch (Exception e) { throw handleException(e); } } - @PostMapping(value = "/export/{entityType}", params = "ids") - public List>> exportEntitiesByIds(@PathVariable EntityType entityType, - @RequestParam UUID[] ids, - @RequestParam Map exportSettingsParams) throws ThingsboardException { + @PostMapping(value = "/export", params = {"multiple"}) + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + public List>> exportEntities(@RequestBody List exportRequests) throws ThingsboardException { SecurityUser user = getCurrentUser(); - List entitiesIds = Arrays.stream(ids) - .map(id -> EntityIdFactory.getByTypeAndUuid(entityType, id)) - .collect(Collectors.toList()); try { - return exportEntitiesByIds(user, entitiesIds, toExportSettings(exportSettingsParams)); + List>> exportDataList = new ArrayList<>(); + for (ExportRequest exportRequest : exportRequests) { + exportDataList.addAll(exportEntitiesByRequest(user, exportRequest)); + } + return exportDataList; } catch (Exception e) { throw handleException(e); } } - @PostMapping(value = "/export/{entityType}") - public List>> exportEntitiesByEntityType(@PathVariable EntityType entityType, - @RequestParam Map exportSettingsParams, - @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "2147483647") int pageSize, - @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { - SecurityUser user = getCurrentUser(); - CustomerId customerId = toCustomerId(customerUuid); - EntityTypeFilter entityTypeFilter = new EntityTypeFilter(); - entityTypeFilter.setEntityType(entityType); - try { - return exportEntitiesByFilter(user, customerId, entityTypeFilter, page, pageSize, toExportSettings(exportSettingsParams)); - } catch (Exception e) { - throw handleException(e); - } - } + private List>> exportEntitiesByRequest(SecurityUser user, ExportRequest request) throws ThingsboardException { + List entitiesIds = findEntitiesForRequest(user, request); - @PostMapping("/exportByFilter") - public List>> exportEntitiesByFilter(@RequestBody EntityFilter filter, - @RequestParam Map exportSettingsParams, - @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "2147483647") int pageSize, - @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { - SecurityUser user = getCurrentUser(); - CustomerId customerId = toCustomerId(customerUuid); - try { - return exportEntitiesByFilter(user, customerId, filter, page, pageSize, toExportSettings(exportSettingsParams)); - } catch (Exception e) { - throw handleException(e); + List>> exportDataList = new ArrayList<>(); + for (EntityId entityId : entitiesIds) { + exportDataList.add(exportImportService.exportEntity(user, entityId, request.getExportSettings())); } + return exportDataList; } - // FIXME: too aggressive - @PostMapping("/exportByFilters") - public List>> exportAllEntitiesByFilters(@RequestBody List filters, - @RequestParam Map exportSettingsParams, - @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { - SecurityUser user = getCurrentUser(); - CustomerId customerId = toCustomerId(customerUuid); - try { - List>> exportDataList = new ArrayList<>(); - for (EntityFilter filter : filters) { - exportDataList.addAll(exportEntitiesByFilter(user, customerId, filter, 0, Integer.MAX_VALUE, toExportSettings(exportSettingsParams))); + private List findEntitiesForRequest(SecurityUser user, ExportRequest request) { + switch (request.getType()) { + case SINGLE_ENTITY: { + return List.of(((SingleEntityExportRequest) request).getEntityId()); } - return exportDataList; - } catch (Exception e) { - throw handleException(e); - } - } + case ENTITY_LIST: { + return ((EntityListExportRequest) request).getEntitiesIds(); + } + case ENTITY_TYPE: { + EntityTypeExportRequest exportRequest = (EntityTypeExportRequest) request; + EntityTypeFilter entityTypeFilter = new EntityTypeFilter(); + entityTypeFilter.setEntityType(exportRequest.getEntityType()); - @PostMapping("/exportByQuery") - public List>> exportEntitiesByQuery(@RequestBody EntityDataQuery entitiesQuery, - @RequestParam Map exportSettingsParams, - @RequestParam(name = "customerId", required = false) UUID customerUuid) throws ThingsboardException { - SecurityUser user = getCurrentUser(); - CustomerId customerId = toCustomerId(customerUuid); - try { - return exportEntitiesByQuery(user, customerId, entitiesQuery, toExportSettings(exportSettingsParams)); - } catch (Exception e) { - throw handleException(e); + CustomerId customerId = Optional.ofNullable(exportRequest.getCustomerId()).orElse(emptyId(EntityType.CUSTOMER)); + return findEntitiesByFilter(user.getTenantId(), customerId, entityTypeFilter, exportRequest.getPage(), exportRequest.getPageSize()); + } + case ENTITY_FILTER: { + EntityFilterExportRequest exportRequest = (EntityFilterExportRequest) request; + EntityFilter filter = exportRequest.getFilter(); + + CustomerId customerId = Optional.ofNullable(exportRequest.getCustomerId()).orElse(emptyId(EntityType.CUSTOMER)); + return findEntitiesByFilter(user.getTenantId(), customerId, filter, exportRequest.getPage(), exportRequest.getPageSize()); + } + case ENTITY_QUERY:{ + EntityQueryExportRequest exportRequest = (EntityQueryExportRequest) request; + EntityDataQuery query = exportRequest.getQuery(); + + CustomerId customerId = Optional.ofNullable(exportRequest.getCustomerId()).orElse(emptyId(EntityType.CUSTOMER)); + return findEntitiesByQuery(user.getTenantId(), customerId, query); + } + default: + throw new IllegalArgumentException("Export request is not supported"); } } - - private List>> exportEntitiesByFilter(SecurityUser user, CustomerId customerId, EntityFilter filter, int page, int pageSize, EntityExportSettings exportSettings) throws ThingsboardException { + private List findEntitiesByFilter(TenantId tenantId, CustomerId customerId, EntityFilter filter, int page, int pageSize) { EntityDataPageLink pageLink = new EntityDataPageLink(); pageLink.setPage(page); pageLink.setPageSize(pageSize); @@ -170,37 +149,23 @@ public class EntitiesExportImportController extends BaseController { pageLink.setSortOrder(new EntityDataSortOrder(sortProperty, EntityDataSortOrder.Direction.DESC)); EntityDataQuery query = new EntityDataQuery(filter, pageLink, List.of(sortProperty), Collections.emptyList(), Collections.emptyList()); - return exportEntitiesByQuery(user, customerId, query, exportSettings); + return findEntitiesByQuery(tenantId, customerId, query); } - private List>> exportEntitiesByQuery(SecurityUser user, CustomerId customerId, EntityDataQuery query, EntityExportSettings exportSettings) throws ThingsboardException { - List entitiesIds = entityService.findEntityDataByQuery(user.getTenantId(), customerId, query).getData().stream() + private List findEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) { + return entityService.findEntityDataByQuery(tenantId, customerId, query).getData().stream() .map(EntityData::getEntityId) .collect(Collectors.toList()); - return exportEntitiesByIds(user, entitiesIds, exportSettings); - } - - private List>> exportEntitiesByIds(SecurityUser user, List entitiesIds, EntityExportSettings exportSettings) throws ThingsboardException { - List>> exportDataList = new ArrayList<>(); - for (EntityId entityId : entitiesIds) { - exportDataList.add(exportEntity(user, entityId, exportSettings)); - } - return exportDataList; - } - - private , I extends EntityId> EntityExportData exportEntity(SecurityUser user, I entityId, EntityExportSettings exportSettings) throws ThingsboardException { - return exportImportService.exportEntity(user, entityId, exportSettings); } @PostMapping("/import") - public List>> importEntities(@RequestBody List>> exportDataList, - @RequestParam Map importSettingsParams) throws ThingsboardException { + public List>> importEntities(@RequestBody ImportRequest importRequest) throws ThingsboardException { SecurityUser user = getCurrentUser(); - EntityImportSettings importSettings = toImportSettings(importSettingsParams); - try { - List>> importResults = importEntities(user, exportDataList, importSettings); + List>> importResults = exportImportService.importEntities(user, + importRequest.getExportDataList(), importRequest.getImportSettings()); + for (EntityImportResult> entityImportResult : importResults) { if (entityImportResult.getCallback() != null) { entityImportResult.getCallback().run(); @@ -212,40 +177,4 @@ public class EntitiesExportImportController extends BaseController { } } - - public List>> importEntities(SecurityUser user, List>> exportDataList, EntityImportSettings importSettings) throws ThingsboardException { - return exportImportService.importEntities(user, exportDataList, importSettings); - } - - - private EntityExportSettings toExportSettings(Map exportSettingsParams) { - return EntityExportSettings.builder() - .exportInboundRelations(getBooleanParam(exportSettingsParams, "exportInboundRelations", false)) - .exportOutboundRelations(getBooleanParam(exportSettingsParams, "exportOutboundRelations", false)) - .build(); - } - - private EntityImportSettings toImportSettings(Map importSettingsParams) { - return EntityImportSettings.builder() - .findExistingByName(getBooleanParam(importSettingsParams, "findExistingByName", false)) - .importInboundRelations(getBooleanParam(importSettingsParams, "importInboundRelations", false)) - .importOutboundRelations(getBooleanParam(importSettingsParams, "importOutboundRelations", false)) - .removeExistingRelations(getBooleanParam(importSettingsParams, "removeExistingRelations", true)) - .updateReferencesToOtherEntities(getBooleanParam(importSettingsParams, "updateReferencesToOtherEntities", true)) - .build(); - } - - - protected static boolean getBooleanParam(Map requestParams, String key, boolean defaultValue) { - return getParam(requestParams, key, defaultValue, Boolean::parseBoolean); - } - - protected static T getParam(Map requestParams, String key, T defaultValue, Function parsingFunction) { - return parsingFunction.apply(requestParams.getOrDefault(key, defaultValue.toString())); - } - - private static CustomerId toCustomerId(UUID customerUuid) { - return new CustomerId(Optional.ofNullable(customerUuid).orElse(EntityId.NULL_UUID)); - } - } diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java index e27090c700..6479b838b2 100644 --- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java +++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java @@ -249,10 +249,25 @@ public class RuleChainController extends BaseController { try { boolean created = ruleChain.getId() == null; ruleChain.setTenantId(getCurrentUser().getTenantId()); + checkEntity(ruleChain.getId(), ruleChain, Resource.RULE_CHAIN); RuleChain savedRuleChain = checkNotNull(ruleChainService.saveRuleChain(ruleChain)); - entityActionService.onRuleChainCreatedOrUpdated(getCurrentUser(), savedRuleChain, created); + + if (RuleChainType.CORE.equals(savedRuleChain.getType())) { + 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; } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java index d692e24bbe..2a64db7385 100644 --- a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java +++ b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java @@ -22,18 +22,14 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.DataConstants; -import org.thingsboard.server.common.data.Device; 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.User; -import org.thingsboard.server.common.data.asset.Asset; 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.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -41,20 +37,12 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.DataType; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; -import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; -import org.thingsboard.server.common.data.rule.RuleChain; -import org.thingsboard.server.common.data.rule.RuleChainMetaData; -import org.thingsboard.server.common.data.rule.RuleChainType; -import org.thingsboard.server.common.data.rule.RuleChainUpdateResult; 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.audit.AuditLogService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.cluster.TbClusterService; -import org.thingsboard.server.service.security.model.SecurityUser; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -235,6 +223,9 @@ public class EntityActionService { auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo); } + public void sendEntityNotificationMsgToEdgeService(TenantId tenantId, EntityId entityId, EdgeEventActionType action) { + tbClusterService.sendNotificationMsgToEdgeService(tenantId, null, entityId, null, null, action); + } private T extractParameter(Class clazz, int index, Object... additionalInfo) { T result = null; @@ -280,48 +271,4 @@ public class EntityActionService { } } - - public void onDeviceCreatedOrUpdated(SecurityUser user, Device savedDevice, Device oldDevice, boolean newEntity) { - tbClusterService.onDeviceUpdated(savedDevice, oldDevice); - logEntityAction(user, savedDevice.getId(), savedDevice, savedDevice.getCustomerId(), - newEntity ? ActionType.ADDED : ActionType.UPDATED, null); - } - - public void onAssetCreatedOrUpdated(SecurityUser user, Asset savedAsset, boolean newEntity) { - logEntityAction(user, savedAsset.getId(), savedAsset, savedAsset.getCustomerId(), - newEntity ? ActionType.ADDED : ActionType.UPDATED, null); - if (!newEntity) { - tbClusterService.sendNotificationMsgToEdgeService(user.getTenantId(), null, savedAsset.getId(), - null, null, EdgeEventActionType.UPDATED); - } - } - - public void onDashboardCreatedOrUpdated(SecurityUser user, Dashboard savedDashboard, boolean newEntity) { - logEntityAction(user, savedDashboard.getId(), savedDashboard, null, - newEntity ? ActionType.ADDED : ActionType.UPDATED, null); - if (!newEntity) { - tbClusterService.sendNotificationMsgToEdgeService(user.getTenantId(), null, savedDashboard.getId(), - null, null, EdgeEventActionType.UPDATED); - } - } - - public void onRuleChainCreatedOrUpdated(SecurityUser user, RuleChain savedRuleChain, boolean newEntity) { - if (RuleChainType.CORE.equals(savedRuleChain.getType())) { - tbClusterService.broadcastEntityStateChangeEvent(user.getTenantId(), savedRuleChain.getId(), - newEntity ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); - } - logEntityAction(user, savedRuleChain.getId(), savedRuleChain, null, - newEntity ? ActionType.ADDED : ActionType.UPDATED, null); - if (RuleChainType.EDGE.equals(savedRuleChain.getType())) { - if (!newEntity) { - tbClusterService.sendNotificationMsgToEdgeService(user.getTenantId(), null, savedRuleChain.getId(), - null, null, EdgeEventActionType.UPDATED); - } - } - } - - public void onCustomerCreatedOrUpdated(SecurityUser user, Customer savedCustomer) { - - } - } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java index 90f6f1c778..9bca5f8bc9 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java @@ -35,7 +35,7 @@ import org.thingsboard.server.service.security.permission.AccessControlService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.sync.exporting.EntityExportService; -import org.thingsboard.server.service.sync.exporting.EntityExportSettings; +import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import org.thingsboard.server.service.sync.importing.EntityImportResult; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java index 447d514c68..3fa9b13406 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java @@ -19,7 +19,7 @@ import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.exporting.EntityExportSettings; +import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import org.thingsboard.server.service.sync.importing.EntityImportResult; import org.thingsboard.server.service.sync.importing.EntityImportSettings; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java index 681a398720..c09cbcd36c 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java @@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; public interface EntityExportService, D extends EntityExportData> { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityExportSettings.java similarity index 79% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportSettings.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityExportSettings.java index 36552b2c12..01e4f8d068 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityExportSettings.java @@ -13,17 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting; +package org.thingsboard.server.service.sync.exporting.data.request; -import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; -import lombok.NoArgsConstructor; @Data -@AllArgsConstructor -@NoArgsConstructor -@Builder public class EntityExportSettings { private boolean exportInboundRelations; private boolean exportOutboundRelations; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityFilterExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityFilterExportRequest.java new file mode 100644 index 0000000000..00ef0604e5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityFilterExportRequest.java @@ -0,0 +1,37 @@ +/** + * 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.sync.exporting.data.request; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.query.EntityFilter; + +@EqualsAndHashCode(callSuper = true) +@Data +public class EntityFilterExportRequest extends ExportRequest { + + private EntityFilter filter; + private int page; + private int pageSize; + private CustomerId customerId; + + @Override + public ExportRequestType getType() { + return ExportRequestType.ENTITY_FILTER; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityListExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityListExportRequest.java new file mode 100644 index 0000000000..01309a421b --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityListExportRequest.java @@ -0,0 +1,35 @@ +/** + * 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.sync.exporting.data.request; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.id.EntityId; + +import java.util.List; + +@EqualsAndHashCode(callSuper = true) +@Data +public class EntityListExportRequest extends ExportRequest { + + private List entitiesIds; + + @Override + public ExportRequestType getType() { + return ExportRequestType.ENTITY_LIST; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityQueryExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityQueryExportRequest.java new file mode 100644 index 0000000000..85232821d7 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityQueryExportRequest.java @@ -0,0 +1,35 @@ +/** + * 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.sync.exporting.data.request; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.query.EntityDataQuery; + +@EqualsAndHashCode(callSuper = true) +@Data +public class EntityQueryExportRequest extends ExportRequest { + + private EntityDataQuery query; + private CustomerId customerId; + + @Override + public ExportRequestType getType() { + return ExportRequestType.ENTITY_QUERY; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityTypeExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityTypeExportRequest.java new file mode 100644 index 0000000000..82cb023258 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityTypeExportRequest.java @@ -0,0 +1,37 @@ +/** + * 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.sync.exporting.data.request; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.CustomerId; + +@EqualsAndHashCode(callSuper = true) +@Data +public class EntityTypeExportRequest extends ExportRequest { + + private EntityType entityType; + private int page; + private int pageSize; + private CustomerId customerId; + + @Override + public ExportRequestType getType() { + return ExportRequestType.ENTITY_TYPE; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequest.java new file mode 100644 index 0000000000..c140786d06 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequest.java @@ -0,0 +1,41 @@ +/** + * 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.sync.exporting.data.request; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; +import lombok.Data; + +@JsonTypeInfo(use = Id.NAME, property = "type") +@JsonSubTypes({ + @Type(name = "SINGLE_ENTITY", value = SingleEntityExportRequest.class), + @Type(name = "ENTITY_LIST", value = EntityListExportRequest.class), + @Type(name = "ENTITY_TYPE", value = EntityTypeExportRequest.class), + @Type(name = "ENTITY_FILTER", value = EntityFilterExportRequest.class), + @Type(name = "ENTITY_QUERY", value = EntityQueryExportRequest.class) +}) +@Data +public abstract class ExportRequest { + + private EntityExportSettings exportSettings; + + @JsonIgnore + public abstract ExportRequestType getType(); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequestType.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequestType.java new file mode 100644 index 0000000000..07e5c946ba --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequestType.java @@ -0,0 +1,25 @@ +/** + * 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.sync.exporting.data.request; + +public enum ExportRequestType { + SINGLE_ENTITY, + ENTITY_LIST, + ENTITY_TYPE, + + ENTITY_FILTER, + ENTITY_QUERY +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/SingleEntityExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/SingleEntityExportRequest.java new file mode 100644 index 0000000000..fe07ba7534 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/SingleEntityExportRequest.java @@ -0,0 +1,33 @@ +/** + * 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.sync.exporting.data.request; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.id.EntityId; + +@EqualsAndHashCode(callSuper = true) +@Data +public class SingleEntityExportRequest extends ExportRequest { + + private EntityId entityId; + + @Override + public ExportRequestType getType() { + return ExportRequestType.SINGLE_ENTITY; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java index 772e010b2b..5d693e25b6 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java @@ -27,7 +27,7 @@ import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.sync.exporting.EntityExportService; -import org.thingsboard.server.service.sync.exporting.EntityExportSettings; +import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/request/ImportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/request/ImportRequest.java new file mode 100644 index 0000000000..cc2eeba618 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/request/ImportRequest.java @@ -0,0 +1,32 @@ +/** + * 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.sync.importing.data.request; + +import lombok.Data; +import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.importing.EntityImportSettings; + +import java.util.List; + +@Data +public class ImportRequest { + + private List>> exportDataList; + private EntityImportSettings importSettings; + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java index 1c031f50e4..9a79c26515 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java @@ -19,6 +19,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.asset.AssetService; @@ -47,9 +48,11 @@ public class AssetImportService extends BaseEntityImportService { - entityActionService.onAssetCreatedOrUpdated(user, savedAsset, oldAsset == null); - }; + return super.getCallback(user, savedAsset, oldAsset).andThen(() -> { + if (oldAsset != null) { + entityActionService.sendEntityNotificationMsgToEdgeService(user.getTenantId(), savedAsset.getId(), EdgeEventActionType.UPDATED); + } + }); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java index bf3c126c92..9715fe008e 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java @@ -20,8 +20,11 @@ import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.HasCustomerId; +import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; @@ -56,6 +59,8 @@ public abstract class BaseEntityImportService {}; + return () -> { + entityActionService.logEntityAction(user, savedEntity.getId(), savedEntity, savedEntity instanceof HasCustomerId ? ((HasCustomerId) savedEntity).getCustomerId() : user.getCustomerId(), + oldEntity == null ? ActionType.ADDED : ActionType.UPDATED, null); + }; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java index 336d9f68f2..a03ee60569 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java @@ -19,6 +19,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; @@ -48,9 +49,11 @@ public class CustomerImportService extends BaseEntityImportService { - entityActionService.onCustomerCreatedOrUpdated(user, savedCustomer); - }; + return super.getCallback(user, savedCustomer, oldCustomer).andThen(() -> { + if (oldCustomer != null) { + entityActionService.sendEntityNotificationMsgToEdgeService(user.getTenantId(), savedCustomer.getId(), EdgeEventActionType.UPDATED); + } + }); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java index af24576d34..e80e699c44 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java @@ -23,7 +23,7 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ShortCustomerInfo; -import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.EntityId; @@ -35,14 +35,12 @@ import org.thingsboard.server.dao.sql.query.DefaultEntityQueryRepository; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.DashboardExportData; -import org.thingsboard.server.service.sync.importing.EntityImportSettings; import org.thingsboard.server.utils.ThrowingRunnable; import java.util.Collections; import java.util.HashSet; import java.util.Optional; import java.util.Set; -import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -114,9 +112,11 @@ public class DashboardImportService extends BaseEntityImportService { - entityActionService.onDashboardCreatedOrUpdated(user, savedDashboard, oldDashboard == null); - }; + return super.getCallback(user, savedDashboard, oldDashboard).andThen(() -> { + if (oldDashboard != null) { + entityActionService.sendEntityNotificationMsgToEdgeService(user.getTenantId(), savedDashboard.getId(), EdgeEventActionType.UPDATED); + } + }); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java index 4212083dea..7484fb98ba 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java @@ -17,17 +17,14 @@ package org.thingsboard.server.service.sync.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.DeviceExportData; -import org.thingsboard.server.service.sync.importing.EntityImportSettings; import org.thingsboard.server.utils.ThrowingRunnable; @Service @@ -36,7 +33,6 @@ import org.thingsboard.server.utils.ThrowingRunnable; public class DeviceImportService extends BaseEntityImportService { private final DeviceService deviceService; - private final TbClusterService clusterService; @Override protected void setOwner(TenantId tenantId, Device device, NewIdProvider idProvider) { @@ -60,9 +56,9 @@ public class DeviceImportService extends BaseEntityImportService { + return super.getCallback(user, savedDevice, oldDevice).andThen(() -> { clusterService.onDeviceUpdated(savedDevice, oldDevice); - }; + }); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java index f9d8d2ddd1..8db608fb42 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java @@ -19,20 +19,26 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.ota.OtaPackageStateService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.DeviceProfileExportData; import org.thingsboard.server.utils.ThrowingRunnable; +import java.util.Objects; + @Service @TbCoreComponent @RequiredArgsConstructor public class DeviceProfileImportService extends BaseEntityImportService { private final DeviceProfileService deviceProfileService; + private final OtaPackageStateService otaPackageStateService; @Override protected void setOwner(TenantId tenantId, DeviceProfile deviceProfile, NewIdProvider idProvider) { @@ -50,9 +56,16 @@ public class DeviceProfileImportService extends BaseEntityImportService { - - }; + return super.getCallback(user, savedDeviceProfile, oldDeviceProfile).andThen(() -> { + clusterService.onDeviceProfileChange(savedDeviceProfile, null); + clusterService.broadcastEntityStateChangeEvent(user.getTenantId(), savedDeviceProfile.getId(), + oldDeviceProfile == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); + entityActionService.sendEntityNotificationMsgToEdgeService(user.getTenantId(), savedDeviceProfile.getId(), + oldDeviceProfile == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); + otaPackageStateService.update(savedDeviceProfile, + oldDeviceProfile != null && !Objects.equals(oldDeviceProfile.getFirmwareId(), savedDeviceProfile.getFirmwareId()), + oldDeviceProfile != null && !Objects.equals(oldDeviceProfile.getSoftwareId(), savedDeviceProfile.getSoftwareId())); + }); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java index f6a96f24bf..ec8a0aa5a2 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java @@ -21,10 +21,13 @@ import com.fasterxml.jackson.databind.node.TextNode; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; +import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; @@ -83,9 +86,14 @@ public class RuleChainImportService extends BaseEntityImportService { - - }; + return super.getCallback(user, savedRuleChain, oldRuleChain).andThen(() -> { + if (savedRuleChain.getType() == RuleChainType.CORE) { + clusterService.broadcastEntityStateChangeEvent(user.getTenantId(), savedRuleChain.getId(), + oldRuleChain == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); + } else if (savedRuleChain.getType() == RuleChainType.EDGE && oldRuleChain != null) { + entityActionService.sendEntityNotificationMsgToEdgeService(user.getTenantId(), savedRuleChain.getId(), EdgeEventActionType.UPDATED); + } + }); } @Override diff --git a/application/src/main/java/org/thingsboard/server/utils/ThrowingRunnable.java b/application/src/main/java/org/thingsboard/server/utils/ThrowingRunnable.java index 6965a79d19..858ccdf422 100644 --- a/application/src/main/java/org/thingsboard/server/utils/ThrowingRunnable.java +++ b/application/src/main/java/org/thingsboard/server/utils/ThrowingRunnable.java @@ -21,4 +21,11 @@ public interface ThrowingRunnable { void run() throws ThingsboardException; + default ThrowingRunnable andThen(ThrowingRunnable after) { + return () -> { + this.run(); + after.run(); + }; + } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java index f114af6625..9c3c8d3ff3 100644 --- a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.JavaType; import lombok.SneakyThrows; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.web.servlet.ResultActions; @@ -54,6 +55,7 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +@Ignore @DaoSqlTest public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImportControllerTest { From 1b48a98fa091b1e85c18aa53905d6f8e47af1f1e Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Fri, 8 Apr 2022 13:53:28 +0300 Subject: [PATCH 19/39] Save relations after all other entities are saved on import; refactoring --- .../EntitiesExportImportController.java | 15 +-- .../DefaultEntitiesExportImportService.java | 56 +++++++--- .../sync/EntitiesExportImportService.java | 8 +- .../sync/importing/EntityImportService.java | 2 + .../{ => data}/EntityImportResult.java | 25 +++-- .../{ => data}/EntityImportSettings.java | 2 +- .../importing/data/request/ImportRequest.java | 2 +- .../importing/impl/AssetImportService.java | 12 +-- .../impl/BaseEntityImportService.java | 102 +++++++++--------- .../importing/impl/CustomerImportService.java | 14 +-- .../impl/DashboardImportService.java | 12 +-- .../importing/impl/DeviceImportService.java | 8 +- .../impl/DeviceProfileImportService.java | 22 ++-- .../impl/RuleChainImportService.java | 18 ++-- ...EntitiesExportImportControllerSqlTest.java | 2 +- .../java/org/thingsboard/server/dao/Dao.java | 2 + .../server/dao/asset/BaseAssetService.java | 2 +- .../server/dao/sql/JpaAbstractDao.java | 8 ++ 18 files changed, 169 insertions(+), 143 deletions(-) rename application/src/main/java/org/thingsboard/server/service/sync/importing/{ => data}/EntityImportResult.java (60%) rename application/src/main/java/org/thingsboard/server/service/sync/importing/{ => data}/EntityImportSettings.java (94%) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index 366eaec41a..e1f5dd4525 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -46,16 +46,13 @@ import org.thingsboard.server.service.sync.exporting.data.request.EntityQueryExp import org.thingsboard.server.service.sync.exporting.data.request.EntityTypeExportRequest; import org.thingsboard.server.service.sync.exporting.data.request.ExportRequest; import org.thingsboard.server.service.sync.exporting.data.request.SingleEntityExportRequest; -import org.thingsboard.server.service.sync.importing.EntityImportResult; +import org.thingsboard.server.service.sync.importing.data.EntityImportResult; import org.thingsboard.server.service.sync.importing.data.request.ImportRequest; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.UUID; -import java.util.function.Function; import java.util.stream.Collectors; import static org.thingsboard.server.dao.sql.query.EntityKeyMapping.CREATED_TIME; @@ -163,15 +160,7 @@ public class EntitiesExportImportController extends BaseController { public List>> importEntities(@RequestBody ImportRequest importRequest) throws ThingsboardException { SecurityUser user = getCurrentUser(); try { - List>> importResults = exportImportService.importEntities(user, - importRequest.getExportDataList(), importRequest.getImportSettings()); - - for (EntityImportResult> entityImportResult : importResults) { - if (entityImportResult.getCallback() != null) { - entityImportResult.getCallback().run(); - } - } - return importResults; + return exportImportService.importEntities(user, importRequest.getExportDataList(), importRequest.getImportSettings()); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java index 9bca5f8bc9..5df3cdde0c 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java @@ -16,6 +16,7 @@ package org.thingsboard.server.service.sync; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -35,12 +36,13 @@ import org.thingsboard.server.service.security.permission.AccessControlService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.sync.exporting.EntityExportService; -import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.importing.EntityImportResult; +import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; import org.thingsboard.server.service.sync.importing.EntityImportService; -import org.thingsboard.server.service.sync.importing.EntityImportSettings; +import org.thingsboard.server.service.sync.importing.data.EntityImportResult; +import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; +import org.thingsboard.server.utils.ThrowingRunnable; import java.util.ArrayList; import java.util.Collection; @@ -48,10 +50,13 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; @Service @TbCoreComponent @RequiredArgsConstructor +@Slf4j public class DefaultEntitiesExportImportService implements EntitiesExportImportService, ExportableEntitiesService { private final Map> exportServices = new HashMap<>(); @@ -77,7 +82,37 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS @Transactional(rollbackFor = Exception.class) @Override - public , I extends EntityId> EntityImportResult importEntity(SecurityUser user, EntityExportData exportData, EntityImportSettings importSettings) throws ThingsboardException { + public List>> importEntities(SecurityUser user, List>> exportDataList, EntityImportSettings importSettings) throws ThingsboardException { + exportDataList.sort(Comparator.comparing(exportData -> SUPPORTED_ENTITY_TYPES.indexOf(exportData.getEntityType()))); + List>> importResults = new ArrayList<>(); + + for (EntityExportData> exportData : exportDataList) { + EntityImportResult> importResult = importEntity(user, exportData, importSettings); + importResults.add(importResult); + } + + for (ThrowingRunnable saveReferencesCallback : importResults.stream() + .map(EntityImportResult::getSaveReferencesCallback) + .filter(Objects::nonNull) + .collect(Collectors.toList())) { + saveReferencesCallback.run(); + } + + importResults.stream() + .map(EntityImportResult::getPushEventsCallback) + .filter(Objects::nonNull) + .forEach(pushEventsCallback -> { + try { + pushEventsCallback.run(); + } catch (Exception e) { + log.error("Failed to send event for entity", e); + } + }); + + return importResults; + } + + private , I extends EntityId> EntityImportResult importEntity(SecurityUser user, EntityExportData exportData, EntityImportSettings importSettings) throws ThingsboardException { if (exportData.getEntity() == null || exportData.getEntity().getId() == null) { throw new DataValidationException("Invalid entity data"); } @@ -85,22 +120,9 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS EntityType entityType = exportData.getEntityType(); EntityImportService> importService = getImportService(entityType); - // TODO [viacheslav]: might throw DataIntegrityViolationException with cause of ConstraintViolationException: need to give normal error return importService.importEntity(user, exportData, importSettings); } - @Transactional(rollbackFor = Exception.class) - @Override - public List>> importEntities(SecurityUser user, List>> exportDataList, EntityImportSettings importSettings) throws ThingsboardException { - exportDataList.sort(Comparator.comparing(exportData -> SUPPORTED_ENTITY_TYPES.indexOf(exportData.getEntityType()))); - - List>> importResults = new ArrayList<>(); - for (EntityExportData> exportData : exportDataList) { - importResults.add(importEntity(user, exportData, importSettings)); - } - return importResults; - } - @Override public , I extends EntityId> E findEntityByTenantIdAndExternalId(TenantId tenantId, I externalId) { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java index 3fa9b13406..b0690ea9ca 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java @@ -19,10 +19,10 @@ import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.importing.EntityImportResult; -import org.thingsboard.server.service.sync.importing.EntityImportSettings; +import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; +import org.thingsboard.server.service.sync.importing.data.EntityImportResult; +import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; import java.util.List; @@ -30,8 +30,6 @@ public interface EntitiesExportImportService { , I extends EntityId> EntityExportData exportEntity(SecurityUser user, I entityId, EntityExportSettings exportSettings) throws ThingsboardException; - , I extends EntityId> EntityImportResult importEntity(SecurityUser user, EntityExportData exportData, EntityImportSettings importSettings) throws ThingsboardException; - List>> importEntities(SecurityUser user, List>> exportDataList, EntityImportSettings importSettings) throws ThingsboardException; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java index e3e40eea2a..6c82530273 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java @@ -21,6 +21,8 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.importing.data.EntityImportResult; +import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; public interface EntityImportService, D extends EntityExportData> { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportResult.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportResult.java similarity index 60% rename from application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportResult.java rename to application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportResult.java index 90d47c4378..e51048c433 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportResult.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportResult.java @@ -13,19 +13,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.importing; +package org.thingsboard.server.service.sync.importing.data; import com.fasterxml.jackson.annotation.JsonIgnore; -import lombok.Data; +import lombok.Getter; +import lombok.Setter; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.msg.queue.TbCallback; import org.thingsboard.server.utils.ThrowingRunnable; -@Data public class EntityImportResult> { + @Getter @Setter private E savedEntity; + @Getter @Setter private E oldEntity; - @JsonIgnore - private transient ThrowingRunnable callback; // to call when entity is successfully saved and transaction is committed + + @Getter @JsonIgnore + private ThrowingRunnable saveReferencesCallback = () -> {}; + @Getter @JsonIgnore + private ThrowingRunnable pushEventsCallback = () -> {}; + + public void addSaveReferencesCallback(ThrowingRunnable callback) { + this.saveReferencesCallback = this.saveReferencesCallback.andThen(callback); + } + + public void addPushEventsCallback(ThrowingRunnable callback) { + this.pushEventsCallback = this.pushEventsCallback.andThen(callback); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportSettings.java rename to application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java index 9145762bef..1b5a93c0d8 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.importing; +package org.thingsboard.server.service.sync.importing.data; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/request/ImportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/request/ImportRequest.java index cc2eeba618..6e857958d6 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/request/ImportRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/request/ImportRequest.java @@ -19,7 +19,7 @@ import lombok.Data; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.importing.EntityImportSettings; +import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java index 9a79c26515..0c8587270a 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java @@ -26,7 +26,6 @@ import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.AssetExportData; -import org.thingsboard.server.utils.ThrowingRunnable; @Service @TbCoreComponent @@ -47,12 +46,11 @@ public class AssetImportService extends BaseEntityImportService { - if (oldAsset != null) { - entityActionService.sendEntityNotificationMsgToEdgeService(user.getTenantId(), savedAsset.getId(), EdgeEventActionType.UPDATED); - } - }); + protected void onEntitySaved(SecurityUser user, Asset savedAsset, Asset oldAsset) { + super.onEntitySaved(user, savedAsset, oldAsset); + if (oldAsset != null) { + entityActionService.sendEntityNotificationMsgToEdgeService(user.getTenantId(), savedAsset.getId(), EdgeEventActionType.UPDATED); + } } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java index 9715fe008e..b7602a53bf 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java @@ -37,10 +37,9 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.importing.EntityImportResult; import org.thingsboard.server.service.sync.importing.EntityImportService; -import org.thingsboard.server.service.sync.importing.EntityImportSettings; -import org.thingsboard.server.utils.ThrowingRunnable; +import org.thingsboard.server.service.sync.importing.data.EntityImportResult; +import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; import java.util.Collections; import java.util.LinkedList; @@ -81,12 +80,13 @@ public abstract class BaseEntityImportService importResult = new EntityImportResult<>(); importResult.setSavedEntity(savedEntity); importResult.setOldEntity(existingEntity); - importResult.setCallback(callback); + + processAfterSaved(user, importResult, exportData, idProvider, importSettings); + return importResult; } @@ -94,60 +94,66 @@ public abstract class BaseEntityImportService newRelations = new LinkedList<>(); + protected void processAfterSaved(SecurityUser user, EntityImportResult importResult, D exportData, + NewIdProvider idProvider, EntityImportSettings importSettings) throws ThingsboardException { + E savedEntity = importResult.getSavedEntity(); + E oldEntity = importResult.getOldEntity(); - if (importSettings.isImportInboundRelations() && CollectionUtils.isNotEmpty(exportData.getInboundRelations())) { - newRelations.addAll(exportData.getInboundRelations().stream() - .peek(relation -> relation.setTo(savedEntity.getId())) - .collect(Collectors.toList())); + importResult.addSaveReferencesCallback(() -> { + List newRelations = new LinkedList<>(); - if (importSettings.isRemoveExistingRelations() && oldEntity != null) { - for (EntityRelation existingRelation : relationService.findByTo(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)) { - exportableEntitiesService.checkPermission(user, existingRelation.getFrom(), Operation.WRITE); - relationService.deleteRelation(user.getTenantId(), existingRelation); + if (importSettings.isImportInboundRelations() && CollectionUtils.isNotEmpty(exportData.getInboundRelations())) { + newRelations.addAll(exportData.getInboundRelations().stream() + .peek(relation -> relation.setTo(savedEntity.getId())) + .collect(Collectors.toList())); + + if (importSettings.isRemoveExistingRelations() && oldEntity != null) { + for (EntityRelation existingRelation : relationService.findByTo(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)) { + exportableEntitiesService.checkPermission(user, existingRelation.getFrom(), Operation.WRITE); + relationService.deleteRelation(user.getTenantId(), existingRelation); + } } } - } - if (importSettings.isImportOutboundRelations() && CollectionUtils.isNotEmpty(exportData.getOutboundRelations())) { - newRelations.addAll(exportData.getOutboundRelations().stream() - .peek(relation -> relation.setFrom(savedEntity.getId())) - .collect(Collectors.toList())); - - if (importSettings.isRemoveExistingRelations() && oldEntity != null) { - for (EntityRelation existingRelation : relationService.findByFrom(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)) { - exportableEntitiesService.checkPermission(user, existingRelation.getTo(), Operation.WRITE); - relationService.deleteRelation(user.getTenantId(), existingRelation); + if (importSettings.isImportOutboundRelations() && CollectionUtils.isNotEmpty(exportData.getOutboundRelations())) { + newRelations.addAll(exportData.getOutboundRelations().stream() + .peek(relation -> relation.setFrom(savedEntity.getId())) + .collect(Collectors.toList())); + + if (importSettings.isRemoveExistingRelations() && oldEntity != null) { + for (EntityRelation existingRelation : relationService.findByFrom(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)) { + exportableEntitiesService.checkPermission(user, existingRelation.getTo(), Operation.WRITE); + relationService.deleteRelation(user.getTenantId(), existingRelation); + } } } - } - for (EntityRelation relation : newRelations) { - HasId otherEntity = null; - if (!relation.getTo().equals(savedEntity.getId())) { - otherEntity = findInternalEntity(user.getTenantId(), relation.getTo()); - relation.setTo(otherEntity.getId()); - } - if (!relation.getFrom().equals(savedEntity.getId())) { - otherEntity = findInternalEntity(user.getTenantId(), relation.getFrom()); - relation.setFrom(otherEntity.getId()); - } - if (otherEntity != null) { - exportableEntitiesService.checkPermission(user, otherEntity, otherEntity.getId().getEntityType(), Operation.WRITE); - } + for (EntityRelation relation : newRelations) { + HasId otherEntity = null; + if (!relation.getTo().equals(savedEntity.getId())) { + otherEntity = findInternalEntity(user.getTenantId(), relation.getTo()); + relation.setTo(otherEntity.getId()); + } + if (!relation.getFrom().equals(savedEntity.getId())) { + otherEntity = findInternalEntity(user.getTenantId(), relation.getFrom()); + relation.setFrom(otherEntity.getId()); + } + if (otherEntity != null) { + exportableEntitiesService.checkPermission(user, otherEntity, otherEntity.getId().getEntityType(), Operation.WRITE); + } - relationService.saveRelation(user.getTenantId(), relation); - } + relationService.saveRelation(user.getTenantId(), relation); + } + }); - return getCallback(user, savedEntity, oldEntity); + importResult.addPushEventsCallback(() -> { + onEntitySaved(user, savedEntity, oldEntity); + }); } - protected ThrowingRunnable getCallback(SecurityUser user, E savedEntity, E oldEntity) { - return () -> { - entityActionService.logEntityAction(user, savedEntity.getId(), savedEntity, savedEntity instanceof HasCustomerId ? ((HasCustomerId) savedEntity).getCustomerId() : user.getCustomerId(), - oldEntity == null ? ActionType.ADDED : ActionType.UPDATED, null); - }; + protected void onEntitySaved(SecurityUser user, E savedEntity, E oldEntity) { + entityActionService.logEntityAction(user, savedEntity.getId(), savedEntity, + savedEntity instanceof HasCustomerId ? ((HasCustomerId) savedEntity).getCustomerId() : user.getCustomerId(), + oldEntity == null ? ActionType.ADDED : ActionType.UPDATED, null); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java index a03ee60569..4d2cfdfffb 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java @@ -20,15 +20,12 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.edge.EdgeEventActionType; -import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.CustomerExportData; -import org.thingsboard.server.service.sync.importing.EntityImportSettings; -import org.thingsboard.server.utils.ThrowingRunnable; @Service @TbCoreComponent @@ -48,12 +45,11 @@ public class CustomerImportService extends BaseEntityImportService { - if (oldCustomer != null) { - entityActionService.sendEntityNotificationMsgToEdgeService(user.getTenantId(), savedCustomer.getId(), EdgeEventActionType.UPDATED); - } - }); + protected void onEntitySaved(SecurityUser user, Customer savedCustomer, Customer oldCustomer) { + super.onEntitySaved(user, savedCustomer, oldCustomer); + if (oldCustomer != null) { + entityActionService.sendEntityNotificationMsgToEdgeService(user.getTenantId(), savedCustomer.getId(), EdgeEventActionType.UPDATED); + } } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java index e80e699c44..6463fe581c 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java @@ -35,7 +35,6 @@ import org.thingsboard.server.dao.sql.query.DefaultEntityQueryRepository; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.DashboardExportData; -import org.thingsboard.server.utils.ThrowingRunnable; import java.util.Collections; import java.util.HashSet; @@ -111,12 +110,11 @@ public class DashboardImportService extends BaseEntityImportService { - if (oldDashboard != null) { - entityActionService.sendEntityNotificationMsgToEdgeService(user.getTenantId(), savedDashboard.getId(), EdgeEventActionType.UPDATED); - } - }); + protected void onEntitySaved(SecurityUser user, Dashboard savedDashboard, Dashboard oldDashboard) { + super.onEntitySaved(user, savedDashboard, oldDashboard); + if (oldDashboard != null) { + entityActionService.sendEntityNotificationMsgToEdgeService(user.getTenantId(), savedDashboard.getId(), EdgeEventActionType.UPDATED); + } } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java index 7484fb98ba..850fea2baa 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java @@ -25,7 +25,6 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.DeviceExportData; -import org.thingsboard.server.utils.ThrowingRunnable; @Service @TbCoreComponent @@ -55,10 +54,9 @@ public class DeviceImportService extends BaseEntityImportService { - clusterService.onDeviceUpdated(savedDevice, oldDevice); - }); + protected void onEntitySaved(SecurityUser user, Device savedDevice, Device oldDevice) { + super.onEntitySaved(user, savedDevice, oldDevice); + clusterService.onDeviceUpdated(savedDevice, oldDevice); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java index 8db608fb42..b68ae2a776 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java @@ -28,7 +28,6 @@ import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.ota.OtaPackageStateService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.DeviceProfileExportData; -import org.thingsboard.server.utils.ThrowingRunnable; import java.util.Objects; @@ -55,17 +54,16 @@ public class DeviceProfileImportService extends BaseEntityImportService { - clusterService.onDeviceProfileChange(savedDeviceProfile, null); - clusterService.broadcastEntityStateChangeEvent(user.getTenantId(), savedDeviceProfile.getId(), - oldDeviceProfile == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); - entityActionService.sendEntityNotificationMsgToEdgeService(user.getTenantId(), savedDeviceProfile.getId(), - oldDeviceProfile == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); - otaPackageStateService.update(savedDeviceProfile, - oldDeviceProfile != null && !Objects.equals(oldDeviceProfile.getFirmwareId(), savedDeviceProfile.getFirmwareId()), - oldDeviceProfile != null && !Objects.equals(oldDeviceProfile.getSoftwareId(), savedDeviceProfile.getSoftwareId())); - }); + protected void onEntitySaved(SecurityUser user, DeviceProfile savedDeviceProfile, DeviceProfile oldDeviceProfile) { + super.onEntitySaved(user, savedDeviceProfile, oldDeviceProfile); + clusterService.onDeviceProfileChange(savedDeviceProfile, null); + clusterService.broadcastEntityStateChangeEvent(user.getTenantId(), savedDeviceProfile.getId(), + oldDeviceProfile == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); + entityActionService.sendEntityNotificationMsgToEdgeService(user.getTenantId(), savedDeviceProfile.getId(), + oldDeviceProfile == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); + otaPackageStateService.update(savedDeviceProfile, + oldDeviceProfile != null && !Objects.equals(oldDeviceProfile.getFirmwareId(), savedDeviceProfile.getFirmwareId()), + oldDeviceProfile != null && !Objects.equals(oldDeviceProfile.getSoftwareId(), savedDeviceProfile.getSoftwareId())); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java index ec8a0aa5a2..16f11171ae 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java @@ -32,7 +32,6 @@ import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.RuleChainExportData; -import org.thingsboard.server.utils.ThrowingRunnable; import java.util.Collections; import java.util.Optional; @@ -85,15 +84,14 @@ public class RuleChainImportService extends BaseEntityImportService { - if (savedRuleChain.getType() == RuleChainType.CORE) { - clusterService.broadcastEntityStateChangeEvent(user.getTenantId(), savedRuleChain.getId(), - oldRuleChain == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); - } else if (savedRuleChain.getType() == RuleChainType.EDGE && oldRuleChain != null) { - entityActionService.sendEntityNotificationMsgToEdgeService(user.getTenantId(), savedRuleChain.getId(), EdgeEventActionType.UPDATED); - } - }); + protected void onEntitySaved(SecurityUser user, RuleChain savedRuleChain, RuleChain oldRuleChain) { + super.onEntitySaved(user, savedRuleChain, oldRuleChain); + if (savedRuleChain.getType() == RuleChainType.CORE) { + clusterService.broadcastEntityStateChangeEvent(user.getTenantId(), savedRuleChain.getId(), + oldRuleChain == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); + } else if (savedRuleChain.getType() == RuleChainType.EDGE && oldRuleChain != null) { + entityActionService.sendEntityNotificationMsgToEdgeService(user.getTenantId(), savedRuleChain.getId(), EdgeEventActionType.UPDATED); + } } @Override diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java index 9c3c8d3ff3..603a43bf8c 100644 --- a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java @@ -48,7 +48,7 @@ import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; import org.thingsboard.server.service.sync.exporting.data.DeviceExportData; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import org.thingsboard.server.service.sync.exporting.data.RuleChainExportData; -import org.thingsboard.server.service.sync.importing.EntityImportResult; +import org.thingsboard.server.service.sync.importing.data.EntityImportResult; import java.util.List; diff --git a/dao/src/main/java/org/thingsboard/server/dao/Dao.java b/dao/src/main/java/org/thingsboard/server/dao/Dao.java index 038be61d21..663c19348d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/Dao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/Dao.java @@ -37,6 +37,8 @@ public interface Dao { T save(TenantId tenantId, T t); + T saveAndFlush(TenantId tenantId, T t); + boolean removeById(TenantId tenantId, UUID id); void removeAllByIds(Collection ids); diff --git a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java index bf8d75d27a..2c379ae91e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/asset/BaseAssetService.java @@ -117,7 +117,7 @@ public class BaseAssetService extends AbstractEntityService implements AssetServ assetValidator.validate(asset, Asset::getTenantId); Asset savedAsset; try { - savedAsset = assetDao.save(asset.getTenantId(), asset); + savedAsset = assetDao.saveAndFlush(asset.getTenantId(), asset); } catch (Exception t) { ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("asset_name_unq_key")) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java index 3c996ff6f5..ad6bb5ccec 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java @@ -67,6 +67,14 @@ public abstract class JpaAbstractDao, D> return DaoUtil.getData(entity); } + @Override + @Transactional + public D saveAndFlush(TenantId tenantId, D domain) { + D d = save(tenantId, domain); + getRepository().flush(); + return d; + } + @Override public D findById(TenantId tenantId, UUID key) { log.debug("Get entity by key {}", key); From a35b16d7cb75d2fc73bc4696634fd1e931739587 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Fri, 8 Apr 2022 18:24:50 +0300 Subject: [PATCH 20/39] Get rid of boilerplate for export --- .../EntitiesExportImportController.java | 18 +++- .../DefaultEntitiesExportImportService.java | 40 +++++---- .../sync/exporting/EntityExportService.java | 3 - .../sync/exporting/data/AssetExportData.java | 32 ------- .../exporting/data/CustomerExportData.java | 32 ------- .../exporting/data/DashboardExportData.java | 32 ------- .../sync/exporting/data/DeviceExportData.java | 6 -- .../data/DeviceProfileExportData.java | 32 ------- .../sync/exporting/data/EntityExportData.java | 31 ++++--- .../exporting/data/RuleChainExportData.java | 6 -- .../data/request/EntityExportSettings.java | 6 ++ .../exporting/impl/AssetExportService.java | 39 -------- .../impl/BaseEntityExportService.java | 60 ++----------- .../exporting/impl/CustomerExportService.java | 39 -------- .../impl/DashboardExportService.java | 39 -------- .../impl/DefaultEntityExportService.java | 90 +++++++++++++++++++ .../exporting/impl/DeviceExportService.java | 6 +- .../impl/DeviceProfileExportService.java | 39 -------- .../impl/RuleChainExportService.java | 6 +- .../importing/data/EntityImportResult.java | 3 + .../importing/impl/AssetImportService.java | 9 +- .../impl/BaseEntityImportService.java | 3 +- .../importing/impl/CustomerImportService.java | 9 +- .../impl/DashboardImportService.java | 9 +- .../importing/impl/DeviceImportService.java | 3 +- .../impl/DeviceProfileImportService.java | 9 +- .../impl/RuleChainImportService.java | 3 +- ...aseEntitiesExportImportControllerTest.java | 62 ++++++++++++- ...EntitiesExportImportControllerSqlTest.java | 60 ++++--------- 29 files changed, 277 insertions(+), 449 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/data/AssetExportData.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/data/CustomerExportData.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DashboardExportData.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceProfileExportData.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/AssetExportService.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/CustomerExportService.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DashboardExportService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceProfileExportService.java diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index e1f5dd4525..9f9e90b0ca 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -16,6 +16,7 @@ package org.thingsboard.server.controller; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -52,6 +53,7 @@ import org.thingsboard.server.service.sync.importing.data.request.ImportRequest; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -61,6 +63,7 @@ import static org.thingsboard.server.dao.sql.query.EntityKeyMapping.CREATED_TIME @RequestMapping("/api/entities") @TbCoreComponent @RequiredArgsConstructor +@Slf4j public class EntitiesExportImportController extends BaseController { private final EntitiesExportImportService exportImportService; @@ -160,7 +163,20 @@ public class EntitiesExportImportController extends BaseController { public List>> importEntities(@RequestBody ImportRequest importRequest) throws ThingsboardException { SecurityUser user = getCurrentUser(); try { - return exportImportService.importEntities(user, importRequest.getExportDataList(), importRequest.getImportSettings()); + List>> importResults = exportImportService.importEntities(user, importRequest.getExportDataList(), importRequest.getImportSettings()); + + importResults.stream() + .map(EntityImportResult::getPushEventsCallback) + .filter(Objects::nonNull) + .forEach(pushEventsCallback -> { + try { + pushEventsCallback.run(); + } catch (Exception e) { + log.error("Failed to send event for entity", e); + } + }); + + return importResults; } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java index 5df3cdde0c..eefa396d06 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java @@ -16,7 +16,6 @@ package org.thingsboard.server.service.sync; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -39,6 +38,8 @@ import org.thingsboard.server.service.sync.exporting.EntityExportService; import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; +import org.thingsboard.server.service.sync.exporting.impl.BaseEntityExportService; +import org.thingsboard.server.service.sync.exporting.impl.DefaultEntityExportService; import org.thingsboard.server.service.sync.importing.EntityImportService; import org.thingsboard.server.service.sync.importing.data.EntityImportResult; import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; @@ -56,7 +57,6 @@ import java.util.stream.Collectors; @Service @TbCoreComponent @RequiredArgsConstructor -@Slf4j public class DefaultEntitiesExportImportService implements EntitiesExportImportService, ExportableEntitiesService { private final Map> exportServices = new HashMap<>(); @@ -98,17 +98,6 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS saveReferencesCallback.run(); } - importResults.stream() - .map(EntityImportResult::getPushEventsCallback) - .filter(Objects::nonNull) - .forEach(pushEventsCallback -> { - try { - pushEventsCallback.run(); - } catch (Exception e) { - log.error("Failed to send event for entity", e); - } - }); - return importResults; } @@ -188,16 +177,31 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS return daos.get(entityType); } + @Autowired - private void setServices(Collection> exportServices, - Collection> importServices, - Collection> daos) { - exportServices.forEach(entityExportService -> { - this.exportServices.put(entityExportService.getEntityType(), entityExportService); + private void setExportServices(DefaultEntityExportService defaultExportService, + Collection> exportServices) { + exportServices.stream() + .sorted(Comparator.comparing(exportService -> exportService.getSupportedEntityTypes().size(), Comparator.reverseOrder())) + .forEach(exportService -> { + exportService.getSupportedEntityTypes().forEach(entityType -> { + this.exportServices.put(entityType, exportService); + }); + }); + SUPPORTED_ENTITY_TYPES.forEach(entityType -> { + this.exportServices.putIfAbsent(entityType, defaultExportService); }); + } + + @Autowired + private void setImportServices(Collection> importServices) { importServices.forEach(entityImportService -> { this.importServices.put(entityImportService.getEntityType(), entityImportService); }); + } + + @Autowired + private void setDaos(Collection> daos) { daos.forEach(dao -> { if (dao.getEntityType() != null) { this.daos.put(dao.getEntityType(), dao); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java index c09cbcd36c..73f946e722 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.sync.exporting; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; @@ -27,6 +26,4 @@ public interface EntityExportService { - - @Override - public EntityType getEntityType() { - return EntityType.ASSET; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/CustomerExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/CustomerExportData.java deleted file mode 100644 index 08888f4a35..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/CustomerExportData.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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.sync.exporting.data; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.EntityType; - -@EqualsAndHashCode(callSuper = true) -@Data -public class CustomerExportData extends EntityExportData { - - @Override - public EntityType getEntityType() { - return EntityType.CUSTOMER; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DashboardExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DashboardExportData.java deleted file mode 100644 index 6f101816c9..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DashboardExportData.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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.sync.exporting.data; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.Dashboard; -import org.thingsboard.server.common.data.EntityType; - -@EqualsAndHashCode(callSuper = true) -@Data -public class DashboardExportData extends EntityExportData { - - @Override - public EntityType getEntityType() { - return EntityType.DASHBOARD; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceExportData.java index 977bd22f82..30d84c0a44 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceExportData.java @@ -18,7 +18,6 @@ package org.thingsboard.server.service.sync.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.security.DeviceCredentials; @EqualsAndHashCode(callSuper = true) @@ -27,9 +26,4 @@ public class DeviceExportData extends EntityExportData { private DeviceCredentials credentials; - @Override - public EntityType getEntityType() { - return EntityType.DEVICE; - } - } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceProfileExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceProfileExportData.java deleted file mode 100644 index 7b71749be4..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceProfileExportData.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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.sync.exporting.data; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.EntityType; - -@EqualsAndHashCode(callSuper = true) -@Data -public class DeviceProfileExportData extends EntityExportData { - - @Override - public EntityType getEntityType() { - return EntityType.DEVICE_PROFILE; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java index 51b5615c9e..bf44b0dc37 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java @@ -15,39 +15,48 @@ */ package org.thingsboard.server.service.sync.exporting.data; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.rule.RuleChain; import java.util.List; @JsonIgnoreProperties(ignoreUnknown = true) -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "entityType") +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "entityType", defaultImpl = EntityExportData.class, visible = true) @JsonSubTypes({ @Type(name = "DEVICE", value = DeviceExportData.class), - @Type(name = "DEVICE_PROFILE", value = DeviceProfileExportData.class), - @Type(name = "ASSET", value = AssetExportData.class), - @Type(name = "RULE_CHAIN", value = RuleChainExportData.class), - @Type(name = "DASHBOARD", value = DashboardExportData.class), - @Type(name = "CUSTOMER", value = CustomerExportData.class) + @Type(name = "RULE_CHAIN", value = RuleChainExportData.class) }) @JsonInclude(JsonInclude.Include.NON_NULL) @Data -public abstract class EntityExportData> { +public class EntityExportData> { + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "entityType", include = JsonTypeInfo.As.EXTERNAL_PROPERTY) + @JsonSubTypes({ + @Type(name = "DEVICE", value = Device.class), + @Type(name = "RULE_CHAIN", value = RuleChain.class), + @Type(name = "DEVICE_PROFILE", value = DeviceProfile.class), + @Type(name = "ASSET", value = Asset.class), + @Type(name = "DASHBOARD", value = Dashboard.class), + @Type(name = "CUSTOMER", value = Customer.class) + }) private E entity; + private EntityType entityType; + private List inboundRelations; private List outboundRelations; - @JsonIgnore - public abstract EntityType getEntityType(); - } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/RuleChainExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/RuleChainExportData.java index 9f59aa4401..025459f27e 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/RuleChainExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/RuleChainExportData.java @@ -17,7 +17,6 @@ package org.thingsboard.server.service.sync.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; @@ -27,9 +26,4 @@ public class RuleChainExportData extends EntityExportData { private RuleChainMetaData metaData; - @Override - public EntityType getEntityType() { - return EntityType.RULE_CHAIN; - } - } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityExportSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityExportSettings.java index 01e4f8d068..ddf2e43bcf 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityExportSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityExportSettings.java @@ -15,9 +15,15 @@ */ package org.thingsboard.server.service.sync.exporting.data.request; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; @Data +@AllArgsConstructor +@NoArgsConstructor +@Builder public class EntityExportSettings { private boolean exportInboundRelations; private boolean exportOutboundRelations; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/AssetExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/AssetExportService.java deleted file mode 100644 index b4f506a1f5..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/AssetExportService.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * 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.sync.exporting.impl; - -import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.asset.Asset; -import org.thingsboard.server.common.data.id.AssetId; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.sync.exporting.data.AssetExportData; - -@Service -@TbCoreComponent -public class AssetExportService extends BaseEntityExportService { - - @Override - protected AssetExportData newExportData() { - return new AssetExportData(); - } - - @Override - public EntityType getEntityType() { - return EntityType.ASSET; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java index 5d693e25b6..7b5f758f93 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java @@ -15,71 +15,29 @@ */ package org.thingsboard.server.service.sync.exporting.impl; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.data.relation.RelationTypeGroup; -import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.security.permission.Operation; -import org.thingsboard.server.service.sync.exporting.EntityExportService; -import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; -import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; -import java.util.List; - -public abstract class BaseEntityExportService, D extends EntityExportData> implements EntityExportService { +import java.util.Set; - @Autowired @Lazy - private ExportableEntitiesService exportableEntitiesService; - @Autowired - private RelationService relationService; +public abstract class BaseEntityExportService, D extends EntityExportData> extends DefaultEntityExportService { @Override - public final D getExportData(SecurityUser user, I entityId, EntityExportSettings exportSettings) throws ThingsboardException { - D exportData = newExportData(); - - E entity = exportableEntitiesService.findEntityByTenantIdAndId(user.getTenantId(), entityId); - if (entity == null) { - throw new IllegalArgumentException("Entity not found"); - } - exportableEntitiesService.checkPermission(user, entity, getEntityType(), Operation.READ); - - exportData.setEntity(entity); - setRelatedEntities(user.getTenantId(), entity, exportData); - setAdditionalExportData(user, entity, exportData, exportSettings); - - return exportData; + protected void setAdditionalExportData(SecurityUser user, E entity, D exportData, EntityExportSettings exportSettings) throws ThingsboardException { + setRelatedEntities(user.getTenantId(), entity, (D) exportData); + super.setAdditionalExportData(user, entity, exportData, exportSettings); } protected void setRelatedEntities(TenantId tenantId, E mainEntity, D exportData) {} - protected void setAdditionalExportData(SecurityUser user, E entity, D exportData, EntityExportSettings exportSettings) throws ThingsboardException { - if (exportSettings.isExportInboundRelations()) { - List inboundRelations = relationService.findByTo(user.getTenantId(), entity.getId(), RelationTypeGroup.COMMON); - if (inboundRelations != null) { - for (EntityRelation relation : inboundRelations) { - exportableEntitiesService.checkPermission(user, relation.getFrom(), Operation.READ); - } - } - exportData.setInboundRelations(inboundRelations); - } - if (exportSettings.isExportOutboundRelations()) { - List outboundRelations = relationService.findByFrom(user.getTenantId(), entity.getId(), RelationTypeGroup.COMMON); - if (outboundRelations != null) { - for (EntityRelation relation : outboundRelations) { - exportableEntitiesService.checkPermission(user, relation.getTo(), Operation.READ); - } - } - exportData.setOutboundRelations(outboundRelations); - } - } - protected abstract D newExportData(); + public abstract Set getSupportedEntityTypes(); + } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/CustomerExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/CustomerExportService.java deleted file mode 100644 index c90d21e573..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/CustomerExportService.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * 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.sync.exporting.impl; - -import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.sync.exporting.data.CustomerExportData; - -@Service -@TbCoreComponent -public class CustomerExportService extends BaseEntityExportService { - - @Override - protected CustomerExportData newExportData() { - return new CustomerExportData(); - } - - @Override - public EntityType getEntityType() { - return EntityType.CUSTOMER; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DashboardExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DashboardExportService.java deleted file mode 100644 index 32584ca917..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DashboardExportService.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * 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.sync.exporting.impl; - -import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.Dashboard; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.id.DashboardId; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.sync.exporting.data.DashboardExportData; - -@Service -@TbCoreComponent -public class DashboardExportService extends BaseEntityExportService { - - @Override - protected DashboardExportData newExportData() { - return new DashboardExportData(); - } - - @Override - public EntityType getEntityType() { - return EntityType.DASHBOARD; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java new file mode 100644 index 0000000000..4fe09e9322 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java @@ -0,0 +1,90 @@ +/** + * 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.sync.exporting.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; +import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.sync.exporting.EntityExportService; +import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; +import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; + +import java.util.List; + +@Service +@TbCoreComponent +@Primary +public class DefaultEntityExportService, D extends EntityExportData> implements EntityExportService { + + @Autowired @Lazy + private ExportableEntitiesService exportableEntitiesService; + @Autowired + private RelationService relationService; + + @Override + public final D getExportData(SecurityUser user, I entityId, EntityExportSettings exportSettings) throws ThingsboardException { + D exportData = newExportData(); + + E entity = exportableEntitiesService.findEntityByTenantIdAndId(user.getTenantId(), entityId); + if (entity == null) { + throw new IllegalArgumentException(entityId.getEntityType() + " [" + entityId.getId() + "] not found"); + } + exportableEntitiesService.checkPermission(user, entity, entity.getId().getEntityType(), Operation.READ); + + exportData.setEntity(entity); + exportData.setEntityType(entityId.getEntityType()); + setAdditionalExportData(user, entity, exportData, exportSettings); + + return exportData; + } + + protected void setAdditionalExportData(SecurityUser user, E entity, D exportData, EntityExportSettings exportSettings) throws ThingsboardException { + if (exportSettings.isExportInboundRelations()) { + List inboundRelations = relationService.findByTo(user.getTenantId(), entity.getId(), RelationTypeGroup.COMMON); + if (inboundRelations != null) { + for (EntityRelation relation : inboundRelations) { + exportableEntitiesService.checkPermission(user, relation.getFrom(), Operation.READ); + } + } + exportData.setInboundRelations(inboundRelations); + } + if (exportSettings.isExportOutboundRelations()) { + List outboundRelations = relationService.findByFrom(user.getTenantId(), entity.getId(), RelationTypeGroup.COMMON); + if (outboundRelations != null) { + for (EntityRelation relation : outboundRelations) { + exportableEntitiesService.checkPermission(user, relation.getTo(), Operation.READ); + } + } + exportData.setOutboundRelations(outboundRelations); + } + } + + protected D newExportData() { + return (D) new EntityExportData(); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceExportService.java index d31a859013..da1e4f3a96 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceExportService.java @@ -25,6 +25,8 @@ import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.sync.exporting.data.DeviceExportData; +import java.util.Set; + @Service @TbCoreComponent @RequiredArgsConstructor @@ -43,8 +45,8 @@ public class DeviceExportService extends BaseEntityExportService getSupportedEntityTypes() { + return Set.of(EntityType.DEVICE); } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceProfileExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceProfileExportService.java deleted file mode 100644 index b3fd65363b..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceProfileExportService.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * 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.sync.exporting.impl; - -import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.sync.exporting.data.DeviceProfileExportData; - -@Service -@TbCoreComponent -public class DeviceProfileExportService extends BaseEntityExportService { - - @Override - protected DeviceProfileExportData newExportData() { - return new DeviceProfileExportData(); - } - - @Override - public EntityType getEntityType() { - return EntityType.DEVICE_PROFILE; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/RuleChainExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/RuleChainExportService.java index 846d4006d0..e814cd6ce7 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/RuleChainExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/RuleChainExportService.java @@ -25,6 +25,8 @@ import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.sync.exporting.data.RuleChainExportData; +import java.util.Set; + @Service @TbCoreComponent @RequiredArgsConstructor @@ -43,8 +45,8 @@ public class RuleChainExportService extends BaseEntityExportService getSupportedEntityTypes() { + return Set.of(EntityType.RULE_CHAIN); } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportResult.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportResult.java index e51048c433..7160a483b3 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportResult.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportResult.java @@ -18,6 +18,7 @@ package org.thingsboard.server.service.sync.importing.data; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; import lombok.Setter; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.utils.ThrowingRunnable; @@ -27,6 +28,8 @@ public class EntityImportResult> private E savedEntity; @Getter @Setter private E oldEntity; + @Getter @Setter + private EntityType entityType; @Getter @JsonIgnore private ThrowingRunnable saveReferencesCallback = () -> {}; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java index 0c8587270a..02eaae27b5 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java @@ -20,17 +20,18 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.exporting.data.AssetExportData; +import org.thingsboard.server.service.sync.exporting.data.EntityExportData; @Service @TbCoreComponent @RequiredArgsConstructor -public class AssetImportService extends BaseEntityImportService { +public class AssetImportService extends BaseEntityImportService> { private final AssetService assetService; @@ -41,12 +42,12 @@ public class AssetImportService extends BaseEntityImportService exportData, NewIdProvider idProvider) { return assetService.saveAsset(asset); } @Override - protected void onEntitySaved(SecurityUser user, Asset savedAsset, Asset oldAsset) { + protected void onEntitySaved(SecurityUser user, Asset savedAsset, Asset oldAsset) throws ThingsboardException { super.onEntitySaved(user, savedAsset, oldAsset); if (oldAsset != null) { entityActionService.sendEntityNotificationMsgToEdgeService(user.getTenantId(), savedAsset.getId(), EdgeEventActionType.UPDATED); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java index b7602a53bf..b576ae1e60 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java @@ -84,6 +84,7 @@ public abstract class BaseEntityImportService importResult = new EntityImportResult<>(); importResult.setSavedEntity(savedEntity); importResult.setOldEntity(existingEntity); + importResult.setEntityType(getEntityType()); processAfterSaved(user, importResult, exportData, idProvider, importSettings); @@ -150,7 +151,7 @@ public abstract class BaseEntityImportService { +public class CustomerImportService extends BaseEntityImportService> { private final CustomerService customerService; @@ -40,12 +41,12 @@ public class CustomerImportService extends BaseEntityImportService exportData, NewIdProvider idProvider) { return customerService.saveCustomer(customer); } @Override - protected void onEntitySaved(SecurityUser user, Customer savedCustomer, Customer oldCustomer) { + protected void onEntitySaved(SecurityUser user, Customer savedCustomer, Customer oldCustomer) throws ThingsboardException { super.onEntitySaved(user, savedCustomer, oldCustomer); if (oldCustomer != null) { entityActionService.sendEntityNotificationMsgToEdgeService(user.getTenantId(), savedCustomer.getId(), EdgeEventActionType.UPDATED); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java index 6463fe581c..3e06480a2f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ShortCustomerInfo; import org.thingsboard.server.common.data.edge.EdgeEventActionType; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.EntityId; @@ -34,7 +35,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.sql.query.DefaultEntityQueryRepository; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.exporting.data.DashboardExportData; +import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import java.util.Collections; import java.util.HashSet; @@ -46,7 +47,7 @@ import java.util.stream.Collectors; @Service @TbCoreComponent @RequiredArgsConstructor -public class DashboardImportService extends BaseEntityImportService { +public class DashboardImportService extends BaseEntityImportService> { private final DashboardService dashboardService; @@ -58,7 +59,7 @@ public class DashboardImportService extends BaseEntityImportService exportData, NewIdProvider idProvider) { Optional.ofNullable(dashboard.getConfiguration()) .flatMap(configuration -> Optional.ofNullable(configuration.get("entityAliases"))) .filter(JsonNode::isObject) @@ -110,7 +111,7 @@ public class DashboardImportService extends BaseEntityImportService { +public class DeviceProfileImportService extends BaseEntityImportService> { private final DeviceProfileService deviceProfileService; private final OtaPackageStateService otaPackageStateService; @@ -45,7 +46,7 @@ public class DeviceProfileImportService extends BaseEntityImportService exportData, NewIdProvider idProvider) { deviceProfile.setDefaultRuleChainId(idProvider.get(DeviceProfile::getDefaultRuleChainId)); deviceProfile.setDefaultDashboardId(idProvider.get(DeviceProfile::getDefaultDashboardId)); deviceProfile.setFirmwareId(idProvider.get(DeviceProfile::getFirmwareId)); @@ -54,7 +55,7 @@ public class DeviceProfileImportService extends BaseEntityImportService, I extends EntityId> EntityExportData exportSingleEntity(I entityId) throws Exception { + SingleEntityExportRequest exportRequest = new SingleEntityExportRequest(); + exportRequest.setEntityId(entityId); + exportRequest.setExportSettings(new EntityExportSettings()); + return (EntityExportData) exportEntities(exportRequest).get(0); + } + + protected List>> exportEntities(ExportRequest exportRequest) throws Exception { + return getResponse(doPost("/api/entities/export", exportRequest), new TypeReference>>>() {}); + } + + protected List>> exportEntities(List exportRequests) throws Exception { + return getResponse(doPost("/api/entities/export?multiple", exportRequests), new TypeReference>>>() {}); + } + + + protected , I extends EntityId> EntityImportResult importEntity(EntityExportData exportData) throws Exception { + return (EntityImportResult) importEntities(List.of((EntityExportData>)exportData)).get(0); + } + + protected List>> importEntities(List>> exportDataList) throws Exception { + ImportRequest importRequest = new ImportRequest(); + importRequest.setImportSettings(EntityImportSettings.builder() + .updateReferencesToOtherEntities(true) + .build()); + importRequest.setExportDataList(exportDataList); + return getResponse(doPost("/api/entities/import", importRequest), new TypeReference>>>() { + @Override + public Type getType() { + return mapper.getTypeFactory().constructCollectionType(List.class, + mapper.getTypeFactory().constructParametricType(EntityImportResult.class, + exportDataList.get(0).getEntity().getClass())); + } + }); + } + + protected T getResponse(ResultActions resultActions, TypeReference typeReference) throws Exception { + try { + return readResponse(resultActions.andExpect(status().isOk()), typeReference); + } catch (AssertionError e) { + throw new AssertionError(readResponse(resultActions, String.class), e); + } + } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java index 603a43bf8c..ccbf311bef 100644 --- a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java @@ -15,15 +15,10 @@ */ package org.thingsboard.server.controller.sql; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JavaType; -import lombok.SneakyThrows; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.web.servlet.ResultActions; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Device; @@ -33,7 +28,7 @@ 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.asset.Asset; -import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; @@ -50,12 +45,8 @@ import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import org.thingsboard.server.service.sync.exporting.data.RuleChainExportData; import org.thingsboard.server.service.sync.importing.data.EntityImportResult; -import java.util.List; - import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@Ignore @DaoSqlTest public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImportControllerTest { @@ -110,7 +101,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp assertThat(exportData.getEntity()).isEqualTo(asset); logInAsTenantAdmin2(); - EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + EntityImportResult importResult = importEntity(exportData); checkImportedEntity(tenantId1, asset, tenantId2, importResult); checkImportedAssetData(asset, importResult.getSavedEntity()); @@ -122,7 +113,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp Asset asset = createAsset(tenantId1, null, "AB", "Asset v1.0"); EntityExportData exportData = exportSingleEntity(asset.getId()); - EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + EntityImportResult importResult = importEntity(exportData); checkImportedEntity(tenantId1, asset, tenantId1, importResult); checkImportedAssetData(asset, importResult.getSavedEntity()); @@ -138,8 +129,8 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp EntityExportData assetExportData = exportSingleEntity(asset.getId()); logInAsTenantAdmin2(); - Customer importedCustomer = importEntities(List.of(customerExportData)).get(0).getSavedEntity(); - Asset importedAsset = importEntities(List.of(assetExportData)).get(0).getSavedEntity(); + Customer importedCustomer = importEntity(customerExportData).getSavedEntity(); + Asset importedAsset = importEntity(assetExportData).getSavedEntity(); assertThat(importedAsset.getCustomerId()).isEqualTo(importedCustomer.getId()); } @@ -150,7 +141,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp Customer customer = createCustomer(tenantId1, "My customer"); Asset asset = createAsset(tenantId1, customer.getId(), "AB", "My asset"); - Asset importedAsset = this.importEntities(List.of(exportSingleEntity(asset.getId()))).get(0).getSavedEntity(); + Asset importedAsset = importEntity(this.exportSingleEntity(asset.getId())).getSavedEntity(); assertThat(importedAsset.getCustomerId()).isEqualTo(asset.getCustomerId()); } @@ -171,7 +162,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp assertThat(exportData.getEntity()).isEqualTo(customer); logInAsTenantAdmin2(); - EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + EntityImportResult importResult = importEntity(exportData); checkImportedEntity(tenantId1, customer, tenantId2, importResult); checkImportedCustomerData(customer, importResult.getSavedEntity()); @@ -183,7 +174,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp Customer customer = createCustomer(tenantAdmin1.getTenantId(), "Customer v1.0"); EntityExportData exportData = exportSingleEntity(customer.getId()); - EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + EntityImportResult importResult = importEntity(exportData); checkImportedEntity(tenantId1, customer, tenantId1, importResult); checkImportedCustomerData(customer, importResult.getSavedEntity()); @@ -214,12 +205,12 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp exportedCredentials.setCredentialsId(credentials.getCredentialsId() + "a"); logInAsTenantAdmin2(); - EntityImportResult profileImportResult = importEntities(List.of(profileExportData)).get(0); + EntityImportResult profileImportResult = importEntity(profileExportData); checkImportedEntity(tenantId1, deviceProfile, tenantId2, profileImportResult); checkImportedDeviceProfileData(deviceProfile, profileImportResult.getSavedEntity()); - EntityImportResult deviceImportResult = importEntities(List.of(deviceExportData)).get(0); + EntityImportResult deviceImportResult = importEntity(deviceExportData); Device importedDevice = deviceImportResult.getSavedEntity(); checkImportedEntity(tenantId1, device, tenantId2, deviceImportResult); checkImportedDeviceData(device, importedDevice); @@ -242,7 +233,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp EntityExportData deviceExportData = exportSingleEntity(device.getId()); - EntityImportResult importResult = importEntities(List.of(deviceExportData)).get(0); + EntityImportResult importResult = importEntity(deviceExportData); Device importedDevice = importResult.getSavedEntity(); checkImportedEntity(tenantId1, device, tenantId1, importResult); @@ -277,7 +268,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp assertThat(((RuleChainExportData) exportData).getMetaData()).isEqualTo(metaData); logInAsTenantAdmin2(); - EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + EntityImportResult importResult = importEntity(exportData); RuleChain importedRuleChain = importResult.getSavedEntity(); RuleChainMetaData importedMetaData = ruleChainService.loadRuleChainMetaData(tenantId2, importedRuleChain.getId()); @@ -293,7 +284,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp EntityExportData exportData = exportSingleEntity(ruleChain.getId()); - EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + EntityImportResult importResult = importEntity(exportData); RuleChain importedRuleChain = importResult.getSavedEntity(); RuleChainMetaData importedMetaData = ruleChainService.loadRuleChainMetaData(tenantId1, importedRuleChain.getId()); @@ -330,7 +321,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp assertThat(exportData.getEntity()).isEqualTo(dashboard); logInAsTenantAdmin2(); - EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + EntityImportResult importResult = importEntity(exportData); checkImportedEntity(tenantId1, dashboard, tenantId2, importResult); checkImportedDashboardData(dashboard, importResult.getSavedEntity()); } @@ -342,7 +333,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp EntityExportData exportData = exportSingleEntity(dashboard.getId()); - EntityImportResult importResult = importEntities(List.of(exportData)).get(0); + EntityImportResult importResult = importEntity(exportData); checkImportedEntity(tenantId1, dashboard, tenantId1, importResult); checkImportedDashboardData(dashboard, importResult.getSavedEntity()); } @@ -376,27 +367,6 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp } - @SneakyThrows - private > List> importEntities(List> exportDataList) { - String requestJson = mapper.writerFor(new TypeReference>>() {}).writeValueAsString(exportDataList); - ResultActions resultActions = doPost("/api/entities/import", (Object) requestJson); - - try { - String responseJson = resultActions.andExpect(status().isOk()).andReturn().getResponse().getContentAsString(); - JavaType type = mapper.getTypeFactory().constructCollectionType(List.class, - mapper.getTypeFactory().constructParametricType(EntityImportResult.class, exportDataList.get(0).getEntity().getClass())); - return mapper.readValue(responseJson, type); - } catch (AssertionError e) { - throw new AssertionError(readResponse(resultActions, String.class), e); - } - } - - private , I extends EntityId> EntityExportData exportSingleEntity(I entityId) throws Exception { - return readResponse(doPost("/api/entities/export/" + entityId.getEntityType() + "/" + entityId.getId().toString()) - .andExpect(status().isOk()), new TypeReference>() {}); - } - - private void logInAsTenantAdmin1() throws Exception { login(tenantAdmin1.getEmail(), "12345678"); } From c04a0ff3a6fbef52c93a40bd56a9eb26fb3aea63 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Tue, 12 Apr 2022 13:04:25 +0300 Subject: [PATCH 21/39] More tests for export/import api; improvements --- .../EntitiesExportImportController.java | 45 +- .../DefaultEntitiesExportImportService.java | 10 +- .../sync/EntitiesExportImportService.java | 2 +- .../sync/exporting/data/EntityExportData.java | 19 +- ...a => CustomEntityFilterExportRequest.java} | 4 +- ...va => CustomEntityQueryExportRequest.java} | 4 +- .../exporting/data/request/ExportRequest.java | 6 +- .../data/request/ExportRequestType.java | 4 +- .../importing/data/EntityImportResult.java | 21 +- .../importing/data/EntityImportSettings.java | 2 + .../importing/data/request/ImportRequest.java | 2 +- .../impl/BaseEntityImportService.java | 33 +- .../impl/DashboardImportService.java | 4 +- .../impl/RuleChainImportService.java | 4 +- .../server/utils/JsonTbEntity.java | 47 ++ ...aseEntitiesExportImportControllerTest.java | 197 ++++- ...EntitiesExportImportControllerSqlTest.java | 731 ++++++++++++++---- .../server/common/data/ExportableEntity.java | 3 + 18 files changed, 875 insertions(+), 263 deletions(-) rename application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/{EntityFilterExportRequest.java => CustomEntityFilterExportRequest.java} (89%) rename application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/{EntityQueryExportRequest.java => CustomEntityQueryExportRequest.java} (89%) create mode 100644 application/src/main/java/org/thingsboard/server/utils/JsonTbEntity.java diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index 9f9e90b0ca..4a1badd38e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -17,13 +17,13 @@ package org.thingsboard.server.controller; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.dao.DataAccessException; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; @@ -41,9 +41,9 @@ import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.EntitiesExportImportService; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.exporting.data.request.EntityFilterExportRequest; +import org.thingsboard.server.service.sync.exporting.data.request.CustomEntityFilterExportRequest; +import org.thingsboard.server.service.sync.exporting.data.request.CustomEntityQueryExportRequest; import org.thingsboard.server.service.sync.exporting.data.request.EntityListExportRequest; -import org.thingsboard.server.service.sync.exporting.data.request.EntityQueryExportRequest; import org.thingsboard.server.service.sync.exporting.data.request.EntityTypeExportRequest; import org.thingsboard.server.service.sync.exporting.data.request.ExportRequest; import org.thingsboard.server.service.sync.exporting.data.request.SingleEntityExportRequest; @@ -71,7 +71,7 @@ public class EntitiesExportImportController extends BaseController { @PostMapping("/export") @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public List>> exportEntities(@RequestBody ExportRequest exportRequest) throws ThingsboardException { + public List> exportEntities(@RequestBody ExportRequest exportRequest) throws ThingsboardException { SecurityUser user = getCurrentUser(); try { return exportEntitiesByRequest(user, exportRequest); @@ -82,10 +82,10 @@ public class EntitiesExportImportController extends BaseController { @PostMapping(value = "/export", params = {"multiple"}) @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public List>> exportEntities(@RequestBody List exportRequests) throws ThingsboardException { + public List> exportEntities(@RequestBody List exportRequests) throws ThingsboardException { SecurityUser user = getCurrentUser(); try { - List>> exportDataList = new ArrayList<>(); + List> exportDataList = new ArrayList<>(); for (ExportRequest exportRequest : exportRequests) { exportDataList.addAll(exportEntitiesByRequest(user, exportRequest)); } @@ -96,10 +96,10 @@ public class EntitiesExportImportController extends BaseController { } - private List>> exportEntitiesByRequest(SecurityUser user, ExportRequest request) throws ThingsboardException { + private List> exportEntitiesByRequest(SecurityUser user, ExportRequest request) throws ThingsboardException { List entitiesIds = findEntitiesForRequest(user, request); - List>> exportDataList = new ArrayList<>(); + List> exportDataList = new ArrayList<>(); for (EntityId entityId : entitiesIds) { exportDataList.add(exportImportService.exportEntity(user, entityId, request.getExportSettings())); } @@ -122,15 +122,15 @@ public class EntitiesExportImportController extends BaseController { CustomerId customerId = Optional.ofNullable(exportRequest.getCustomerId()).orElse(emptyId(EntityType.CUSTOMER)); return findEntitiesByFilter(user.getTenantId(), customerId, entityTypeFilter, exportRequest.getPage(), exportRequest.getPageSize()); } - case ENTITY_FILTER: { - EntityFilterExportRequest exportRequest = (EntityFilterExportRequest) request; + case CUSTOM_ENTITY_FILTER: { + CustomEntityFilterExportRequest exportRequest = (CustomEntityFilterExportRequest) request; EntityFilter filter = exportRequest.getFilter(); CustomerId customerId = Optional.ofNullable(exportRequest.getCustomerId()).orElse(emptyId(EntityType.CUSTOMER)); return findEntitiesByFilter(user.getTenantId(), customerId, filter, exportRequest.getPage(), exportRequest.getPageSize()); } - case ENTITY_QUERY:{ - EntityQueryExportRequest exportRequest = (EntityQueryExportRequest) request; + case CUSTOM_ENTITY_QUERY: { + CustomEntityQueryExportRequest exportRequest = (CustomEntityQueryExportRequest) request; EntityDataQuery query = exportRequest.getQuery(); CustomerId customerId = Optional.ofNullable(exportRequest.getCustomerId()).orElse(emptyId(EntityType.CUSTOMER)); @@ -153,24 +153,29 @@ public class EntitiesExportImportController extends BaseController { } private List findEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) { - return entityService.findEntityDataByQuery(tenantId, customerId, query).getData().stream() - .map(EntityData::getEntityId) - .collect(Collectors.toList()); + try { + return entityService.findEntityDataByQuery(tenantId, customerId, query).getData().stream() + .map(EntityData::getEntityId) + .collect(Collectors.toList()); + } catch (DataAccessException e) { + log.error("Failed to find entity data by query: {}", e.getMessage()); + throw new IllegalArgumentException("Entity filter cannot be processed"); + } } @PostMapping("/import") - public List>> importEntities(@RequestBody ImportRequest importRequest) throws ThingsboardException { + public List> importEntities(@RequestBody ImportRequest importRequest) throws ThingsboardException { SecurityUser user = getCurrentUser(); try { - List>> importResults = exportImportService.importEntities(user, importRequest.getExportDataList(), importRequest.getImportSettings()); + List> importResults = exportImportService.importEntities(user, importRequest.getExportDataList(), importRequest.getImportSettings()); importResults.stream() - .map(EntityImportResult::getPushEventsCallback) + .map(EntityImportResult::getSendEventsCallback) .filter(Objects::nonNull) - .forEach(pushEventsCallback -> { + .forEach(sendEventsCallback -> { try { - pushEventsCallback.run(); + sendEventsCallback.run(); } catch (Exception e) { log.error("Failed to send event for entity", e); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java index eefa396d06..cfb895c36a 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java @@ -67,7 +67,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS protected static final List SUPPORTED_ENTITY_TYPES = List.of( EntityType.CUSTOMER, EntityType.ASSET, EntityType.RULE_CHAIN, - EntityType.DEVICE_PROFILE, EntityType.DEVICE, EntityType.DASHBOARD + EntityType.DASHBOARD, EntityType.DEVICE_PROFILE, EntityType.DEVICE ); @@ -82,12 +82,12 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS @Transactional(rollbackFor = Exception.class) @Override - public List>> importEntities(SecurityUser user, List>> exportDataList, EntityImportSettings importSettings) throws ThingsboardException { + public List> importEntities(SecurityUser user, List> exportDataList, EntityImportSettings importSettings) throws ThingsboardException { exportDataList.sort(Comparator.comparing(exportData -> SUPPORTED_ENTITY_TYPES.indexOf(exportData.getEntityType()))); - List>> importResults = new ArrayList<>(); + List> importResults = new ArrayList<>(); - for (EntityExportData> exportData : exportDataList) { - EntityImportResult> importResult = importEntity(user, exportData, importSettings); + for (EntityExportData exportData : exportDataList) { + EntityImportResult importResult = importEntity(user, exportData, importSettings); importResults.add(importResult); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java index b0690ea9ca..eaaf01ba2c 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java @@ -30,6 +30,6 @@ public interface EntitiesExportImportService { , I extends EntityId> EntityExportData exportEntity(SecurityUser user, I entityId, EntityExportSettings exportSettings) throws ThingsboardException; - List>> importEntities(SecurityUser user, List>> exportDataList, EntityImportSettings importSettings) throws ThingsboardException; + List> importEntities(SecurityUser user, List> exportDataList, EntityImportSettings importSettings) throws ThingsboardException; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java index bf44b0dc37..4ab6dd57b1 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java @@ -21,21 +21,16 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; -import org.thingsboard.server.common.data.Customer; -import org.thingsboard.server.common.data.Dashboard; -import org.thingsboard.server.common.data.Device; -import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; -import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.relation.EntityRelation; -import org.thingsboard.server.common.data.rule.RuleChain; +import org.thingsboard.server.utils.JsonTbEntity; import java.util.List; @JsonIgnoreProperties(ignoreUnknown = true) -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "entityType", defaultImpl = EntityExportData.class, visible = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "entityType", defaultImpl = EntityExportData.class) @JsonSubTypes({ @Type(name = "DEVICE", value = DeviceExportData.class), @Type(name = "RULE_CHAIN", value = RuleChainExportData.class) @@ -44,15 +39,7 @@ import java.util.List; @Data public class EntityExportData> { - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "entityType", include = JsonTypeInfo.As.EXTERNAL_PROPERTY) - @JsonSubTypes({ - @Type(name = "DEVICE", value = Device.class), - @Type(name = "RULE_CHAIN", value = RuleChain.class), - @Type(name = "DEVICE_PROFILE", value = DeviceProfile.class), - @Type(name = "ASSET", value = Asset.class), - @Type(name = "DASHBOARD", value = Dashboard.class), - @Type(name = "CUSTOMER", value = Customer.class) - }) + @JsonTbEntity private E entity; private EntityType entityType; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityFilterExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityFilterExportRequest.java similarity index 89% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityFilterExportRequest.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityFilterExportRequest.java index 00ef0604e5..02cbca98d6 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityFilterExportRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityFilterExportRequest.java @@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.query.EntityFilter; @EqualsAndHashCode(callSuper = true) @Data -public class EntityFilterExportRequest extends ExportRequest { +public class CustomEntityFilterExportRequest extends ExportRequest { private EntityFilter filter; private int page; @@ -31,7 +31,7 @@ public class EntityFilterExportRequest extends ExportRequest { @Override public ExportRequestType getType() { - return ExportRequestType.ENTITY_FILTER; + return ExportRequestType.CUSTOM_ENTITY_FILTER; } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityQueryExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityQueryExportRequest.java similarity index 89% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityQueryExportRequest.java rename to application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityQueryExportRequest.java index 85232821d7..76d258e891 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityQueryExportRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityQueryExportRequest.java @@ -22,14 +22,14 @@ import org.thingsboard.server.common.data.query.EntityDataQuery; @EqualsAndHashCode(callSuper = true) @Data -public class EntityQueryExportRequest extends ExportRequest { +public class CustomEntityQueryExportRequest extends ExportRequest { private EntityDataQuery query; private CustomerId customerId; @Override public ExportRequestType getType() { - return ExportRequestType.ENTITY_QUERY; + return ExportRequestType.CUSTOM_ENTITY_QUERY; } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequest.java index c140786d06..af0f161227 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequest.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.sync.exporting.data.request; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo; @@ -27,15 +26,14 @@ import lombok.Data; @Type(name = "SINGLE_ENTITY", value = SingleEntityExportRequest.class), @Type(name = "ENTITY_LIST", value = EntityListExportRequest.class), @Type(name = "ENTITY_TYPE", value = EntityTypeExportRequest.class), - @Type(name = "ENTITY_FILTER", value = EntityFilterExportRequest.class), - @Type(name = "ENTITY_QUERY", value = EntityQueryExportRequest.class) + @Type(name = "CUSTOM_ENTITY_FILTER", value = CustomEntityFilterExportRequest.class), + @Type(name = "CUSTOM_ENTITY_QUERY", value = CustomEntityQueryExportRequest.class) }) @Data public abstract class ExportRequest { private EntityExportSettings exportSettings; - @JsonIgnore public abstract ExportRequestType getType(); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequestType.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequestType.java index 07e5c946ba..c36ac567e3 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequestType.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequestType.java @@ -20,6 +20,6 @@ public enum ExportRequestType { ENTITY_LIST, ENTITY_TYPE, - ENTITY_FILTER, - ENTITY_QUERY + CUSTOM_ENTITY_FILTER, + CUSTOM_ENTITY_QUERY } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportResult.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportResult.java index 7160a483b3..62c574d5f0 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportResult.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportResult.java @@ -16,32 +16,33 @@ package org.thingsboard.server.service.sync.importing.data; import com.fasterxml.jackson.annotation.JsonIgnore; -import lombok.Getter; -import lombok.Setter; +import lombok.Data; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.utils.JsonTbEntity; import org.thingsboard.server.utils.ThrowingRunnable; +@Data public class EntityImportResult> { - @Getter @Setter + + @JsonTbEntity private E savedEntity; - @Getter @Setter + @JsonTbEntity private E oldEntity; - @Getter @Setter private EntityType entityType; - @Getter @JsonIgnore + @JsonIgnore private ThrowingRunnable saveReferencesCallback = () -> {}; - @Getter @JsonIgnore - private ThrowingRunnable pushEventsCallback = () -> {}; + @JsonIgnore + private ThrowingRunnable sendEventsCallback = () -> {}; public void addSaveReferencesCallback(ThrowingRunnable callback) { this.saveReferencesCallback = this.saveReferencesCallback.andThen(callback); } - public void addPushEventsCallback(ThrowingRunnable callback) { - this.pushEventsCallback = this.pushEventsCallback.andThen(callback); + public void addSendEventsCallback(ThrowingRunnable callback) { + this.sendEventsCallback = this.sendEventsCallback.andThen(callback); } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java index 1b5a93c0d8..3b457e1f5a 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java @@ -26,8 +26,10 @@ import lombok.NoArgsConstructor; @Builder public class EntityImportSettings { private boolean findExistingByName; + private boolean importInboundRelations; private boolean importOutboundRelations; private boolean removeExistingRelations; + private boolean updateReferencesToOtherEntities; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/request/ImportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/request/ImportRequest.java index 6e857958d6..7943e1a404 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/request/ImportRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/request/ImportRequest.java @@ -26,7 +26,7 @@ import java.util.List; @Data public class ImportRequest { - private List>> exportDataList; + private List> exportDataList; private EntityImportSettings importSettings; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java index b576ae1e60..97547868d2 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java @@ -21,7 +21,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.cluster.TbClusterService; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.HasCustomerId; import org.thingsboard.server.common.data.audit.ActionType; @@ -76,6 +75,7 @@ public abstract class BaseEntityImportService { + importResult.addSendEventsCallback(() -> { onEntitySaved(user, savedEntity, oldEntity); }); } @@ -172,8 +172,6 @@ public abstract class BaseEntityImportService HasId findInternalEntity(TenantId tenantId, ID externalId) { - if (externalId == null || externalId.isNullUid()) return null; - return (HasId) Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndExternalId(tenantId, externalId)) .or(() -> Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndId(tenantId, externalId))) .orElseThrow(() -> new IllegalArgumentException("Cannot find " + externalId.getEntityType() + " by external id " + externalId)); @@ -187,19 +185,18 @@ public abstract class BaseEntityImportService ALWAYS_UPDATE_REFERENCED_IDS = Set.of( - EntityType.RULE_CHAIN - ); - public ID get(Function idExtractor) { - if (existingEntity == null || importSettings.isUpdateReferencesToOtherEntities() - || ALWAYS_UPDATE_REFERENCED_IDS.contains(getEntityType())) { + if (existingEntity == null || importSettings.isUpdateReferencesToOtherEntities()) { return getInternalId(idExtractor.apply(this.entity)); } else { return idExtractor.apply(existingEntity); } } + public ID getInternal(ID externalId) { + return getInternalId(externalId); + } + public Set get(Function> listExtractor, Function idGetter, BiConsumer idSetter) { if (existingEntity == null || importSettings.isUpdateReferencesToOtherEntities()) { return Optional.ofNullable(listExtractor.apply(entity)).orElse(Collections.emptySet()).stream() @@ -213,17 +210,15 @@ public abstract class BaseEntityImportService ID getInternalId(ID externalId) { + if (externalId == null || externalId.isNullUid()) return null; + HasId entity = findInternalEntity(user.getTenantId(), externalId); - if (entity != null) { - try { - exportableEntitiesService.checkPermission(user, entity, entity.getId().getEntityType(), Operation.READ); - } catch (ThingsboardException e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - return entity.getId(); - } else { - return null; + try { + exportableEntitiesService.checkPermission(user, entity, entity.getId().getEntityType(), Operation.READ); + } catch (ThingsboardException e) { + throw new IllegalArgumentException(e.getMessage(), e); } + return entity.getId(); } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java index 3e06480a2f..fda6904421 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java @@ -74,7 +74,7 @@ public class DashboardImportService extends BaseEntityImportService { String uuid = matchResult.group(); EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, uuid); - return idProvider.get(d -> entityId).toString(); + return idProvider.getInternal(entityId).toString(); }); ((ObjectNode) entityAlias).set("filter", JacksonUtil.toJsonNode(newFilterJson)); }); @@ -86,7 +86,7 @@ public class DashboardImportService extends BaseEntityImportService existingAssignedCustomers = Optional.ofNullable(dashboardService.findDashboardById(tenantId, dashboard.getId()).getAssignedCustomers()) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java index f901fcfd17..5fef00cbed 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java @@ -63,14 +63,14 @@ public class RuleChainImportService extends BaseEntityImportService { ((ObjectNode) ruleNodeConfig).set("ruleChainId", new TextNode( - idProvider.get(rc -> new RuleChainId(otherRuleChainUuid)).toString() + idProvider.getInternal(new RuleChainId(otherRuleChainUuid)).toString() )); ruleNode.setConfiguration(ruleNodeConfig); }); }); Optional.ofNullable(metaData.getRuleChainConnections()).orElse(Collections.emptyList()) .forEach(ruleChainConnectionInfo -> { - ruleChainConnectionInfo.setTargetRuleChainId(idProvider.get(rc -> ruleChainConnectionInfo.getTargetRuleChainId())); + ruleChainConnectionInfo.setTargetRuleChainId(idProvider.getInternal(ruleChainConnectionInfo.getTargetRuleChainId())); }); ruleChain.setFirstRuleNodeId(null); diff --git a/application/src/main/java/org/thingsboard/server/utils/JsonTbEntity.java b/application/src/main/java/org/thingsboard/server/utils/JsonTbEntity.java new file mode 100644 index 0000000000..cfc38ff3c3 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/utils/JsonTbEntity.java @@ -0,0 +1,47 @@ +/** + * 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.utils; + +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Dashboard; +import org.thingsboard.server.common.data.Device; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.rule.RuleChain; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@JacksonAnnotationsInside +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "entityType", include = JsonTypeInfo.As.EXTERNAL_PROPERTY) +@JsonSubTypes({ + @Type(name = "DEVICE", value = Device.class), + @Type(name = "RULE_CHAIN", value = RuleChain.class), + @Type(name = "DEVICE_PROFILE", value = DeviceProfile.class), + @Type(name = "ASSET", value = Asset.class), + @Type(name = "DASHBOARD", value = Dashboard.class), + @Type(name = "CUSTOMER", value = Customer.class) +}) +public @interface JsonTbEntity { +} diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEntitiesExportImportControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEntitiesExportImportControllerTest.java index 0d73ed9f09..a8a5bca534 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEntitiesExportImportControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEntitiesExportImportControllerTest.java @@ -17,6 +17,8 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.node.TextNode; +import org.junit.After; +import org.junit.Before; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.web.servlet.ResultActions; import org.thingsboard.common.util.JacksonUtil; @@ -28,6 +30,10 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.DeviceData; @@ -35,19 +41,29 @@ import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileCon import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; +import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; +import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; import org.thingsboard.server.service.sync.exporting.data.request.ExportRequest; @@ -56,11 +72,12 @@ import org.thingsboard.server.service.sync.importing.data.EntityImportResult; import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; import org.thingsboard.server.service.sync.importing.data.request.ImportRequest; -import java.lang.reflect.Type; +import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Collections; import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public abstract class BaseEntitiesExportImportControllerTest extends AbstractControllerTest { @@ -68,6 +85,8 @@ public abstract class BaseEntitiesExportImportControllerTest extends AbstractCon @Autowired protected DeviceService deviceService; @Autowired + protected OtaPackageService otaPackageService; + @Autowired protected DeviceProfileService deviceProfileService; @Autowired protected AssetService assetService; @@ -77,6 +96,45 @@ public abstract class BaseEntitiesExportImportControllerTest extends AbstractCon protected RuleChainService ruleChainService; @Autowired protected DashboardService dashboardService; + @Autowired + protected RelationService relationService; + @Autowired + protected TenantService tenantService; + + protected TenantId tenantId1; + protected User tenantAdmin1; + + protected TenantId tenantId2; + protected User tenantAdmin2; + + @Before + public void beforeEach() throws Exception { + loginSysAdmin(); + Tenant tenant1 = new Tenant(); + tenant1.setTitle("Tenant 1"); + tenant1.setEmail("tenant1@thingsboard.org"); + this.tenantId1 = tenantService.saveTenant(tenant1).getId(); + User tenantAdmin1 = new User(); + tenantAdmin1.setTenantId(tenantId1); + tenantAdmin1.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin1.setEmail("tenant1-admin@thingsboard.org"); + this.tenantAdmin1 = createUser(tenantAdmin1, "12345678"); + Tenant tenant2 = new Tenant(); + tenant2.setTitle("Tenant 2"); + tenant2.setEmail("tenant2@thingsboard.org"); + this.tenantId2 = tenantService.saveTenant(tenant2).getId(); + User tenantAdmin2 = new User(); + tenantAdmin2.setTenantId(tenantId2); + tenantAdmin2.setAuthority(Authority.TENANT_ADMIN); + tenantAdmin2.setEmail("tenant2-admin@thingsboard.org"); + this.tenantAdmin2 = createUser(tenantAdmin2, "12345678"); + } + + @After + public void afterEach() { + tenantService.deleteTenant(tenantId1); + tenantService.deleteTenant(tenantId2); + } protected Device createDevice(TenantId tenantId, CustomerId customerId, DeviceProfileId deviceProfileId, String name) { Device device = new Device(); @@ -91,13 +149,38 @@ public abstract class BaseEntitiesExportImportControllerTest extends AbstractCon return deviceService.saveDevice(device); } - protected DeviceProfile createDeviceProfile(TenantId tenantId, String name) { + protected OtaPackage createOtaPackage(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType type) { + OtaPackage otaPackage = new OtaPackage(); + otaPackage.setTenantId(tenantId); + otaPackage.setDeviceProfileId(deviceProfileId); + otaPackage.setType(type); + otaPackage.setTitle("My " + type); + otaPackage.setVersion("v1.0"); + otaPackage.setFileName("filename.txt"); + otaPackage.setContentType("text/plain"); + otaPackage.setChecksumAlgorithm(ChecksumAlgorithm.SHA256); + otaPackage.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"); + otaPackage.setDataSize(1L); + otaPackage.setData(ByteBuffer.wrap(new byte[]{(int) 1})); + return otaPackageService.saveOtaPackage(otaPackage); + } + + protected void checkImportedDeviceData(Device initialDevice, Device importedDevice) { + assertThat(importedDevice.getName()).isEqualTo(initialDevice.getName()); + assertThat(importedDevice.getType()).isEqualTo(initialDevice.getType()); + assertThat(importedDevice.getDeviceData()).isEqualTo(initialDevice.getDeviceData()); + assertThat(importedDevice.getLabel()).isEqualTo(initialDevice.getLabel()); + } + + protected DeviceProfile createDeviceProfile(TenantId tenantId, RuleChainId defaultRuleChainId, DashboardId defaultDashboardId, String name) { DeviceProfile deviceProfile = new DeviceProfile(); deviceProfile.setTenantId(tenantId); deviceProfile.setName(name); deviceProfile.setDescription("dscrptn"); deviceProfile.setType(DeviceProfileType.DEFAULT); deviceProfile.setTransportType(DeviceTransportType.DEFAULT); + deviceProfile.setDefaultRuleChainId(defaultRuleChainId); + deviceProfile.setDefaultDashboardId(defaultDashboardId); DeviceProfileData profileData = new DeviceProfileData(); profileData.setConfiguration(new DefaultDeviceProfileConfiguration()); profileData.setTransportConfiguration(new DefaultDeviceProfileTransportConfiguration()); @@ -105,6 +188,14 @@ public abstract class BaseEntitiesExportImportControllerTest extends AbstractCon return deviceProfileService.saveDeviceProfile(deviceProfile); } + protected void checkImportedDeviceProfileData(DeviceProfile initialProfile, DeviceProfile importedProfile) { + assertThat(initialProfile.getName()).isEqualTo(importedProfile.getName()); + assertThat(initialProfile.getType()).isEqualTo(importedProfile.getType()); + assertThat(initialProfile.getTransportType()).isEqualTo(importedProfile.getTransportType()); + assertThat(initialProfile.getProfileData()).isEqualTo(importedProfile.getProfileData()); + assertThat(initialProfile.getDescription()).isEqualTo(importedProfile.getDescription()); + } + protected Asset createAsset(TenantId tenantId, CustomerId customerId, String type, String name) { Asset asset = new Asset(); asset.setTenantId(tenantId); @@ -116,6 +207,13 @@ public abstract class BaseEntitiesExportImportControllerTest extends AbstractCon return assetService.saveAsset(asset); } + protected void checkImportedAssetData(Asset initialAsset, Asset importedAsset) { + assertThat(importedAsset.getName()).isEqualTo(initialAsset.getName()); + assertThat(importedAsset.getType()).isEqualTo(initialAsset.getType()); + assertThat(importedAsset.getLabel()).isEqualTo(initialAsset.getLabel()); + assertThat(importedAsset.getAdditionalInfo()).isEqualTo(initialAsset.getAdditionalInfo()); + } + protected Customer createCustomer(TenantId tenantId, String name) { Customer customer = new Customer(); customer.setTenantId(tenantId); @@ -127,6 +225,13 @@ public abstract class BaseEntitiesExportImportControllerTest extends AbstractCon return customerService.saveCustomer(customer); } + protected void checkImportedCustomerData(Customer initialCustomer, Customer importedCustomer) { + assertThat(importedCustomer.getTitle()).isEqualTo(initialCustomer.getTitle()); + assertThat(importedCustomer.getCountry()).isEqualTo(initialCustomer.getCountry()); + assertThat(importedCustomer.getAddress()).isEqualTo(initialCustomer.getAddress()); + assertThat(importedCustomer.getEmail()).isEqualTo(initialCustomer.getEmail()); + } + protected Dashboard createDashboard(TenantId tenantId, CustomerId customerId, String name) { Dashboard dashboard = new Dashboard(); dashboard.setTenantId(tenantId); @@ -142,6 +247,16 @@ public abstract class BaseEntitiesExportImportControllerTest extends AbstractCon return dashboard; } + protected void checkImportedDashboardData(Dashboard initialDashboard, Dashboard importedDashboard) { + assertThat(importedDashboard.getTitle()).isEqualTo(initialDashboard.getTitle()); + assertThat(importedDashboard.getConfiguration()).isEqualTo(initialDashboard.getConfiguration()); + assertThat(importedDashboard.getImage()).isEqualTo(initialDashboard.getImage()); + assertThat(importedDashboard.isMobileHide()).isEqualTo(initialDashboard.isMobileHide()); + if (initialDashboard.getAssignedCustomers() != null) { + assertThat(importedDashboard.getAssignedCustomers()).containsAll(initialDashboard.getAssignedCustomers()); + } + } + protected RuleChain createRuleChain(TenantId tenantId, String name) { RuleChain ruleChain = new RuleChain(); ruleChain.setTenantId(tenantId); @@ -178,6 +293,50 @@ public abstract class BaseEntitiesExportImportControllerTest extends AbstractCon return ruleChainService.findRuleChainById(tenantId, ruleChain.getId()); } + protected void checkImportedRuleChainData(RuleChain initialRuleChain, RuleChainMetaData initialMetaData, RuleChain importedRuleChain, RuleChainMetaData importedMetaData) { + assertThat(importedRuleChain.getType()).isEqualTo(initialRuleChain.getType()); + assertThat(importedRuleChain.getName()).isEqualTo(initialRuleChain.getName()); + assertThat(importedRuleChain.isDebugMode()).isEqualTo(initialRuleChain.isDebugMode()); + assertThat(importedRuleChain.getConfiguration()).isEqualTo(initialRuleChain.getConfiguration()); + + assertThat(importedMetaData.getConnections()).isEqualTo(initialMetaData.getConnections()); + assertThat(importedMetaData.getFirstNodeIndex()).isEqualTo(initialMetaData.getFirstNodeIndex()); + for (int i = 0; i < initialMetaData.getNodes().size(); i++) { + RuleNode initialNode = initialMetaData.getNodes().get(i); + RuleNode importedNode = importedMetaData.getNodes().get(i); + assertThat(importedNode.getRuleChainId()).isEqualTo(importedRuleChain.getId()); + assertThat(importedNode.getName()).isEqualTo(initialNode.getName()); + assertThat(importedNode.getType()).isEqualTo(initialNode.getType()); + assertThat(importedNode.getConfiguration()).isEqualTo(initialNode.getConfiguration()); + assertThat(importedNode.getAdditionalInfo()).isEqualTo(initialNode.getAdditionalInfo()); + } + } + + protected EntityRelation createRelation(EntityId from, EntityId to) { + EntityRelation relation = new EntityRelation(); + relation.setFrom(from); + relation.setTo(to); + relation.setType(EntityRelation.MANAGES_TYPE); + relation.setAdditionalInfo(JacksonUtil.newObjectNode().set("a", new TextNode("b"))); + relation.setTypeGroup(RelationTypeGroup.COMMON); + relationService.saveRelation(TenantId.SYS_TENANT_ID, relation); + return relation; + } + + protected & HasTenantId> void checkImportedEntity(TenantId tenantId1, E initialEntity, TenantId tenantId2, E importedEntity) { + assertThat(initialEntity.getTenantId()).isEqualTo(tenantId1); + assertThat(importedEntity.getTenantId()).isEqualTo(tenantId2); + + assertThat(importedEntity.getExternalId()).isEqualTo(initialEntity.getId()); + + boolean sameTenant = tenantId1.equals(tenantId2); + if (!sameTenant) { + assertThat(importedEntity.getId()).isNotEqualTo(initialEntity.getId()); + } else { + assertThat(importedEntity.getId()).isEqualTo(initialEntity.getId()); + } + } + protected , I extends EntityId> EntityExportData exportSingleEntity(I entityId) throws Exception { SingleEntityExportRequest exportRequest = new SingleEntityExportRequest(); @@ -186,33 +345,30 @@ public abstract class BaseEntitiesExportImportControllerTest extends AbstractCon return (EntityExportData) exportEntities(exportRequest).get(0); } - protected List>> exportEntities(ExportRequest exportRequest) throws Exception { - return getResponse(doPost("/api/entities/export", exportRequest), new TypeReference>>>() {}); + protected List> exportEntities(ExportRequest exportRequest) throws Exception { + return getResponse(doPost("/api/entities/export", exportRequest), new TypeReference>>() {}); } - protected List>> exportEntities(List exportRequests) throws Exception { - return getResponse(doPost("/api/entities/export?multiple", exportRequests), new TypeReference>>>() {}); + protected List> exportEntities(List exportRequests) throws Exception { + return getResponse(doPost("/api/entities/export?multiple", exportRequests), new TypeReference>>() {}); } protected , I extends EntityId> EntityImportResult importEntity(EntityExportData exportData) throws Exception { - return (EntityImportResult) importEntities(List.of((EntityExportData>)exportData)).get(0); + return (EntityImportResult) importEntities(List.of((EntityExportData>) exportData)).get(0); } - protected List>> importEntities(List>> exportDataList) throws Exception { + protected List> importEntities(List> exportDataList) throws Exception { ImportRequest importRequest = new ImportRequest(); importRequest.setImportSettings(EntityImportSettings.builder() .updateReferencesToOtherEntities(true) .build()); importRequest.setExportDataList(exportDataList); - return getResponse(doPost("/api/entities/import", importRequest), new TypeReference>>>() { - @Override - public Type getType() { - return mapper.getTypeFactory().constructCollectionType(List.class, - mapper.getTypeFactory().constructParametricType(EntityImportResult.class, - exportDataList.get(0).getEntity().getClass())); - } - }); + return importEntities(importRequest); + } + + protected List> importEntities(ImportRequest importRequest) throws Exception { + return getResponse(doPost("/api/entities/import", importRequest), new TypeReference>>() {}); } protected T getResponse(ResultActions resultActions, TypeReference typeReference) throws Exception { @@ -223,4 +379,13 @@ public abstract class BaseEntitiesExportImportControllerTest extends AbstractCon } } + + protected void logInAsTenantAdmin1() throws Exception { + login(tenantAdmin1.getEmail(), "12345678"); + } + + protected void logInAsTenantAdmin2() throws Exception { + login(tenantAdmin2.getEmail(), "12345678"); + } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java index ccbf311bef..920d91e93d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java @@ -15,86 +15,81 @@ */ package org.thingsboard.server.controller.sql; -import org.junit.After; -import org.junit.Before; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; +import com.google.common.collect.Streams; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.testcontainers.shaded.org.apache.commons.lang.RandomStringUtils; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; -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.OtaPackage; import org.thingsboard.server.common.data.asset.Asset; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; +import org.thingsboard.server.common.data.query.EntityListFilter; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; -import org.thingsboard.server.common.data.rule.RuleNode; -import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.controller.BaseEntitiesExportImportControllerTest; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.dao.tenant.TenantService; -import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; +import org.thingsboard.server.service.action.EntityActionService; +import org.thingsboard.server.service.ota.OtaPackageStateService; import org.thingsboard.server.service.sync.exporting.data.DeviceExportData; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import org.thingsboard.server.service.sync.exporting.data.RuleChainExportData; +import org.thingsboard.server.service.sync.exporting.data.request.CustomEntityFilterExportRequest; +import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; +import org.thingsboard.server.service.sync.exporting.data.request.EntityListExportRequest; +import org.thingsboard.server.service.sync.exporting.data.request.EntityTypeExportRequest; +import org.thingsboard.server.service.sync.exporting.data.request.ExportRequest; +import org.thingsboard.server.service.sync.exporting.data.request.SingleEntityExportRequest; import org.thingsboard.server.service.sync.importing.data.EntityImportResult; +import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; +import org.thingsboard.server.service.sync.importing.data.request.ImportRequest; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.verify; @DaoSqlTest public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImportControllerTest { - @Autowired - private TenantService tenantService; @Autowired private DeviceCredentialsService deviceCredentialsService; - @Autowired - private ExportableEntitiesService exportableEntitiesService; - - private TenantId tenantId1; - private User tenantAdmin1; - - private TenantId tenantId2; - private User tenantAdmin2; - - @Before - public void beforeEach() throws Exception { - loginSysAdmin(); - Tenant tenant1 = new Tenant(); - tenant1.setTitle("Tenant 1"); - tenant1.setEmail("tenant1@thingsboard.org"); - this.tenantId1 = tenantService.saveTenant(tenant1).getId(); - User tenantAdmin1 = new User(); - tenantAdmin1.setTenantId(tenantId1); - tenantAdmin1.setAuthority(Authority.TENANT_ADMIN); - tenantAdmin1.setEmail("tenant1-admin@thingsboard.org"); - this.tenantAdmin1 = createUser(tenantAdmin1, "12345678"); - Tenant tenant2 = new Tenant(); - tenant2.setTitle("Tenant 2"); - tenant2.setEmail("tenant2@thingsboard.org"); - this.tenantId2 = tenantService.saveTenant(tenant2).getId(); - User tenantAdmin2 = new User(); - tenantAdmin2.setTenantId(tenantId2); - tenantAdmin2.setAuthority(Authority.TENANT_ADMIN); - tenantAdmin2.setEmail("tenant2-admin@thingsboard.org"); - this.tenantAdmin2 = createUser(tenantAdmin2, "12345678"); - } - - @After - public void afterEach() { - tenantService.deleteTenant(tenantId1); - tenantService.deleteTenant(tenantId2); - } - + @SpyBean + private EntityActionService entityActionService; + @SpyBean + private TbClusterService clusterService; + @SpyBean + private OtaPackageStateService otaPackageStateService; @Test - public void testExportImportSingleAsset_betweenTenants() throws Exception { + public void testExportImportAsset_betweenTenants() throws Exception { logInAsTenantAdmin1(); Asset asset = createAsset(tenantId1, null, "AB", "Asset of tenant 1"); EntityExportData exportData = exportSingleEntity(asset.getId()); @@ -103,40 +98,24 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp logInAsTenantAdmin2(); EntityImportResult importResult = importEntity(exportData); - checkImportedEntity(tenantId1, asset, tenantId2, importResult); + checkImportedEntity(tenantId1, asset, tenantId2, importResult.getSavedEntity()); checkImportedAssetData(asset, importResult.getSavedEntity()); } @Test - public void testExportImportSingleAsset_sameTenant() throws Exception { + public void testExportImportAsset_sameTenant() throws Exception { logInAsTenantAdmin1(); Asset asset = createAsset(tenantId1, null, "AB", "Asset v1.0"); EntityExportData exportData = exportSingleEntity(asset.getId()); EntityImportResult importResult = importEntity(exportData); - checkImportedEntity(tenantId1, asset, tenantId1, importResult); + checkImportedEntity(tenantId1, asset, tenantId1, importResult.getSavedEntity()); checkImportedAssetData(asset, importResult.getSavedEntity()); } @Test - public void testExportImportAsset_withCustomer_betweenTenants() throws Exception { - logInAsTenantAdmin1(); - Customer customer = createCustomer(tenantId1, "My customer"); - Asset asset = createAsset(tenantId1, customer.getId(), "AB", "My asset"); - - EntityExportData customerExportData = exportSingleEntity(customer.getId()); - EntityExportData assetExportData = exportSingleEntity(asset.getId()); - - logInAsTenantAdmin2(); - Customer importedCustomer = importEntity(customerExportData).getSavedEntity(); - Asset importedAsset = importEntity(assetExportData).getSavedEntity(); - - assertThat(importedAsset.getCustomerId()).isEqualTo(importedCustomer.getId()); - } - - @Test - public void testExportImportAsset_withCustomer_sameTenant() throws Exception { + public void testExportImportAsset_sameTenant_withCustomer() throws Exception { logInAsTenantAdmin1(); Customer customer = createCustomer(tenantId1, "My customer"); Asset asset = createAsset(tenantId1, customer.getId(), "AB", "My asset"); @@ -146,16 +125,9 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp assertThat(importedAsset.getCustomerId()).isEqualTo(asset.getCustomerId()); } - private void checkImportedAssetData(Asset initialAsset, Asset importedAsset) { - assertThat(importedAsset.getName()).isEqualTo(initialAsset.getName()); - assertThat(importedAsset.getType()).isEqualTo(initialAsset.getType()); - assertThat(importedAsset.getLabel()).isEqualTo(initialAsset.getLabel()); - assertThat(importedAsset.getAdditionalInfo()).isEqualTo(initialAsset.getAdditionalInfo()); - } - @Test - public void testExportImportSingleCustomer_betweenTenants() throws Exception { + public void testExportImportCustomer_betweenTenants() throws Exception { logInAsTenantAdmin1(); Customer customer = createCustomer(tenantAdmin1.getTenantId(), "Customer of tenant 1"); EntityExportData exportData = exportSingleEntity(customer.getId()); @@ -164,34 +136,27 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp logInAsTenantAdmin2(); EntityImportResult importResult = importEntity(exportData); - checkImportedEntity(tenantId1, customer, tenantId2, importResult); + checkImportedEntity(tenantId1, customer, tenantId2, importResult.getSavedEntity()); checkImportedCustomerData(customer, importResult.getSavedEntity()); } @Test - public void testExportImportSingleCustomer_sameTenant() throws Exception { + public void testExportImportCustomer_sameTenant() throws Exception { logInAsTenantAdmin1(); Customer customer = createCustomer(tenantAdmin1.getTenantId(), "Customer v1.0"); EntityExportData exportData = exportSingleEntity(customer.getId()); EntityImportResult importResult = importEntity(exportData); - checkImportedEntity(tenantId1, customer, tenantId1, importResult); + checkImportedEntity(tenantId1, customer, tenantId1, importResult.getSavedEntity()); checkImportedCustomerData(customer, importResult.getSavedEntity()); } - private void checkImportedCustomerData(Customer initialCustomer, Customer importedCustomer) { - assertThat(importedCustomer.getTitle()).isEqualTo(initialCustomer.getTitle()); - assertThat(importedCustomer.getCountry()).isEqualTo(initialCustomer.getCountry()); - assertThat(importedCustomer.getAddress()).isEqualTo(initialCustomer.getAddress()); - assertThat(importedCustomer.getEmail()).isEqualTo(initialCustomer.getEmail()); - } - @Test public void testExportImportDeviceWithProfile_betweenTenants() throws Exception { logInAsTenantAdmin1(); - DeviceProfile deviceProfile = createDeviceProfile(tenantId1, "Device profile of tenant 1"); + DeviceProfile deviceProfile = createDeviceProfile(tenantId1, null, null, "Device profile of tenant 1"); Device device = createDevice(tenantId1, null, deviceProfile.getId(), "Device of tenant 1"); DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId1, device.getId()); @@ -206,13 +171,13 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp logInAsTenantAdmin2(); EntityImportResult profileImportResult = importEntity(profileExportData); - checkImportedEntity(tenantId1, deviceProfile, tenantId2, profileImportResult); + checkImportedEntity(tenantId1, deviceProfile, tenantId2, profileImportResult.getSavedEntity()); checkImportedDeviceProfileData(deviceProfile, profileImportResult.getSavedEntity()); EntityImportResult deviceImportResult = importEntity(deviceExportData); Device importedDevice = deviceImportResult.getSavedEntity(); - checkImportedEntity(tenantId1, device, tenantId2, deviceImportResult); + checkImportedEntity(tenantId1, device, tenantId2, deviceImportResult.getSavedEntity()); checkImportedDeviceData(device, importedDevice); assertThat(importedDevice.getDeviceProfileId()).isEqualTo(profileImportResult.getSavedEntity().getId()); @@ -227,8 +192,14 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp @Test public void testExportImportDevice_sameTenant() throws Exception { logInAsTenantAdmin1(); - DeviceProfile deviceProfile = createDeviceProfile(tenantId1, "Device profile v1.0"); + DeviceProfile deviceProfile = createDeviceProfile(tenantId1, null, null, "Device profile v1.0"); + OtaPackage firmware = createOtaPackage(tenantId1, deviceProfile.getId(), OtaPackageType.FIRMWARE); + OtaPackage software = createOtaPackage(tenantId1, deviceProfile.getId(), OtaPackageType.SOFTWARE); Device device = createDevice(tenantId1, null, deviceProfile.getId(), "Device v1.0"); + device.setFirmwareId(firmware.getId()); + device.setSoftwareId(software.getId()); + device = deviceService.saveDevice(device); + DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId1, device.getId()); EntityExportData deviceExportData = exportSingleEntity(device.getId()); @@ -236,29 +207,119 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp EntityImportResult importResult = importEntity(deviceExportData); Device importedDevice = importResult.getSavedEntity(); - checkImportedEntity(tenantId1, device, tenantId1, importResult); + checkImportedEntity(tenantId1, device, tenantId1, importResult.getSavedEntity()); assertThat(importedDevice.getDeviceProfileId()).isEqualTo(device.getDeviceProfileId()); assertThat(deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId1, device.getId())).isEqualTo(credentials); + assertThat(importedDevice.getFirmwareId()).isEqualTo(firmware.getId()); + assertThat(importedDevice.getSoftwareId()).isEqualTo(software.getId()); + } + + + @Test + public void testExportImportDashboard_betweenTenants() throws Exception { + logInAsTenantAdmin1(); + Dashboard dashboard = createDashboard(tenantAdmin1.getTenantId(), null, "Dashboard of tenant 1"); + + EntityExportData exportData = exportSingleEntity(dashboard.getId()); + assertThat(exportData.getEntity()).isEqualTo(dashboard); + + logInAsTenantAdmin2(); + EntityImportResult importResult = importEntity(exportData); + checkImportedEntity(tenantId1, dashboard, tenantId2, importResult.getSavedEntity()); + checkImportedDashboardData(dashboard, importResult.getSavedEntity()); } - private void checkImportedDeviceProfileData(DeviceProfile initialProfile, DeviceProfile importedProfile) { - assertThat(initialProfile.getName()).isEqualTo(importedProfile.getName()); - assertThat(initialProfile.getType()).isEqualTo(importedProfile.getType()); - assertThat(initialProfile.getTransportType()).isEqualTo(importedProfile.getTransportType()); - assertThat(initialProfile.getProfileData()).isEqualTo(importedProfile.getProfileData()); - assertThat(initialProfile.getDescription()).isEqualTo(importedProfile.getDescription()); + @Test + public void testExportImportDashboard_sameTenant() throws Exception { + logInAsTenantAdmin1(); + Dashboard dashboard = createDashboard(tenantAdmin1.getTenantId(), null, "Dashboard v1.0"); + + EntityExportData exportData = exportSingleEntity(dashboard.getId()); + + EntityImportResult importResult = importEntity(exportData); + checkImportedEntity(tenantId1, dashboard, tenantId1, importResult.getSavedEntity()); + checkImportedDashboardData(dashboard, importResult.getSavedEntity()); } - private void checkImportedDeviceData(Device initialDevice, Device importedDevice) { - assertThat(importedDevice.getName()).isEqualTo(initialDevice.getName()); - assertThat(importedDevice.getType()).isEqualTo(initialDevice.getType()); - assertThat(importedDevice.getDeviceData()).isEqualTo(initialDevice.getDeviceData()); - assertThat(importedDevice.getLabel()).isEqualTo(initialDevice.getLabel()); + @Test + public void testExportImportDashboard_betweenTenants_withCustomer_updated() throws Exception { + logInAsTenantAdmin1(); + Dashboard dashboard = createDashboard(tenantAdmin1.getTenantId(), null, "Dashboard of tenant 1"); + + EntityExportData exportData = exportSingleEntity(dashboard.getId()); + + logInAsTenantAdmin2(); + Dashboard importedDashboard = (Dashboard) importEntities(List.of(exportData)).get(0).getSavedEntity(); + checkImportedEntity(tenantId1, dashboard, tenantId2, importedDashboard); + + logInAsTenantAdmin1(); + Customer customer = createCustomer(tenantId1, "Customer 1"); + EntityExportData customerExportData = exportSingleEntity(customer.getId()); + dashboardService.assignDashboardToCustomer(tenantId1, dashboard.getId(), customer.getId()); + exportData = exportSingleEntity(dashboard.getId()); + + logInAsTenantAdmin2(); + Customer importedCustomer = (Customer) importEntities(List.of(customerExportData)).get(0).getSavedEntity(); + importedDashboard = (Dashboard) importEntities(List.of(exportData)).get(0).getSavedEntity(); + assertThat(importedDashboard.getAssignedCustomers()).hasOnlyOneElementSatisfying(customerInfo -> { + assertThat(customerInfo.getCustomerId()).isEqualTo(importedCustomer.getId()); + }); + } + + @Test + public void testExportImportDashboard_betweenTenants_withEntityAliases() throws Exception { + logInAsTenantAdmin1(); + Asset asset1 = createAsset(tenantId1, null, "A", "Asset 1"); + Asset asset2 = createAsset(tenantId1, null, "A", "Asset 2"); + Dashboard dashboard = createDashboard(tenantId1, null, "Dashboard 1"); + + String entityAliases = "{\n" + + "\t\"23c4185d-1497-9457-30b2-6d91e69a5b2c\": {\n" + + "\t\t\"alias\": \"assets\",\n" + + "\t\t\"filter\": {\n" + + "\t\t\t\"entityList\": [\n" + + "\t\t\t\t\"" + asset1.getId().toString() + "\",\n" + + "\t\t\t\t\"" + asset2.getId().toString() + "\"\n" + + "\t\t\t],\n" + + "\t\t\t\"entityType\": \"ASSET\",\n" + + "\t\t\t\"resolveMultiple\": true,\n" + + "\t\t\t\"type\": \"entityList\"\n" + + "\t\t},\n" + + "\t\t\"id\": \"23c4185d-1497-9457-30b2-6d91e69a5b2c\"\n" + + "\t}\n" + + "}"; + ObjectNode dashboardConfiguration = JacksonUtil.newObjectNode(); + dashboardConfiguration.set("entityAliases", JacksonUtil.toJsonNode(entityAliases)); + dashboardConfiguration.set("description", new TextNode("hallo")); + dashboard.setConfiguration(dashboardConfiguration); + dashboard = dashboardService.saveDashboard(dashboard); + + EntityTypeExportRequest assetsExportRequest = new EntityTypeExportRequest(); + assetsExportRequest.setEntityType(EntityType.ASSET); + assetsExportRequest.setPageSize(10); + assetsExportRequest.setExportSettings(new EntityExportSettings()); + EntityTypeExportRequest dashboardsExportRequest = new EntityTypeExportRequest(); + dashboardsExportRequest.setEntityType(EntityType.DASHBOARD); + dashboardsExportRequest.setPageSize(10); + dashboardsExportRequest.setExportSettings(new EntityExportSettings()); + List> exportDataList = exportEntities(List.of(assetsExportRequest, dashboardsExportRequest)); + + logInAsTenantAdmin2(); + Map>> importResults = importEntities(exportDataList).stream().collect(Collectors.groupingBy(EntityImportResult::getEntityType)); + Asset importedAsset1 = (Asset) importResults.get(EntityType.ASSET).get(0).getSavedEntity(); + Asset importedAsset2 = (Asset) importResults.get(EntityType.ASSET).get(1).getSavedEntity(); + Dashboard importedDashboard = (Dashboard) importResults.get(EntityType.DASHBOARD).get(0).getSavedEntity(); + + Set entityAliasEntitiesIds = Streams.stream(importedDashboard.getConfiguration() + .get("entityAliases").elements().next().get("filter").get("entityList").elements()) + .map(JsonNode::asText).collect(Collectors.toSet()); + assertThat(entityAliasEntitiesIds).doesNotContain(asset1.getId().toString(), asset2.getId().toString()); + assertThat(entityAliasEntitiesIds).contains(importedAsset1.getId().toString(), importedAsset2.getId().toString()); } @Test - public void testExportImportSingleRuleChain_betweenTenants() throws Exception { + public void testExportImportRuleChain_betweenTenants() throws Exception { logInAsTenantAdmin1(); RuleChain ruleChain = createRuleChain(tenantId1, "Rule chain of tenant 1"); RuleChainMetaData metaData = ruleChainService.loadRuleChainMetaData(tenantId1, ruleChain.getId()); @@ -272,12 +333,12 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp RuleChain importedRuleChain = importResult.getSavedEntity(); RuleChainMetaData importedMetaData = ruleChainService.loadRuleChainMetaData(tenantId2, importedRuleChain.getId()); - checkImportedEntity(tenantId1, ruleChain, tenantId2, importResult); + checkImportedEntity(tenantId1, ruleChain, tenantId2, importResult.getSavedEntity()); checkImportedRuleChainData(ruleChain, metaData, importedRuleChain, importedMetaData); } @Test - public void testExportImportSingleRuleChain_sameTenant() throws Exception { + public void testExportImportRuleChain_sameTenant() throws Exception { logInAsTenantAdmin1(); RuleChain ruleChain = createRuleChain(tenantId1, "Rule chain v1.0"); RuleChainMetaData metaData = ruleChainService.loadRuleChainMetaData(tenantId1, ruleChain.getId()); @@ -288,91 +349,439 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp RuleChain importedRuleChain = importResult.getSavedEntity(); RuleChainMetaData importedMetaData = ruleChainService.loadRuleChainMetaData(tenantId1, importedRuleChain.getId()); - checkImportedEntity(tenantId1, ruleChain, tenantId1, importResult); + checkImportedEntity(tenantId1, ruleChain, tenantId1, importResult.getSavedEntity()); checkImportedRuleChainData(ruleChain, metaData, importedRuleChain, importedMetaData); } - private void checkImportedRuleChainData(RuleChain initialRuleChain, RuleChainMetaData initialMetaData, RuleChain importedRuleChain, RuleChainMetaData importedMetaData) { - assertThat(importedRuleChain.getType()).isEqualTo(initialRuleChain.getType()); - assertThat(importedRuleChain.getName()).isEqualTo(initialRuleChain.getName()); - assertThat(importedRuleChain.isDebugMode()).isEqualTo(initialRuleChain.isDebugMode()); - assertThat(importedRuleChain.getConfiguration()).isEqualTo(initialRuleChain.getConfiguration()); - - assertThat(importedMetaData.getConnections()).isEqualTo(initialMetaData.getConnections()); - assertThat(importedMetaData.getFirstNodeIndex()).isEqualTo(initialMetaData.getFirstNodeIndex()); - for (int i = 0; i < initialMetaData.getNodes().size(); i++) { - RuleNode initialNode = initialMetaData.getNodes().get(i); - RuleNode importedNode = importedMetaData.getNodes().get(i); - assertThat(importedNode.getRuleChainId()).isEqualTo(importedRuleChain.getId()); - assertThat(importedNode.getName()).isEqualTo(initialNode.getName()); - assertThat(importedNode.getType()).isEqualTo(initialNode.getType()); - assertThat(importedNode.getConfiguration()).isEqualTo(initialNode.getConfiguration()); - assertThat(importedNode.getAdditionalInfo()).isEqualTo(initialNode.getAdditionalInfo()); - } + + @Test + public void testExportImportBatch_betweenTenants() throws Exception { + logInAsTenantAdmin1(); + + Customer customer = createCustomer(tenantId1, "Customer 1"); + Asset asset = createAsset(tenantId1, customer.getId(), "A", "Customer 1 - Asset 1"); + RuleChain ruleChain = createRuleChain(tenantId1, "Rule chain 1"); + Dashboard dashboard = createDashboard(tenantId1, customer.getId(), "Customer 1 - Dashboard 1"); + DeviceProfile deviceProfile = createDeviceProfile(tenantId1, ruleChain.getId(), dashboard.getId(), "Device profile 1"); + Device device = createDevice(tenantId1, customer.getId(), deviceProfile.getId(), "Customer 1 - Device 1"); + + EntityListExportRequest exportRequest = new EntityListExportRequest(); + exportRequest.setExportSettings(new EntityExportSettings()); + exportRequest.setEntitiesIds(List.of(customer.getId(), asset.getId(), ruleChain.getId(), deviceProfile.getId(), dashboard.getId())); + List> exportDataList = exportEntities(exportRequest); + exportRequest.setEntitiesIds(List.of(device.getId())); + DeviceExportData deviceExportData = (DeviceExportData) exportEntities(exportRequest).get(0); + deviceExportData.getCredentials().setCredentialsId(RandomStringUtils.randomAlphanumeric(10)); + exportDataList.add(deviceExportData); + + logInAsTenantAdmin2(); + ImportRequest importRequest = new ImportRequest(); + importRequest.setImportSettings(EntityImportSettings.builder() + .updateReferencesToOtherEntities(true) + .build()); + importRequest.setExportDataList(exportDataList); + Map> importResults = importEntities(importRequest).stream() + .collect(Collectors.toMap(EntityImportResult::getEntityType, r -> r)); + + Customer importedCustomer = (Customer) importResults.get(EntityType.CUSTOMER).getSavedEntity(); + checkImportedEntity(tenantId1, customer, tenantId2, importedCustomer); + + Asset importedAsset = (Asset) importResults.get(EntityType.ASSET).getSavedEntity(); + checkImportedEntity(tenantId1, asset, tenantId2, importedAsset); + assertThat(importedAsset.getCustomerId()).isEqualTo(importedCustomer.getId()); + + RuleChain importedRuleChain = (RuleChain) importResults.get(EntityType.RULE_CHAIN).getSavedEntity(); + checkImportedEntity(tenantId1, ruleChain, tenantId2, importedRuleChain); + + Dashboard importedDashboard = (Dashboard) importResults.get(EntityType.DASHBOARD).getSavedEntity(); + checkImportedEntity(tenantId1, dashboard, tenantId2, importedDashboard); + assertThat(importedDashboard.getAssignedCustomers()).size().isOne(); + assertThat(importedDashboard.getAssignedCustomers()).hasOnlyOneElementSatisfying(customerInfo -> { + assertThat(customerInfo.getCustomerId()).isEqualTo(importedCustomer.getId()); + }); + + DeviceProfile importedDeviceProfile = (DeviceProfile) importResults.get(EntityType.DEVICE_PROFILE).getSavedEntity(); + checkImportedEntity(tenantId1, deviceProfile, tenantId2, importedDeviceProfile); + assertThat(importedDeviceProfile.getDefaultRuleChainId()).isEqualTo(importedRuleChain.getId()); + assertThat(importedDeviceProfile.getDefaultDashboardId()).isEqualTo(importedDashboard.getId()); + + Device importedDevice = (Device) importResults.get(EntityType.DEVICE).getSavedEntity(); + checkImportedEntity(tenantId1, device, tenantId2, importedDevice); + assertThat(importedDevice.getCustomerId()).isEqualTo(importedCustomer.getId()); + assertThat(importedDevice.getDeviceProfileId()).isEqualTo(importedDeviceProfile.getId()); } @Test - public void testExportImportSingleDashboard_betweenTenants() throws Exception { + public void testExportImportWithInboundRelations_betweenTenants() throws Exception { logInAsTenantAdmin1(); - Dashboard dashboard = createDashboard(tenantAdmin1.getTenantId(), null, "Dashboard of tenant 1"); - EntityExportData exportData = exportSingleEntity(dashboard.getId()); - assertThat(exportData.getEntity()).isEqualTo(dashboard); + Asset asset = createAsset(tenantId1, null, "A", "Asset 1"); + Device device = createDevice(tenantId1, null, null, "Device 1"); + EntityRelation relation = createRelation(asset.getId(), device.getId()); + + EntityListExportRequest exportRequest = new EntityListExportRequest(); + exportRequest.setEntitiesIds(List.of(asset.getId(), device.getId())); + exportRequest.setExportSettings(EntityExportSettings.builder() + .exportInboundRelations(true) + .exportOutboundRelations(false) + .build()); + List> exportDataList = exportEntities(exportRequest); + + EntityExportData deviceExportData = exportDataList.stream().filter(exportData -> exportData.getEntityType() == EntityType.DEVICE).findFirst().orElse(null); + assertThat(deviceExportData.getInboundRelations()).size().isOne(); + assertThat(deviceExportData.getInboundRelations().get(0)).matches(entityRelation -> { + return entityRelation.getFrom().equals(asset.getId()) && entityRelation.getTo().equals(device.getId()); + }); + ((DeviceExportData) deviceExportData).getCredentials().setCredentialsId("ab"); + ((Device) deviceExportData.getEntity()).setDeviceProfileId(null); logInAsTenantAdmin2(); - EntityImportResult importResult = importEntity(exportData); - checkImportedEntity(tenantId1, dashboard, tenantId2, importResult); - checkImportedDashboardData(dashboard, importResult.getSavedEntity()); + + ImportRequest importRequest = new ImportRequest(); + importRequest.setExportDataList(exportDataList); + importRequest.setImportSettings(EntityImportSettings.builder() + .importInboundRelations(true) + .build()); + Map> importResults = importEntities(importRequest).stream().collect(Collectors.toMap(EntityImportResult::getEntityType, r -> r)); + + Device importedDevice = (Device) importResults.get(EntityType.DEVICE).getSavedEntity(); + Asset importedAsset = (Asset) importResults.get(EntityType.ASSET).getSavedEntity(); + checkImportedEntity(tenantId1, device, tenantId2, importedDevice); + checkImportedEntity(tenantId1, asset, tenantId2, importedAsset); + + List importedRelations = relationService.findByTo(TenantId.SYS_TENANT_ID, importedDevice.getId(), RelationTypeGroup.COMMON); + assertThat(importedRelations).size().isOne(); + assertThat(importedRelations.get(0)).satisfies(importedRelation -> { + assertThat(importedRelation.getFrom()).isEqualTo(importedAsset.getId()); + assertThat(importedRelation.getType()).isEqualTo(relation.getType()); + assertThat(importedRelation.getAdditionalInfo()).isEqualTo(relation.getAdditionalInfo()); + }); } @Test - public void testExportImportSingleDashboard_sameTenant() throws Exception { + public void testExportImportWithRelations_betweenTenants() throws Exception { logInAsTenantAdmin1(); - Dashboard dashboard = createDashboard(tenantAdmin1.getTenantId(), null, "Dashboard v1.0"); - EntityExportData exportData = exportSingleEntity(dashboard.getId()); + Asset asset = createAsset(tenantId1, null, "A", "Asset 1"); + Device device = createDevice(tenantId1, null, null, "Device 1"); + EntityRelation relation = createRelation(asset.getId(), device.getId()); - EntityImportResult importResult = importEntity(exportData); - checkImportedEntity(tenantId1, dashboard, tenantId1, importResult); - checkImportedDashboardData(dashboard, importResult.getSavedEntity()); + EntityListExportRequest exportRequest = new EntityListExportRequest(); + exportRequest.setEntitiesIds(List.of(asset.getId(), device.getId())); + exportRequest.setExportSettings(EntityExportSettings.builder() + .exportInboundRelations(true) + .exportOutboundRelations(true) + .build()); + List> exportDataList = exportEntities(exportRequest); + + assertThat(exportDataList).allMatch(exportData -> exportData.getInboundRelations().size() + exportData.getOutboundRelations().size() == 1); + + EntityExportData deviceExportData = exportDataList.stream().filter(exportData -> exportData.getEntityType() == EntityType.DEVICE).findFirst().orElse(null); + ((DeviceExportData) deviceExportData).getCredentials().setCredentialsId("ab"); + ((Device) deviceExportData.getEntity()).setDeviceProfileId(null); + + logInAsTenantAdmin2(); + + ImportRequest importRequest = new ImportRequest(); + importRequest.setExportDataList(exportDataList); + importRequest.setImportSettings(EntityImportSettings.builder() + .importInboundRelations(true) + .importOutboundRelations(true) + .build()); + Map> importResults = importEntities(importRequest).stream().collect(Collectors.toMap(EntityImportResult::getEntityType, r -> r)); + + Device importedDevice = (Device) importResults.get(EntityType.DEVICE).getSavedEntity(); + Asset importedAsset = (Asset) importResults.get(EntityType.ASSET).getSavedEntity(); + + List importedRelations = relationService.findByTo(TenantId.SYS_TENANT_ID, importedDevice.getId(), RelationTypeGroup.COMMON); + assertThat(importedRelations).size().isOne(); + assertThat(importedRelations.get(0)).satisfies(importedRelation -> { + assertThat(importedRelation.getFrom()).isEqualTo(importedAsset.getId()); + assertThat(importedRelation.getType()).isEqualTo(relation.getType()); + assertThat(importedRelation.getAdditionalInfo()).isEqualTo(relation.getAdditionalInfo()); + }); } - private void checkImportedDashboardData(Dashboard initialDashboard, Dashboard importedDashboard) { - assertThat(importedDashboard.getTitle()).isEqualTo(initialDashboard.getTitle()); - assertThat(importedDashboard.getConfiguration()).isEqualTo(initialDashboard.getConfiguration()); - assertThat(importedDashboard.getImage()).isEqualTo(initialDashboard.getImage()); - assertThat(importedDashboard.isMobileHide()).isEqualTo(initialDashboard.isMobileHide()); - if (initialDashboard.getAssignedCustomers() != null) { - assertThat(importedDashboard.getAssignedCustomers()).containsAll(initialDashboard.getAssignedCustomers()); - } + @Test + public void testExportImportWithRelations_sameTenant() throws Exception { + logInAsTenantAdmin1(); + + Asset asset = createAsset(tenantId1, null, "A", "Asset 1"); + Device device1 = createDevice(tenantId1, null, null, "Device 1"); + EntityRelation relation1 = createRelation(asset.getId(), device1.getId()); + + SingleEntityExportRequest exportRequest = new SingleEntityExportRequest(); + exportRequest.setEntityId(asset.getId()); + exportRequest.setExportSettings(EntityExportSettings.builder() + .exportOutboundRelations(true) + .build()); + EntityExportData assetExportData = (EntityExportData) exportEntities(exportRequest).get(0); + assertThat(assetExportData.getOutboundRelations()).size().isOne(); + + Device device2 = createDevice(tenantId1, null, null, "Device 2"); + EntityRelation relation2 = createRelation(asset.getId(), device2.getId()); + + ImportRequest importRequest = new ImportRequest(); + importRequest.setExportDataList(List.of(assetExportData)); + importRequest.setImportSettings(EntityImportSettings.builder() + .importOutboundRelations(true) + .build()); + + importEntities(importRequest); + + List relations = relationService.findByFrom(TenantId.SYS_TENANT_ID, asset.getId(), RelationTypeGroup.COMMON); + assertThat(relations).contains(relation1, relation2); + } + + @Test + public void textExportImportWithRelations_sameTenant_removeExisting() throws Exception { + logInAsTenantAdmin1(); + + Asset asset1 = createAsset(tenantId1, null, "A", "Asset 1"); + Device device = createDevice(tenantId1, null, null, "Device 1"); + EntityRelation relation1 = createRelation(asset1.getId(), device.getId()); + + SingleEntityExportRequest exportRequest = new SingleEntityExportRequest(); + exportRequest.setEntityId(device.getId()); + exportRequest.setExportSettings(EntityExportSettings.builder() + .exportInboundRelations(true) + .build()); + EntityExportData deviceExportData = exportEntities(exportRequest).get(0); + assertThat(deviceExportData.getInboundRelations()).size().isOne(); + + Asset asset2 = createAsset(tenantId1, null, "A", "Asset 2"); + EntityRelation relation2 = createRelation(asset2.getId(), device.getId()); + + ImportRequest importRequest = new ImportRequest(); + importRequest.setExportDataList(List.of(deviceExportData)); + importRequest.setImportSettings(EntityImportSettings.builder() + .importInboundRelations(true) + .removeExistingRelations(true) + .build()); + + importEntities(importRequest); + + List relations = relationService.findByTo(TenantId.SYS_TENANT_ID, device.getId(), RelationTypeGroup.COMMON); + assertThat(relations).contains(relation1); + assertThat(relations).doesNotContain(relation2); + } + + + @Test + public void testExportImportDeviceProfile_betweenTenants_findExistingByName() throws Exception { + logInAsTenantAdmin1(); + DeviceProfile defaultDeviceProfile = deviceProfileService.findDefaultDeviceProfile(tenantId1); + + EntityListExportRequest exportRequest = new EntityListExportRequest(); + exportRequest.setEntitiesIds(List.of(defaultDeviceProfile.getId())); + exportRequest.setExportSettings(new EntityExportSettings()); + List> exportDataList = exportEntities(exportRequest); + + logInAsTenantAdmin2(); + ImportRequest importRequest = new ImportRequest(); + importRequest.setExportDataList(exportDataList); + importRequest.setImportSettings(EntityImportSettings.builder() + .findExistingByName(false) + .updateReferencesToOtherEntities(true) + .build()); + assertThatThrownBy(() -> { + importEntities(importRequest); + }).hasMessageContaining("default device profile is present"); + + importRequest.getImportSettings().setFindExistingByName(true); + importEntities(importRequest); + checkImportedEntity(tenantId1, defaultDeviceProfile, tenantId2, deviceProfileService.findDefaultDeviceProfile(tenantId2)); } + @Test + public void testExportImportDashboard_betweenTenants_doNotUpdateReferencesToOtherEntities() throws Exception { + logInAsTenantAdmin1(); + Customer customer = createCustomer(tenantId1, "Customer 1"); + Dashboard dashboard = createDashboard(tenantId1, customer.getId(), "Dashboard 1"); + + EntityListExportRequest exportRequest = new EntityListExportRequest(); + exportRequest.setEntitiesIds(List.of(customer.getId(), dashboard.getId())); + exportRequest.setExportSettings(new EntityExportSettings()); + List> exportDataList = exportEntities(exportRequest); + + logInAsTenantAdmin2(); + Map> importResults = importEntities(exportDataList).stream().collect(Collectors.toMap(EntityImportResult::getEntityType, r -> r)); + + Customer importedCustomer = (Customer) importResults.get(EntityType.CUSTOMER).getSavedEntity(); + Dashboard importedDashboard = (Dashboard) importResults.get(EntityType.DASHBOARD).getSavedEntity(); + assertThat(importedDashboard.getAssignedCustomers()).hasOnlyOneElementSatisfying(customerInfo -> { + assertThat(customerInfo.getCustomerId()).isEqualTo(importedCustomer.getId()); + }); + assertThat(importedDashboard.getConfiguration()).isEqualTo(dashboard.getConfiguration()); - private & HasTenantId> void checkImportedEntity(TenantId tenantId1, E initialEntity, TenantId tenantId2, EntityImportResult importResult) { - E importedEntity = importResult.getSavedEntity(); + logInAsTenantAdmin1(); + dashboard.setConfiguration(JacksonUtil.newObjectNode().set("aaa", new TextNode("bbb"))); + dashboard = dashboardService.saveDashboard(dashboard); + dashboard = dashboardService.unassignDashboardFromCustomer(tenantId1, dashboard.getId(), customer.getId()); + EntityExportData updatedDashboardExportData = exportSingleEntity(dashboard.getId()); + assertThat(updatedDashboardExportData.getEntity().getAssignedCustomers()).isNullOrEmpty(); - assertThat(initialEntity.getTenantId()).isEqualTo(tenantId1); - assertThat(importedEntity.getTenantId()).isEqualTo(tenantId2); + logInAsTenantAdmin2(); + ImportRequest importRequest = new ImportRequest(); + importRequest.setExportDataList(List.of(updatedDashboardExportData)); + importRequest.setImportSettings(EntityImportSettings.builder() + .updateReferencesToOtherEntities(false) + .build()); + importedDashboard = (Dashboard) importEntities(importRequest).get(0).getSavedEntity(); + + assertThat(importedDashboard.getConfiguration()).isEqualTo(dashboard.getConfiguration()); + assertThat(importedDashboard.getAssignedCustomers()).hasOnlyOneElementSatisfying(customerInfo -> { + assertThat(customerInfo.getCustomerId()).isEqualTo(importedCustomer.getId()); + }); + } - assertThat(importedEntity.getExternalId()).isEqualTo(initialEntity.getId()); - boolean sameTenant = tenantId1.equals(tenantId2); - if (!sameTenant) { - assertThat(importedEntity.getId()).isNotEqualTo(initialEntity.getId()); - } else { - assertThat(importedEntity.getId()).isEqualTo(initialEntity.getId()); - assertThat(importResult.getOldEntity()).isEqualTo(initialEntity); + @Test + public void testExportRequests() throws Exception { + logInAsTenantAdmin1(); + + Device device = createDevice(tenantId1, null, null, "Device 1"); + DeviceProfile deviceProfile = createDeviceProfile(tenantId1, null, null, "Device profile 1"); + RuleChain ruleChain = createRuleChain(tenantId1, "Rule chain 1"); + Asset asset = createAsset(tenantId1, null, "A", "Asset 1"); + Dashboard dashboard = createDashboard(tenantId1, null, "Dashboard 1"); + Customer customer = createCustomer(tenantId1, "Customer 1"); + + Map> entities = Map.of( + EntityType.DEVICE, device, EntityType.DEVICE_PROFILE, deviceProfile, + EntityType.RULE_CHAIN, ruleChain, EntityType.ASSET, asset, + EntityType.DASHBOARD, dashboard, EntityType.CUSTOMER, customer + ); + + for (ExportableEntity entity : entities.values()) { + testEntityTypeExportRequest(entity); + testCustomEntityFilterExportRequest(entity); } } + private void testEntityTypeExportRequest(ExportableEntity entity) throws Exception { + EntityTypeExportRequest exportRequest = new EntityTypeExportRequest(); + exportRequest.setExportSettings(new EntityExportSettings()); + exportRequest.setPageSize(10); + exportRequest.setEntityType(entity.getId().getEntityType()); + + List> exportDataList = exportEntities(exportRequest); + assertThat(exportDataList).size().isNotZero(); + assertThat(exportDataList).anySatisfy(exportData -> { + assertThat(exportData.getEntity()).isEqualTo(entity); + }); + } + + private void testCustomEntityFilterExportRequest(ExportableEntity entity) throws Exception { + CustomEntityFilterExportRequest exportRequest = new CustomEntityFilterExportRequest(); + exportRequest.setExportSettings(new EntityExportSettings()); + exportRequest.setPageSize(10); - private void logInAsTenantAdmin1() throws Exception { - login(tenantAdmin1.getEmail(), "12345678"); + EntityListFilter filter = new EntityListFilter(); + filter.setEntityType(entity.getId().getEntityType()); + filter.setEntityList(List.of(entity.getId().toString())); + exportRequest.setFilter(filter); + + List> exportDataList = exportEntities(exportRequest); + assertThat(exportDataList).hasOnlyOneElementSatisfying(exportData -> { + assertThat(exportData.getEntity()).isEqualTo(entity); + }); } - private void logInAsTenantAdmin2() throws Exception { - login(tenantAdmin2.getEmail(), "12345678"); + + @Test + public void testExportImportCustomerEntities_betweenTenants() throws Exception { + logInAsTenantAdmin1(); + Customer customer = createCustomer(tenantId1, "Customer 1"); + + Device tenantDevice = createDevice(tenantId1, null, null, "Tenant device 1"); + Device customerDevice = createDevice(tenantId1, customer.getId(), null, "Customer device 1"); + Asset tenantAsset = createAsset(tenantId1, null, "A", "Tenant asset 1"); + Asset customerAsset = createAsset(tenantId1, customer.getId(), "A", "Customer asset 1"); + + List exportRequests = new ArrayList<>(); + + for (EntityType entityType : Set.of(EntityType.DEVICE, EntityType.ASSET)) { + EntityTypeExportRequest exportRequest = new EntityTypeExportRequest(); + exportRequest.setExportSettings(new EntityExportSettings()); + exportRequest.setPageSize(10); + exportRequest.setEntityType(entityType); + exportRequest.setCustomerId(customer.getId()); + exportRequests.add(exportRequest); + } + + List> exportDataList = exportEntities(exportRequests); + assertThat(exportDataList).size().isEqualTo(2); + assertThat(exportDataList).anySatisfy(exportData -> { + assertThat(exportData.getEntity()).isEqualTo(customerDevice); + }); + assertThat(exportDataList).anySatisfy(exportData -> { + assertThat(exportData.getEntity()).isEqualTo(customerAsset); + }); + } + + + @Test + public void testEntityEventsOnImport() throws Exception { + logInAsTenantAdmin1(); + + Customer customer = createCustomer(tenantId1, "Customer 1"); + Asset asset = createAsset(tenantId1, null, "A", "Asset 1"); + RuleChain ruleChain = createRuleChain(tenantId1, "Rule chain 1"); + Dashboard dashboard = createDashboard(tenantId1, null, "Dashboard 1"); + DeviceProfile deviceProfile = createDeviceProfile(tenantId1, ruleChain.getId(), dashboard.getId(), "Device profile 1"); + Device device = createDevice(tenantId1, null, deviceProfile.getId(), "Device 1"); + + EntityListExportRequest exportRequest = new EntityListExportRequest(); + exportRequest.setEntitiesIds(List.of(customer.getId(), asset.getId(), device.getId(), ruleChain.getId(), dashboard.getId(), deviceProfile.getId())); + exportRequest.setExportSettings(new EntityExportSettings()); + + Map entitiesExportData = exportEntities(exportRequest).stream() + .collect(Collectors.toMap(EntityExportData::getEntityType, r -> r)); + + logInAsTenantAdmin2(); + + Customer importedCustomer = (Customer) importEntity(entitiesExportData.get(EntityType.CUSTOMER)).getSavedEntity(); + verify(entityActionService).logEntityAction(any(), eq(importedCustomer.getId()), eq(importedCustomer), + any(), eq(ActionType.ADDED), isNull()); + importEntity(entitiesExportData.get(EntityType.CUSTOMER)); + verify(entityActionService).logEntityAction(any(), eq(importedCustomer.getId()), eq(importedCustomer), + any(), eq(ActionType.UPDATED), isNull()); + verify(clusterService).sendNotificationMsgToEdgeService(any(), any(), eq(importedCustomer.getId()), any(), any(), eq(EdgeEventActionType.UPDATED)); + + Asset importedAsset = (Asset) importEntity(entitiesExportData.get(EntityType.ASSET)).getSavedEntity(); + verify(entityActionService).logEntityAction(any(), eq(importedAsset.getId()), eq(importedAsset), + any(), eq(ActionType.ADDED), isNull()); + importEntity(entitiesExportData.get(EntityType.ASSET)); + verify(entityActionService).logEntityAction(any(), eq(importedAsset.getId()), eq(importedAsset), + any(), eq(ActionType.UPDATED), isNull()); + verify(clusterService).sendNotificationMsgToEdgeService(any(), any(), eq(importedAsset.getId()), any(), any(), eq(EdgeEventActionType.UPDATED)); + + RuleChain importedRuleChain = (RuleChain) importEntity(entitiesExportData.get(EntityType.RULE_CHAIN)).getSavedEntity(); + verify(entityActionService).logEntityAction(any(), eq(importedRuleChain.getId()), eq(importedRuleChain), + any(), eq(ActionType.ADDED), isNull()); + verify(clusterService).broadcastEntityStateChangeEvent(any(), eq(importedRuleChain.getId()), eq(ComponentLifecycleEvent.CREATED)); + + Dashboard importedDashboard = (Dashboard) importEntity(entitiesExportData.get(EntityType.DASHBOARD)).getSavedEntity(); + verify(entityActionService).logEntityAction(any(), eq(importedDashboard.getId()), eq(importedDashboard), + any(), eq(ActionType.ADDED), isNull()); + + DeviceProfile importedDeviceProfile = (DeviceProfile) importEntity(entitiesExportData.get(EntityType.DEVICE_PROFILE)).getSavedEntity(); + verify(entityActionService).logEntityAction(any(), eq(importedDeviceProfile.getId()), eq(importedDeviceProfile), + any(), eq(ActionType.ADDED), isNull()); + verify(clusterService).onDeviceProfileChange(eq(importedDeviceProfile), any()); + verify(clusterService).broadcastEntityStateChangeEvent(any(), eq(importedDeviceProfile.getId()), eq(ComponentLifecycleEvent.CREATED)); + verify(clusterService).sendNotificationMsgToEdgeService(any(), any(), eq(importedDeviceProfile.getId()), any(), any(), eq(EdgeEventActionType.ADDED)); + verify(otaPackageStateService).update(eq(importedDeviceProfile), eq(false), eq(false)); + + ((DeviceExportData)entitiesExportData.get(EntityType.DEVICE)).getCredentials().setCredentialsId("abc"); + Device importedDevice = (Device) importEntity(entitiesExportData.get(EntityType.DEVICE)).getSavedEntity(); + verify(entityActionService).logEntityAction(any(), eq(importedDevice.getId()), eq(importedDevice), + any(), eq(ActionType.ADDED), isNull()); + verify(clusterService).onDeviceUpdated(eq(importedDevice), isNull()); + importEntity(entitiesExportData.get(EntityType.DEVICE)); + verify(clusterService).onDeviceUpdated(eq(importedDevice), eq(importedDevice)); } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java b/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java index 50569b7a02..4da7de93ee 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java @@ -26,4 +26,7 @@ public interface ExportableEntity extends HasId, HasName I getExternalId(); void setExternalId(I externalId); + long getCreatedTime(); + void setCreatedTime(long createdTime); + } From 119430ff9a483d12f96f410d8c93dad1ffd11d87 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Mon, 18 Apr 2022 14:32:43 +0300 Subject: [PATCH 22/39] Minor improvements --- .../sync/DefaultEntitiesExportImportService.java | 12 ++++++++++-- .../sync/importing/impl/BaseEntityImportService.java | 2 +- .../sync/importing/impl/DashboardImportService.java | 10 ++++++++++ .../sync/importing/impl/RuleChainImportService.java | 10 ++++++++++ .../server/dao/dashboard/DashboardService.java | 5 +++++ .../server/dao/rule/RuleChainService.java | 3 +++ .../server/common/data/ExportableEntity.java | 1 - .../thingsboard/server/dao/ExportableEntityDao.java | 2 +- .../server/dao/dashboard/DashboardDao.java | 6 ++++++ .../server/dao/dashboard/DashboardServiceImpl.java | 7 +++++++ .../server/dao/rule/BaseRuleChainService.java | 7 ++++++- .../server/dao/sql/asset/JpaAssetDao.java | 2 +- .../server/dao/sql/customer/JpaCustomerDao.java | 2 +- .../dao/sql/dashboard/DashboardRepository.java | 3 ++- .../server/dao/sql/dashboard/JpaDashboardDao.java | 5 +++-- .../server/dao/sql/device/JpaDeviceDao.java | 2 +- .../server/dao/sql/device/JpaDeviceProfileDao.java | 2 +- .../server/dao/sql/rule/JpaRuleChainDao.java | 5 ----- .../server/dao/sql/rule/RuleChainRepository.java | 2 -- 19 files changed, 68 insertions(+), 20 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java index cfb895c36a..1db59d68f1 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java @@ -83,7 +83,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS @Transactional(rollbackFor = Exception.class) @Override public List> importEntities(SecurityUser user, List> exportDataList, EntityImportSettings importSettings) throws ThingsboardException { - exportDataList.sort(Comparator.comparing(exportData -> SUPPORTED_ENTITY_TYPES.indexOf(exportData.getEntityType()))); + fixOrder(exportDataList); List> importResults = new ArrayList<>(); for (EntityExportData exportData : exportDataList) { @@ -101,6 +101,10 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS return importResults; } + private void fixOrder(List> exportDataList) { + exportDataList.sort(Comparator.comparing(exportData -> SUPPORTED_ENTITY_TYPES.indexOf(exportData.getEntityType()))); + } + private , I extends EntityId> EntityImportResult importEntity(SecurityUser user, EntityExportData exportData, EntityImportSettings importSettings) throws ThingsboardException { if (exportData.getEntity() == null || exportData.getEntity().getId() == null) { throw new DataValidationException("Invalid entity data"); @@ -137,7 +141,11 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS @Override public , I extends EntityId> E findEntityByTenantIdAndName(TenantId tenantId, EntityType entityType, String name) { ExportableEntityDao dao = (ExportableEntityDao) getDao(entityType); - return dao.findFirstByTenantIdAndName(tenantId.getId(), name); + try { + return dao.findByTenantIdAndName(tenantId.getId(), name); + } catch (UnsupportedOperationException e) { + return null; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java index 97547868d2..c7aaf9c198 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java @@ -158,7 +158,7 @@ public abstract class BaseEntityImportService Optional.ofNullable(exportableEntitiesService.findEntityByTenantIdAndId(tenantId, entity.getId()))) .or(() -> { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java index fda6904421..c2a3e4aea7 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java @@ -36,6 +36,7 @@ import org.thingsboard.server.dao.sql.query.DefaultEntityQueryRepository; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; import java.util.Collections; import java.util.HashSet; @@ -58,6 +59,15 @@ public class DashboardImportService extends BaseEntityImportService exportData, NewIdProvider idProvider) { Optional.ofNullable(dashboard.getConfiguration()) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java index 5fef00cbed..dcc3b6ca92 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java @@ -33,6 +33,7 @@ import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.RuleChainExportData; +import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; import java.util.Collections; import java.util.Optional; @@ -50,6 +51,15 @@ public class RuleChainImportService extends BaseEntityImportService findDashboardsByTenantIdAndEdgeId(TenantId tenantId, EdgeId edgeId, PageLink pageLink); DashboardInfo findFirstDashboardInfoByTenantIdAndName(TenantId tenantId, String name); + + List findTenantDashboardsByTitle(TenantId tenantId, String title); + } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java index 4eed652bb7..9069b75710 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleChainService.java @@ -32,6 +32,7 @@ import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleChainUpdateResult; import org.thingsboard.server.common.data.rule.RuleNode; +import java.util.Collection; import java.util.List; /** @@ -65,6 +66,8 @@ public interface RuleChainService { PageData findTenantRuleChainsByType(TenantId tenantId, RuleChainType type, PageLink pageLink); + Collection findTenantRuleChainsByTypeAndName(TenantId tenantId, RuleChainType type, String name); + void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId); void deleteRuleChainsByTenantId(TenantId tenantId); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java b/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java index 4da7de93ee..c6d13013b1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ExportableEntity.java @@ -20,7 +20,6 @@ import org.thingsboard.server.common.data.id.HasId; public interface ExportableEntity extends HasId, HasName { - I getId(); void setId(I id); I getExternalId(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java index 467389d9d0..cd534f24e6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java @@ -23,6 +23,6 @@ public interface ExportableEntityDao> extends Dao< T findByTenantIdAndExternalId(UUID tenantId, UUID externalId); - T findFirstByTenantIdAndName(UUID tenantId, String name); + default T findByTenantIdAndName(UUID tenantId, String name) { throw new UnsupportedOperationException(); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java index 90353f49b3..87804fab80 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardDao.java @@ -21,6 +21,9 @@ import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.dao.TenantEntityDao; +import java.util.List; +import java.util.UUID; + /** * The Interface DashboardDao. */ @@ -33,4 +36,7 @@ public interface DashboardDao extends Dao, TenantEntityDao, Exportabl * @return saved dashboard object */ Dashboard save(TenantId tenantId, Dashboard dashboard); + + List findByTenantIdAndTitle(UUID tenantId, String title); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java index ee5c44e20f..1e82d3f27c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/dashboard/DashboardServiceImpl.java @@ -40,6 +40,8 @@ import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; +import java.util.List; + import static org.thingsboard.server.dao.service.Validator.validateId; @Service @@ -279,6 +281,11 @@ public class DashboardServiceImpl extends AbstractEntityService implements Dashb return dashboardInfoDao.findFirstByTenantIdAndName(tenantId.getId(), name); } + @Override + public List findTenantDashboardsByTitle(TenantId tenantId, String title) { + return dashboardDao.findByTenantIdAndTitle(tenantId.getId(), title); + } + private PaginatedRemover tenantDashboardsRemover = new PaginatedRemover() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java index fd84c12a1b..8920cc0c1a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleChainService.java @@ -391,6 +391,11 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC return ruleChainDao.findRuleChainsByTenantIdAndType(tenantId.getId(), type, pageLink); } + @Override + public Collection findTenantRuleChainsByTypeAndName(TenantId tenantId, RuleChainType type, String name) { + return ruleChainDao.findByTenantIdAndTypeAndName(tenantId, type, name); + } + @Override @Transactional public void deleteRuleChainById(TenantId tenantId, RuleChainId ruleChainId) { @@ -457,7 +462,7 @@ public class BaseRuleChainService extends AbstractEntityService implements RuleC ruleChain.setRoot(false); if (overwrite) { - Collection existingRuleChains = ruleChainDao.findByTenantIdAndTypeAndName(tenantId, + Collection existingRuleChains = findTenantRuleChainsByTypeAndName(tenantId, Optional.ofNullable(ruleChain.getType()).orElse(RuleChainType.CORE), ruleChain.getName()); Optional existingRuleChain = existingRuleChains.stream().findFirst(); if (existingRuleChain.isPresent()) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java index 5988c2d2db..bcfdaae965 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java @@ -215,7 +215,7 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao im } @Override - public Asset findFirstByTenantIdAndName(UUID tenantId, String name) { + public Asset findByTenantIdAndName(UUID tenantId, String name) { return findAssetsByTenantIdAndName(tenantId, name).orElse(null); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java index fa09304427..c6ba310793 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java @@ -76,7 +76,7 @@ public class JpaCustomerDao extends JpaAbstractSearchTextDao findByTenantIdAndTitle(UUID tenantId, String title); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java index 771de3c813..6ff9fc6bfb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java @@ -26,6 +26,7 @@ import org.thingsboard.server.dao.dashboard.DashboardDao; import org.thingsboard.server.dao.model.sql.DashboardEntity; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; +import java.util.List; import java.util.UUID; /** @@ -58,8 +59,8 @@ public class JpaDashboardDao extends JpaAbstractSearchTextDao findByTenantIdAndTitle(UUID tenantId, String title) { + return DaoUtil.convertDataList(dashboardRepository.findByTenantIdAndTitle(tenantId, title)); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java index d2247f5e82..93cd857660 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java @@ -309,7 +309,7 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao } @Override - public Device findFirstByTenantIdAndName(UUID tenantId, String name) { + public Device findByTenantIdAndName(UUID tenantId, String name) { return findDeviceByTenantIdAndName(tenantId, name).orElse(null); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java index b21a07442f..cae94cfb73 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java @@ -117,7 +117,7 @@ public class JpaDeviceProfileDao extends JpaAbstractSearchTextDao findByTenantIdAndTypeAndName(UUID tenantId, RuleChainType type, String name); - RuleChainEntity findFirstByTenantIdAndName(UUID tenantId, String name); - } From eacc0e6bbebd8f31dc30f9a8c129cf9b4d1f3946 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Mon, 18 Apr 2022 16:19:06 +0300 Subject: [PATCH 23/39] Remove import option whether to update references to other entities; minor improvements --- .../importing/data/EntityImportSettings.java | 2 - .../importing/impl/AssetImportService.java | 6 +-- .../impl/BaseEntityImportService.java | 43 +++-------------- .../importing/impl/CustomerImportService.java | 4 +- .../impl/DashboardImportService.java | 23 +++++---- .../importing/impl/DeviceImportService.java | 12 ++--- .../impl/DeviceProfileImportService.java | 12 ++--- .../impl/RuleChainImportService.java | 8 ++-- ...aseEntitiesExportImportControllerTest.java | 4 +- ...EntitiesExportImportControllerSqlTest.java | 47 +------------------ 10 files changed, 42 insertions(+), 119 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java index 3b457e1f5a..d4b0c46f31 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java @@ -30,6 +30,4 @@ public class EntityImportSettings { private boolean importInboundRelations; private boolean importOutboundRelations; private boolean removeExistingRelations; - - private boolean updateReferencesToOtherEntities; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java index 02eaae27b5..ae54a988f3 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java @@ -36,13 +36,13 @@ public class AssetImportService extends BaseEntityImportService exportData, NewIdProvider idProvider) { + protected Asset prepareAndSave(TenantId tenantId, Asset asset, EntityExportData exportData, IdProvider idProvider) { return assetService.saveAsset(asset); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java index c7aaf9c198..82df075238 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java @@ -40,13 +40,9 @@ import org.thingsboard.server.service.sync.importing.EntityImportService; import org.thingsboard.server.service.sync.importing.data.EntityImportResult; import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; -import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Optional; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.function.Function; import java.util.stream.Collectors; public abstract class BaseEntityImportService, D extends EntityExportData> implements EntityImportService { @@ -68,7 +64,7 @@ public abstract class BaseEntityImportService importResult, D exportData, - NewIdProvider idProvider, EntityImportSettings importSettings) throws ThingsboardException { + IdProvider idProvider, EntityImportSettings importSettings) throws ThingsboardException { E savedEntity = importResult.getSavedEntity(); E oldEntity = importResult.getOldEntity(); @@ -179,37 +175,10 @@ public abstract class BaseEntityImportService ID get(Function idExtractor) { - if (existingEntity == null || importSettings.isUpdateReferencesToOtherEntities()) { - return getInternalId(idExtractor.apply(this.entity)); - } else { - return idExtractor.apply(existingEntity); - } - } - - public ID getInternal(ID externalId) { - return getInternalId(externalId); - } - - public Set get(Function> listExtractor, Function idGetter, BiConsumer idSetter) { - if (existingEntity == null || importSettings.isUpdateReferencesToOtherEntities()) { - return Optional.ofNullable(listExtractor.apply(entity)).orElse(Collections.emptySet()).stream() - .peek(t -> { - idSetter.accept(t, getInternalId(idGetter.apply(t))); - }) - .collect(Collectors.toSet()); - } else { - return listExtractor.apply(existingEntity); - } - } - private ID getInternalId(ID externalId) { + public ID getInternalId(ID externalId) { if (externalId == null || externalId.isNullUid()) return null; HasId entity = findInternalEntity(user.getTenantId(), externalId); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java index b03c0ab335..1a79fe666b 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java @@ -36,12 +36,12 @@ public class CustomerImportService extends BaseEntityImportService exportData, NewIdProvider idProvider) { + protected Customer prepareAndSave(TenantId tenantId, Customer customer, EntityExportData exportData, IdProvider idProvider) { return customerService.saveCustomer(customer); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java index c2a3e4aea7..e7c5ed0ea3 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java @@ -55,7 +55,7 @@ public class DashboardImportService extends BaseEntityImportService exportData, NewIdProvider idProvider) { + protected Dashboard prepareAndSave(TenantId tenantId, Dashboard dashboard, EntityExportData exportData, IdProvider idProvider) { Optional.ofNullable(dashboard.getConfiguration()) .flatMap(configuration -> Optional.ofNullable(configuration.get("entityAliases"))) .filter(JsonNode::isObject) @@ -84,15 +84,17 @@ public class DashboardImportService extends BaseEntityImportService { String uuid = matchResult.group(); EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, uuid); - return idProvider.getInternal(entityId).toString(); + return idProvider.getInternalId(entityId).toString(); }); ((ObjectNode) entityAlias).set("filter", JacksonUtil.toJsonNode(newFilterJson)); }); })); - // TODO [viacheslav]: improve the code below + Set assignedCustomers = Optional.ofNullable(dashboard.getAssignedCustomers()).orElse(Collections.emptySet()).stream() + .peek(customerInfo -> customerInfo.setCustomerId(idProvider.getInternalId(customerInfo.getCustomerId()))) + .collect(Collectors.toSet()); + if (dashboard.getId() == null) { - Set assignedCustomers = idProvider.get(Dashboard::getAssignedCustomers, ShortCustomerInfo::getCustomerId, ShortCustomerInfo::setCustomerId); dashboard.setAssignedCustomers(null); dashboard = dashboardService.saveDashboard(dashboard); for (ShortCustomerInfo customerInfo : assignedCustomers) { @@ -101,20 +103,21 @@ public class DashboardImportService extends BaseEntityImportService existingAssignedCustomers = Optional.ofNullable(dashboardService.findDashboardById(tenantId, dashboard.getId()).getAssignedCustomers()) .orElse(Collections.emptySet()).stream().map(ShortCustomerInfo::getCustomerId).collect(Collectors.toSet()); - Set newAssignedCustomers = idProvider.get(Dashboard::getAssignedCustomers, ShortCustomerInfo::getCustomerId, ShortCustomerInfo::setCustomerId).stream() - .map(ShortCustomerInfo::getCustomerId).collect(Collectors.toSet()); + Set newAssignedCustomers = assignedCustomers.stream().map(ShortCustomerInfo::getCustomerId).collect(Collectors.toSet()); Set toUnassign = new HashSet<>(existingAssignedCustomers); toUnassign.removeAll(newAssignedCustomers); for (CustomerId customerId : toUnassign) { - dashboardService.unassignDashboardFromCustomer(tenantId, dashboard.getId(), customerId); + assignedCustomers = dashboardService.unassignDashboardFromCustomer(tenantId, dashboard.getId(), customerId).getAssignedCustomers(); } + Set toAssign = new HashSet<>(newAssignedCustomers); toAssign.removeAll(existingAssignedCustomers); for (CustomerId customerId : toAssign) { - dashboardService.assignDashboardToCustomer(tenantId, dashboard.getId(), customerId); + assignedCustomers = dashboardService.assignDashboardToCustomer(tenantId, dashboard.getId(), customerId).getAssignedCustomers(); } - dashboard.setAssignedCustomers(dashboardService.findDashboardById(tenantId, dashboard.getId()).getAssignedCustomers()); + + dashboard.setAssignedCustomers(assignedCustomers); dashboard = dashboardService.saveDashboard(dashboard); } return dashboard; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java index 504bd1a828..091b6eb856 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java @@ -35,16 +35,16 @@ public class DeviceImportService extends BaseEntityImportService exportData, NewIdProvider idProvider) { - deviceProfile.setDefaultRuleChainId(idProvider.get(DeviceProfile::getDefaultRuleChainId)); - deviceProfile.setDefaultDashboardId(idProvider.get(DeviceProfile::getDefaultDashboardId)); - deviceProfile.setFirmwareId(idProvider.get(DeviceProfile::getFirmwareId)); - deviceProfile.setSoftwareId(idProvider.get(DeviceProfile::getSoftwareId)); + protected DeviceProfile prepareAndSave(TenantId tenantId, DeviceProfile deviceProfile, EntityExportData exportData, IdProvider idProvider) { + deviceProfile.setDefaultRuleChainId(idProvider.getInternalId(deviceProfile.getDefaultRuleChainId())); + deviceProfile.setDefaultDashboardId(idProvider.getInternalId(deviceProfile.getDefaultDashboardId())); + deviceProfile.setFirmwareId(idProvider.getInternalId(deviceProfile.getFirmwareId())); + deviceProfile.setSoftwareId(idProvider.getInternalId(deviceProfile.getSoftwareId())); return deviceProfileService.saveDeviceProfile(deviceProfile); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java index dcc3b6ca92..1e4ed958a8 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java @@ -47,7 +47,7 @@ public class RuleChainImportService extends BaseEntityImportService { @@ -73,14 +73,14 @@ public class RuleChainImportService extends BaseEntityImportService { ((ObjectNode) ruleNodeConfig).set("ruleChainId", new TextNode( - idProvider.getInternal(new RuleChainId(otherRuleChainUuid)).toString() + idProvider.getInternalId(new RuleChainId(otherRuleChainUuid)).toString() )); ruleNode.setConfiguration(ruleNodeConfig); }); }); Optional.ofNullable(metaData.getRuleChainConnections()).orElse(Collections.emptyList()) .forEach(ruleChainConnectionInfo -> { - ruleChainConnectionInfo.setTargetRuleChainId(idProvider.getInternal(ruleChainConnectionInfo.getTargetRuleChainId())); + ruleChainConnectionInfo.setTargetRuleChainId(idProvider.getInternalId(ruleChainConnectionInfo.getTargetRuleChainId())); }); ruleChain.setFirstRuleNodeId(null); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEntitiesExportImportControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEntitiesExportImportControllerTest.java index a8a5bca534..b5c5a5aab8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEntitiesExportImportControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEntitiesExportImportControllerTest.java @@ -360,9 +360,7 @@ public abstract class BaseEntitiesExportImportControllerTest extends AbstractCon protected List> importEntities(List> exportDataList) throws Exception { ImportRequest importRequest = new ImportRequest(); - importRequest.setImportSettings(EntityImportSettings.builder() - .updateReferencesToOtherEntities(true) - .build()); + importRequest.setImportSettings(new EntityImportSettings()); importRequest.setExportDataList(exportDataList); return importEntities(importRequest); } diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java index 920d91e93d..8a0c958b2c 100644 --- a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java @@ -376,9 +376,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp logInAsTenantAdmin2(); ImportRequest importRequest = new ImportRequest(); - importRequest.setImportSettings(EntityImportSettings.builder() - .updateReferencesToOtherEntities(true) - .build()); + importRequest.setImportSettings(new EntityImportSettings()); importRequest.setExportDataList(exportDataList); Map> importResults = importEntities(importRequest).stream() .collect(Collectors.toMap(EntityImportResult::getEntityType, r -> r)); @@ -583,7 +581,6 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp importRequest.setExportDataList(exportDataList); importRequest.setImportSettings(EntityImportSettings.builder() .findExistingByName(false) - .updateReferencesToOtherEntities(true) .build()); assertThatThrownBy(() -> { importEntities(importRequest); @@ -594,48 +591,6 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp checkImportedEntity(tenantId1, defaultDeviceProfile, tenantId2, deviceProfileService.findDefaultDeviceProfile(tenantId2)); } - @Test - public void testExportImportDashboard_betweenTenants_doNotUpdateReferencesToOtherEntities() throws Exception { - logInAsTenantAdmin1(); - Customer customer = createCustomer(tenantId1, "Customer 1"); - Dashboard dashboard = createDashboard(tenantId1, customer.getId(), "Dashboard 1"); - - EntityListExportRequest exportRequest = new EntityListExportRequest(); - exportRequest.setEntitiesIds(List.of(customer.getId(), dashboard.getId())); - exportRequest.setExportSettings(new EntityExportSettings()); - List> exportDataList = exportEntities(exportRequest); - - logInAsTenantAdmin2(); - Map> importResults = importEntities(exportDataList).stream().collect(Collectors.toMap(EntityImportResult::getEntityType, r -> r)); - - Customer importedCustomer = (Customer) importResults.get(EntityType.CUSTOMER).getSavedEntity(); - Dashboard importedDashboard = (Dashboard) importResults.get(EntityType.DASHBOARD).getSavedEntity(); - assertThat(importedDashboard.getAssignedCustomers()).hasOnlyOneElementSatisfying(customerInfo -> { - assertThat(customerInfo.getCustomerId()).isEqualTo(importedCustomer.getId()); - }); - assertThat(importedDashboard.getConfiguration()).isEqualTo(dashboard.getConfiguration()); - - logInAsTenantAdmin1(); - dashboard.setConfiguration(JacksonUtil.newObjectNode().set("aaa", new TextNode("bbb"))); - dashboard = dashboardService.saveDashboard(dashboard); - dashboard = dashboardService.unassignDashboardFromCustomer(tenantId1, dashboard.getId(), customer.getId()); - EntityExportData updatedDashboardExportData = exportSingleEntity(dashboard.getId()); - assertThat(updatedDashboardExportData.getEntity().getAssignedCustomers()).isNullOrEmpty(); - - logInAsTenantAdmin2(); - ImportRequest importRequest = new ImportRequest(); - importRequest.setExportDataList(List.of(updatedDashboardExportData)); - importRequest.setImportSettings(EntityImportSettings.builder() - .updateReferencesToOtherEntities(false) - .build()); - importedDashboard = (Dashboard) importEntities(importRequest).get(0).getSavedEntity(); - - assertThat(importedDashboard.getConfiguration()).isEqualTo(dashboard.getConfiguration()); - assertThat(importedDashboard.getAssignedCustomers()).hasOnlyOneElementSatisfying(customerInfo -> { - assertThat(customerInfo.getCustomerId()).isEqualTo(importedCustomer.getId()); - }); - } - @Test public void testExportRequests() throws Exception { From a5cdbca690b431bca9c7ad99a585840f50edac99 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 21 Apr 2022 13:37:45 +0300 Subject: [PATCH 24/39] Relations import: log entity action --- .../impl/BaseEntityImportService.java | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java index 82df075238..5802e140f3 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java @@ -91,11 +91,16 @@ public abstract class BaseEntityImportService importResult, D exportData, IdProvider idProvider, EntityImportSettings importSettings) throws ThingsboardException { E savedEntity = importResult.getSavedEntity(); E oldEntity = importResult.getOldEntity(); + importResult.addSendEventsCallback(() -> { + onEntitySaved(user, savedEntity, oldEntity); + }); + importResult.addSaveReferencesCallback(() -> { List newRelations = new LinkedList<>(); @@ -107,7 +112,7 @@ public abstract class BaseEntityImportService { + entityActionService.logEntityAction(user, relation.getFrom(), null, null, + ActionType.RELATION_ADD_OR_UPDATE, null, relation); + entityActionService.logEntityAction(user, relation.getTo(), null, null, + ActionType.RELATION_ADD_OR_UPDATE, null, relation); + }); } }); + } + private void deleteRelation(SecurityUser user, EntityRelation relation, EntityImportResult importResult) { + relationService.deleteRelation(user.getTenantId(), relation); importResult.addSendEventsCallback(() -> { - onEntitySaved(user, savedEntity, oldEntity); + entityActionService.logEntityAction(user, relation.getFrom(), null, null, + ActionType.RELATION_DELETED, null, relation); + entityActionService.logEntityAction(user, relation.getTo(), null, null, + ActionType.RELATION_DELETED, null, relation); }); } + protected void onEntitySaved(SecurityUser user, E savedEntity, E oldEntity) throws ThingsboardException { entityActionService.logEntityAction(user, savedEntity.getId(), savedEntity, savedEntity instanceof HasCustomerId ? ((HasCustomerId) savedEntity).getCustomerId() : user.getCustomerId(), From 832f32fbe1e5cfb28c2a47f30e9210fd62b9ece7 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 21 Apr 2022 14:34:55 +0300 Subject: [PATCH 25/39] Improvements for relations import --- .../sync/exporting/data/EntityExportData.java | 3 +- .../impl/DefaultEntityExportService.java | 22 ++--- .../importing/data/EntityImportSettings.java | 5 +- .../impl/BaseEntityImportService.java | 89 +++++++++---------- ...EntitiesExportImportControllerSqlTest.java | 27 +++--- 5 files changed, 68 insertions(+), 78 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java index 4ab6dd57b1..728d8212c4 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java @@ -43,7 +43,6 @@ public class EntityExportData> { private E entity; private EntityType entityType; - private List inboundRelations; - private List outboundRelations; + private List relations; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java index 4fe09e9322..672bc50109 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java @@ -33,6 +33,7 @@ import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; +import java.util.ArrayList; import java.util.List; @Service @@ -63,24 +64,25 @@ public class DefaultEntityExportService relations = null; if (exportSettings.isExportInboundRelations()) { List inboundRelations = relationService.findByTo(user.getTenantId(), entity.getId(), RelationTypeGroup.COMMON); - if (inboundRelations != null) { - for (EntityRelation relation : inboundRelations) { - exportableEntitiesService.checkPermission(user, relation.getFrom(), Operation.READ); - } + for (EntityRelation relation : inboundRelations) { + exportableEntitiesService.checkPermission(user, relation.getFrom(), Operation.READ); } - exportData.setInboundRelations(inboundRelations); + relations = new ArrayList<>(inboundRelations); } if (exportSettings.isExportOutboundRelations()) { List outboundRelations = relationService.findByFrom(user.getTenantId(), entity.getId(), RelationTypeGroup.COMMON); - if (outboundRelations != null) { - for (EntityRelation relation : outboundRelations) { - exportableEntitiesService.checkPermission(user, relation.getTo(), Operation.READ); - } + for (EntityRelation relation : outboundRelations) { + exportableEntitiesService.checkPermission(user, relation.getTo(), Operation.READ); } - exportData.setOutboundRelations(outboundRelations); + if (relations == null) { + relations = new ArrayList<>(); + } + relations.addAll(outboundRelations); } + exportData.setRelations(relations); } protected D newExportData() { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java index d4b0c46f31..a5de7d8744 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java @@ -26,8 +26,5 @@ import lombok.NoArgsConstructor; @Builder public class EntityImportSettings { private boolean findExistingByName; - - private boolean importInboundRelations; - private boolean importOutboundRelations; - private boolean removeExistingRelations; + private boolean updateRelations; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java index 5802e140f3..1edd6a1ec9 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java @@ -16,7 +16,6 @@ package org.thingsboard.server.service.sync.importing.impl; import lombok.RequiredArgsConstructor; -import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.transaction.annotation.Transactional; @@ -40,10 +39,9 @@ import org.thingsboard.server.service.sync.importing.EntityImportService; import org.thingsboard.server.service.sync.importing.data.EntityImportResult; import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; public abstract class BaseEntityImportService, D extends EntityExportData> implements EntityImportService { @@ -102,47 +100,53 @@ public abstract class BaseEntityImportService { - List newRelations = new LinkedList<>(); - - if (importSettings.isImportInboundRelations() && CollectionUtils.isNotEmpty(exportData.getInboundRelations())) { - newRelations.addAll(exportData.getInboundRelations().stream() - .peek(relation -> relation.setTo(savedEntity.getId())) - .collect(Collectors.toList())); - - if (importSettings.isRemoveExistingRelations() && oldEntity != null) { - for (EntityRelation existingRelation : relationService.findByTo(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)) { - exportableEntitiesService.checkPermission(user, existingRelation.getFrom(), Operation.WRITE); - deleteRelation(user, existingRelation, importResult); - } - } - } - if (importSettings.isImportOutboundRelations() && CollectionUtils.isNotEmpty(exportData.getOutboundRelations())) { - newRelations.addAll(exportData.getOutboundRelations().stream() - .peek(relation -> relation.setFrom(savedEntity.getId())) - .collect(Collectors.toList())); - - if (importSettings.isRemoveExistingRelations() && oldEntity != null) { - for (EntityRelation existingRelation : relationService.findByFrom(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)) { - exportableEntitiesService.checkPermission(user, existingRelation.getTo(), Operation.WRITE); - deleteRelation(user, existingRelation, importResult); - } - } + List relations = exportData.getRelations(); + if (relations == null || !importSettings.isUpdateRelations()) { + return; } + relations = new ArrayList<>(relations); - for (EntityRelation relation : newRelations) { - HasId otherEntity = null; + for (EntityRelation relation : relations) { if (!relation.getTo().equals(savedEntity.getId())) { - otherEntity = findInternalEntity(user.getTenantId(), relation.getTo()); - relation.setTo(otherEntity.getId()); + HasId to = findInternalEntity(user.getTenantId(), relation.getTo()); + exportableEntitiesService.checkPermission(user, to, to.getId().getEntityType(), Operation.WRITE); + relation.setTo(to.getId()); } - if (!relation.getFrom().equals(savedEntity.getId())) { - otherEntity = findInternalEntity(user.getTenantId(), relation.getFrom()); - relation.setFrom(otherEntity.getId()); + if (!relation.getFrom().equals(savedEntity.getId())){ + HasId from = findInternalEntity(user.getTenantId(), relation.getFrom()); + exportableEntitiesService.checkPermission(user, from, from.getId().getEntityType(), Operation.WRITE); + relation.setFrom(from.getId()); } - if (otherEntity != null) { - exportableEntitiesService.checkPermission(user, otherEntity, otherEntity.getId().getEntityType(), Operation.WRITE); + } + + if (oldEntity != null) { + List existingRelations = new ArrayList<>(); + existingRelations.addAll(relationService.findByTo(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)); + existingRelations.addAll(relationService.findByFrom(user.getTenantId(), savedEntity.getId(), RelationTypeGroup.COMMON)); + + for (EntityRelation existingRelation : existingRelations) { + if (!relations.contains(existingRelation)) { + EntityId otherEntity = null; + if (!existingRelation.getTo().equals(savedEntity.getId())) { + otherEntity = existingRelation.getTo(); + } else if (!existingRelation.getFrom().equals(savedEntity.getId())){ + otherEntity = existingRelation.getFrom(); + } + if (otherEntity != null) { + exportableEntitiesService.checkPermission(user, otherEntity, Operation.WRITE); + } + relationService.deleteRelation(user.getTenantId(), existingRelation); + importResult.addSendEventsCallback(() -> { + entityActionService.logEntityAction(user, existingRelation.getFrom(), null, null, + ActionType.RELATION_DELETED, null, existingRelation); + entityActionService.logEntityAction(user, existingRelation.getTo(), null, null, + ActionType.RELATION_DELETED, null, existingRelation); + }); + } } + } + for (EntityRelation relation : relations) { relationService.saveRelation(user.getTenantId(), relation); importResult.addSendEventsCallback(() -> { entityActionService.logEntityAction(user, relation.getFrom(), null, null, @@ -154,17 +158,6 @@ public abstract class BaseEntityImportService importResult) { - relationService.deleteRelation(user.getTenantId(), relation); - importResult.addSendEventsCallback(() -> { - entityActionService.logEntityAction(user, relation.getFrom(), null, null, - ActionType.RELATION_DELETED, null, relation); - entityActionService.logEntityAction(user, relation.getTo(), null, null, - ActionType.RELATION_DELETED, null, relation); - }); - } - - protected void onEntitySaved(SecurityUser user, E savedEntity, E oldEntity) throws ThingsboardException { entityActionService.logEntityAction(user, savedEntity.getId(), savedEntity, savedEntity instanceof HasCustomerId ? ((HasCustomerId) savedEntity).getCustomerId() : user.getCustomerId(), diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java index 8a0c958b2c..8d1857f133 100644 --- a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java @@ -311,7 +311,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp Dashboard importedDashboard = (Dashboard) importResults.get(EntityType.DASHBOARD).get(0).getSavedEntity(); Set entityAliasEntitiesIds = Streams.stream(importedDashboard.getConfiguration() - .get("entityAliases").elements().next().get("filter").get("entityList").elements()) + .get("entityAliases").elements().next().get("filter").get("entityList").elements()) .map(JsonNode::asText).collect(Collectors.toSet()); assertThat(entityAliasEntitiesIds).doesNotContain(asset1.getId().toString(), asset2.getId().toString()); assertThat(entityAliasEntitiesIds).contains(importedAsset1.getId().toString(), importedAsset2.getId().toString()); @@ -427,8 +427,8 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp List> exportDataList = exportEntities(exportRequest); EntityExportData deviceExportData = exportDataList.stream().filter(exportData -> exportData.getEntityType() == EntityType.DEVICE).findFirst().orElse(null); - assertThat(deviceExportData.getInboundRelations()).size().isOne(); - assertThat(deviceExportData.getInboundRelations().get(0)).matches(entityRelation -> { + assertThat(deviceExportData.getRelations()).size().isOne(); + assertThat(deviceExportData.getRelations().get(0)).matches(entityRelation -> { return entityRelation.getFrom().equals(asset.getId()) && entityRelation.getTo().equals(device.getId()); }); ((DeviceExportData) deviceExportData).getCredentials().setCredentialsId("ab"); @@ -439,7 +439,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp ImportRequest importRequest = new ImportRequest(); importRequest.setExportDataList(exportDataList); importRequest.setImportSettings(EntityImportSettings.builder() - .importInboundRelations(true) + .updateRelations(true) .build()); Map> importResults = importEntities(importRequest).stream().collect(Collectors.toMap(EntityImportResult::getEntityType, r -> r)); @@ -473,7 +473,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp .build()); List> exportDataList = exportEntities(exportRequest); - assertThat(exportDataList).allMatch(exportData -> exportData.getInboundRelations().size() + exportData.getOutboundRelations().size() == 1); + assertThat(exportDataList).allMatch(exportData -> exportData.getRelations().size() == 1); EntityExportData deviceExportData = exportDataList.stream().filter(exportData -> exportData.getEntityType() == EntityType.DEVICE).findFirst().orElse(null); ((DeviceExportData) deviceExportData).getCredentials().setCredentialsId("ab"); @@ -484,8 +484,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp ImportRequest importRequest = new ImportRequest(); importRequest.setExportDataList(exportDataList); importRequest.setImportSettings(EntityImportSettings.builder() - .importInboundRelations(true) - .importOutboundRelations(true) + .updateRelations(true) .build()); Map> importResults = importEntities(importRequest).stream().collect(Collectors.toMap(EntityImportResult::getEntityType, r -> r)); @@ -515,7 +514,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp .exportOutboundRelations(true) .build()); EntityExportData assetExportData = (EntityExportData) exportEntities(exportRequest).get(0); - assertThat(assetExportData.getOutboundRelations()).size().isOne(); + assertThat(assetExportData.getRelations()).size().isOne(); Device device2 = createDevice(tenantId1, null, null, "Device 2"); EntityRelation relation2 = createRelation(asset.getId(), device2.getId()); @@ -523,13 +522,14 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp ImportRequest importRequest = new ImportRequest(); importRequest.setExportDataList(List.of(assetExportData)); importRequest.setImportSettings(EntityImportSettings.builder() - .importOutboundRelations(true) + .updateRelations(true) .build()); importEntities(importRequest); List relations = relationService.findByFrom(TenantId.SYS_TENANT_ID, asset.getId(), RelationTypeGroup.COMMON); - assertThat(relations).contains(relation1, relation2); + assertThat(relations).contains(relation1); + assertThat(relations).doesNotContain(relation2); } @Test @@ -546,7 +546,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp .exportInboundRelations(true) .build()); EntityExportData deviceExportData = exportEntities(exportRequest).get(0); - assertThat(deviceExportData.getInboundRelations()).size().isOne(); + assertThat(deviceExportData.getRelations()).size().isOne(); Asset asset2 = createAsset(tenantId1, null, "A", "Asset 2"); EntityRelation relation2 = createRelation(asset2.getId(), device.getId()); @@ -554,8 +554,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp ImportRequest importRequest = new ImportRequest(); importRequest.setExportDataList(List.of(deviceExportData)); importRequest.setImportSettings(EntityImportSettings.builder() - .importInboundRelations(true) - .removeExistingRelations(true) + .updateRelations(true) .build()); importEntities(importRequest); @@ -730,7 +729,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp verify(clusterService).sendNotificationMsgToEdgeService(any(), any(), eq(importedDeviceProfile.getId()), any(), any(), eq(EdgeEventActionType.ADDED)); verify(otaPackageStateService).update(eq(importedDeviceProfile), eq(false), eq(false)); - ((DeviceExportData)entitiesExportData.get(EntityType.DEVICE)).getCredentials().setCredentialsId("abc"); + ((DeviceExportData) entitiesExportData.get(EntityType.DEVICE)).getCredentials().setCredentialsId("abc"); Device importedDevice = (Device) importEntity(entitiesExportData.get(EntityType.DEVICE)).getSavedEntity(); verify(entityActionService).logEntityAction(any(), eq(importedDevice.getId()), eq(importedDevice), any(), eq(ActionType.ADDED), isNull()); From 457d2340a905ec5d98346ac56618cdf5d9f46044 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 21 Apr 2022 16:23:54 +0300 Subject: [PATCH 26/39] Refactor relations export --- .../data/request/EntityExportSettings.java | 3 +-- .../impl/DefaultEntityExportService.java | 16 +++++++--------- .../EntitiesExportImportControllerSqlTest.java | 10 ++++------ 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityExportSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityExportSettings.java index ddf2e43bcf..5454105ee5 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityExportSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityExportSettings.java @@ -25,6 +25,5 @@ import lombok.NoArgsConstructor; @NoArgsConstructor @Builder public class EntityExportSettings { - private boolean exportInboundRelations; - private boolean exportOutboundRelations; + private boolean exportRelations; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java index 672bc50109..a051f0e59f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java @@ -64,25 +64,23 @@ public class DefaultEntityExportService relations = null; - if (exportSettings.isExportInboundRelations()) { + if (exportSettings.isExportRelations()) { + List relations = new ArrayList<>(); + List inboundRelations = relationService.findByTo(user.getTenantId(), entity.getId(), RelationTypeGroup.COMMON); for (EntityRelation relation : inboundRelations) { exportableEntitiesService.checkPermission(user, relation.getFrom(), Operation.READ); } - relations = new ArrayList<>(inboundRelations); - } - if (exportSettings.isExportOutboundRelations()) { + relations.addAll(inboundRelations); + List outboundRelations = relationService.findByFrom(user.getTenantId(), entity.getId(), RelationTypeGroup.COMMON); for (EntityRelation relation : outboundRelations) { exportableEntitiesService.checkPermission(user, relation.getTo(), Operation.READ); } - if (relations == null) { - relations = new ArrayList<>(); - } relations.addAll(outboundRelations); + + exportData.setRelations(relations); } - exportData.setRelations(relations); } protected D newExportData() { diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java index 8d1857f133..44d8f5f461 100644 --- a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java @@ -421,8 +421,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp EntityListExportRequest exportRequest = new EntityListExportRequest(); exportRequest.setEntitiesIds(List.of(asset.getId(), device.getId())); exportRequest.setExportSettings(EntityExportSettings.builder() - .exportInboundRelations(true) - .exportOutboundRelations(false) + .exportRelations(true) .build()); List> exportDataList = exportEntities(exportRequest); @@ -468,8 +467,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp EntityListExportRequest exportRequest = new EntityListExportRequest(); exportRequest.setEntitiesIds(List.of(asset.getId(), device.getId())); exportRequest.setExportSettings(EntityExportSettings.builder() - .exportInboundRelations(true) - .exportOutboundRelations(true) + .exportRelations(true) .build()); List> exportDataList = exportEntities(exportRequest); @@ -511,7 +509,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp SingleEntityExportRequest exportRequest = new SingleEntityExportRequest(); exportRequest.setEntityId(asset.getId()); exportRequest.setExportSettings(EntityExportSettings.builder() - .exportOutboundRelations(true) + .exportRelations(true) .build()); EntityExportData assetExportData = (EntityExportData) exportEntities(exportRequest).get(0); assertThat(assetExportData.getRelations()).size().isOne(); @@ -543,7 +541,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp SingleEntityExportRequest exportRequest = new SingleEntityExportRequest(); exportRequest.setEntityId(device.getId()); exportRequest.setExportSettings(EntityExportSettings.builder() - .exportInboundRelations(true) + .exportRelations(true) .build()); EntityExportData deviceExportData = exportEntities(exportRequest).get(0); assertThat(deviceExportData.getRelations()).size().isOne(); From 428ff2851dd3fbf189b58b64baea369b13b8297f Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 21 Apr 2022 18:25:56 +0300 Subject: [PATCH 27/39] Minor refactoring --- .../sync/DefaultEntitiesExportImportService.java | 2 +- .../sync/exporting/impl/DeviceExportService.java | 2 +- .../sync/importing/impl/BaseEntityImportService.java | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java index 1db59d68f1..44a9396fab 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java @@ -153,7 +153,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS public void checkPermission(SecurityUser user, HasId entity, EntityType entityType, Operation operation) throws ThingsboardException { if (entity instanceof HasTenantId) { accessControlService.checkPermission(user, Resource.of(entityType), operation, entity.getId(), (HasTenantId) entity); - } else if (entity != null) { + } else { accessControlService.checkPermission(user, Resource.of(entityType), operation); } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceExportService.java index da1e4f3a96..21df8beda2 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceExportService.java @@ -36,7 +36,7 @@ public class DeviceExportService extends BaseEntityExportService { - List relations = exportData.getRelations(); - if (relations == null || !importSettings.isUpdateRelations()) { + if (!importSettings.isUpdateRelations() || exportData.getRelations() == null) { return; } - relations = new ArrayList<>(relations); + + List relations = new ArrayList<>(exportData.getRelations()); for (EntityRelation relation : relations) { if (!relation.getTo().equals(savedEntity.getId())) { @@ -112,7 +112,7 @@ public abstract class BaseEntityImportService from = findInternalEntity(user.getTenantId(), relation.getFrom()); exportableEntitiesService.checkPermission(user, from, from.getId().getEntityType(), Operation.WRITE); relation.setFrom(from.getId()); @@ -129,7 +129,7 @@ public abstract class BaseEntityImportService Date: Thu, 21 Apr 2022 19:13:58 +0300 Subject: [PATCH 28/39] Refactor export/import api --- .../EntitiesExportImportController.java | 104 ++------- .../DefaultEntitiesExportImportService.java | 88 +------- .../DefaultExportableEntitiesService.java | 206 ++++++++++++++++++ .../exporting/ExportableEntitiesService.java | 5 + .../sync/exporting/data/DeviceExportData.java | 2 + .../exporting/data/RuleChainExportData.java | 2 + 6 files changed, 239 insertions(+), 168 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index 4a1badd38e..3dff560ac1 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -17,47 +17,25 @@ package org.thingsboard.server.controller; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.dao.DataAccessException; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.exception.ThingsboardException; -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.query.EntityData; -import org.thingsboard.server.common.data.query.EntityDataPageLink; -import org.thingsboard.server.common.data.query.EntityDataQuery; -import org.thingsboard.server.common.data.query.EntityDataSortOrder; -import org.thingsboard.server.common.data.query.EntityFilter; -import org.thingsboard.server.common.data.query.EntityKey; -import org.thingsboard.server.common.data.query.EntityKeyType; -import org.thingsboard.server.common.data.query.EntityTypeFilter; -import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.EntitiesExportImportService; +import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.exporting.data.request.CustomEntityFilterExportRequest; -import org.thingsboard.server.service.sync.exporting.data.request.CustomEntityQueryExportRequest; -import org.thingsboard.server.service.sync.exporting.data.request.EntityListExportRequest; -import org.thingsboard.server.service.sync.exporting.data.request.EntityTypeExportRequest; import org.thingsboard.server.service.sync.exporting.data.request.ExportRequest; -import org.thingsboard.server.service.sync.exporting.data.request.SingleEntityExportRequest; import org.thingsboard.server.service.sync.importing.data.EntityImportResult; import org.thingsboard.server.service.sync.importing.data.request.ImportRequest; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; -import java.util.Optional; -import java.util.stream.Collectors; - -import static org.thingsboard.server.dao.sql.query.EntityKeyMapping.CREATED_TIME; @RestController @RequestMapping("/api/entities") @@ -67,7 +45,8 @@ import static org.thingsboard.server.dao.sql.query.EntityKeyMapping.CREATED_TIME public class EntitiesExportImportController extends BaseController { private final EntitiesExportImportService exportImportService; - private final EntityService entityService; + private final ExportableEntitiesService exportableEntitiesService; + @PostMapping("/export") @PreAuthorize("hasAuthority('TENANT_ADMIN')") @@ -76,6 +55,7 @@ public class EntitiesExportImportController extends BaseController { try { return exportEntitiesByRequest(user, exportRequest); } catch (Exception e) { + log.warn("Failed to export entities for request {}", exportRequest, e); throw handleException(e); } } @@ -85,84 +65,29 @@ public class EntitiesExportImportController extends BaseController { public List> exportEntities(@RequestBody List exportRequests) throws ThingsboardException { SecurityUser user = getCurrentUser(); try { - List> exportDataList = new ArrayList<>(); + List> result = new ArrayList<>(); for (ExportRequest exportRequest : exportRequests) { - exportDataList.addAll(exportEntitiesByRequest(user, exportRequest)); + List> exportDataList = exportEntitiesByRequest(user, exportRequest); + result.addAll(exportDataList); } - return exportDataList; + return result; } catch (Exception e) { + log.warn("Failed to export entities for requests {}", exportRequests, e); throw handleException(e); } } - - private List> exportEntitiesByRequest(SecurityUser user, ExportRequest request) throws ThingsboardException { - List entitiesIds = findEntitiesForRequest(user, request); + private List> exportEntitiesByRequest(SecurityUser user, ExportRequest exportRequest) throws ThingsboardException { + List entities = exportableEntitiesService.findEntitiesForRequest(user.getTenantId(), exportRequest); List> exportDataList = new ArrayList<>(); - for (EntityId entityId : entitiesIds) { - exportDataList.add(exportImportService.exportEntity(user, entityId, request.getExportSettings())); + for (EntityId entityId : entities) { + EntityExportData exportData = exportImportService.exportEntity(user, entityId, exportRequest.getExportSettings()); + exportDataList.add(exportData); } return exportDataList; } - private List findEntitiesForRequest(SecurityUser user, ExportRequest request) { - switch (request.getType()) { - case SINGLE_ENTITY: { - return List.of(((SingleEntityExportRequest) request).getEntityId()); - } - case ENTITY_LIST: { - return ((EntityListExportRequest) request).getEntitiesIds(); - } - case ENTITY_TYPE: { - EntityTypeExportRequest exportRequest = (EntityTypeExportRequest) request; - EntityTypeFilter entityTypeFilter = new EntityTypeFilter(); - entityTypeFilter.setEntityType(exportRequest.getEntityType()); - - CustomerId customerId = Optional.ofNullable(exportRequest.getCustomerId()).orElse(emptyId(EntityType.CUSTOMER)); - return findEntitiesByFilter(user.getTenantId(), customerId, entityTypeFilter, exportRequest.getPage(), exportRequest.getPageSize()); - } - case CUSTOM_ENTITY_FILTER: { - CustomEntityFilterExportRequest exportRequest = (CustomEntityFilterExportRequest) request; - EntityFilter filter = exportRequest.getFilter(); - - CustomerId customerId = Optional.ofNullable(exportRequest.getCustomerId()).orElse(emptyId(EntityType.CUSTOMER)); - return findEntitiesByFilter(user.getTenantId(), customerId, filter, exportRequest.getPage(), exportRequest.getPageSize()); - } - case CUSTOM_ENTITY_QUERY: { - CustomEntityQueryExportRequest exportRequest = (CustomEntityQueryExportRequest) request; - EntityDataQuery query = exportRequest.getQuery(); - - CustomerId customerId = Optional.ofNullable(exportRequest.getCustomerId()).orElse(emptyId(EntityType.CUSTOMER)); - return findEntitiesByQuery(user.getTenantId(), customerId, query); - } - default: - throw new IllegalArgumentException("Export request is not supported"); - } - } - - private List findEntitiesByFilter(TenantId tenantId, CustomerId customerId, EntityFilter filter, int page, int pageSize) { - EntityDataPageLink pageLink = new EntityDataPageLink(); - pageLink.setPage(page); - pageLink.setPageSize(pageSize); - EntityKey sortProperty = new EntityKey(EntityKeyType.ENTITY_FIELD, CREATED_TIME); - pageLink.setSortOrder(new EntityDataSortOrder(sortProperty, EntityDataSortOrder.Direction.DESC)); - - EntityDataQuery query = new EntityDataQuery(filter, pageLink, List.of(sortProperty), Collections.emptyList(), Collections.emptyList()); - return findEntitiesByQuery(tenantId, customerId, query); - } - - private List findEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) { - try { - return entityService.findEntityDataByQuery(tenantId, customerId, query).getData().stream() - .map(EntityData::getEntityId) - .collect(Collectors.toList()); - } catch (DataAccessException e) { - log.error("Failed to find entity data by query: {}", e.getMessage()); - throw new IllegalArgumentException("Entity filter cannot be processed"); - } - } - @PostMapping("/import") public List> importEntities(@RequestBody ImportRequest importRequest) throws ThingsboardException { @@ -183,6 +108,7 @@ public class EntitiesExportImportController extends BaseController { return importResults; } catch (Exception e) { + log.warn("Failed to import entities for request {}", importRequest, e); throw handleException(e); } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java index 44a9396fab..75c4ff9e12 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java @@ -16,26 +16,18 @@ package org.thingsboard.server.service.sync; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; -import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.HasId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.Dao; -import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.security.permission.AccessControlService; -import org.thingsboard.server.service.security.permission.Operation; -import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.sync.exporting.EntityExportService; -import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; import org.thingsboard.server.service.sync.exporting.impl.BaseEntityExportService; @@ -57,13 +49,11 @@ import java.util.stream.Collectors; @Service @TbCoreComponent @RequiredArgsConstructor -public class DefaultEntitiesExportImportService implements EntitiesExportImportService, ExportableEntitiesService { +@Slf4j +public class DefaultEntitiesExportImportService implements EntitiesExportImportService { private final Map> exportServices = new HashMap<>(); private final Map> importServices = new HashMap<>(); - private final Map> daos = new HashMap<>(); - - private final AccessControlService accessControlService; protected static final List SUPPORTED_ENTITY_TYPES = List.of( EntityType.CUSTOMER, EntityType.ASSET, EntityType.RULE_CHAIN, @@ -117,75 +107,24 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS } - @Override - public , I extends EntityId> E findEntityByTenantIdAndExternalId(TenantId tenantId, I externalId) { - EntityType entityType = externalId.getEntityType(); - if (SUPPORTED_ENTITY_TYPES.contains(entityType)) { - ExportableEntityDao dao = (ExportableEntityDao) getDao(entityType); - return dao.findByTenantIdAndExternalId(tenantId.getId(), externalId.getId()); - } else { - return null; - } - } - - @Override - public , I extends EntityId> E findEntityByTenantIdAndId(TenantId tenantId, I id) { - Dao dao = (Dao) getDao(id.getEntityType()); - E entity = dao.findById(tenantId, id.getId()); - if (entity instanceof HasTenantId && !((HasTenantId) entity).getTenantId().equals(tenantId)) { - return null; - } - return entity; - } - - @Override - public , I extends EntityId> E findEntityByTenantIdAndName(TenantId tenantId, EntityType entityType, String name) { - ExportableEntityDao dao = (ExportableEntityDao) getDao(entityType); - try { - return dao.findByTenantIdAndName(tenantId.getId(), name); - } catch (UnsupportedOperationException e) { - return null; - } - } - - - @Override - public void checkPermission(SecurityUser user, HasId entity, EntityType entityType, Operation operation) throws ThingsboardException { - if (entity instanceof HasTenantId) { - accessControlService.checkPermission(user, Resource.of(entityType), operation, entity.getId(), (HasTenantId) entity); - } else { - accessControlService.checkPermission(user, Resource.of(entityType), operation); - } - } - - @Override - public void checkPermission(SecurityUser user, EntityId entityId, Operation operation) throws ThingsboardException { - HasId entity = findEntityByTenantIdAndId(user.getTenantId(), entityId); - checkPermission(user, entity, entityId.getEntityType(), operation); - } - - @SuppressWarnings("unchecked") private , D extends EntityExportData> EntityExportService getExportService(EntityType entityType) { - if (!SUPPORTED_ENTITY_TYPES.contains(entityType)) { + EntityExportService exportService = exportServices.get(entityType); + if (exportService == null) { throw new IllegalArgumentException("Export for entity type " + entityType + " is not supported"); } - return (EntityExportService) exportServices.get(entityType); + return (EntityExportService) exportService; } @SuppressWarnings("unchecked") private , D extends EntityExportData> EntityImportService getImportService(EntityType entityType) { - if (!SUPPORTED_ENTITY_TYPES.contains(entityType)) { + EntityImportService importService = importServices.get(entityType); + if (importService == null) { throw new IllegalArgumentException("Import for entity type " + entityType + " is not supported"); } - return (EntityImportService) importServices.get(entityType); + return (EntityImportService) importService; } - private Dao getDao(EntityType entityType) { - return daos.get(entityType); - } - - @Autowired private void setExportServices(DefaultEntityExportService defaultExportService, Collection> exportServices) { @@ -208,13 +147,4 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS }); } - @Autowired - private void setDaos(Collection> daos) { - daos.forEach(dao -> { - if (dao.getEntityType() != null) { - this.daos.put(dao.getEntityType(), dao); - } - }); - } - } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java new file mode 100644 index 0000000000..c2c99c63f5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java @@ -0,0 +1,206 @@ +/** + * 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.sync.exporting; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.query.EntityData; +import org.thingsboard.server.common.data.query.EntityDataPageLink; +import org.thingsboard.server.common.data.query.EntityDataQuery; +import org.thingsboard.server.common.data.query.EntityDataSortOrder; +import org.thingsboard.server.common.data.query.EntityFilter; +import org.thingsboard.server.common.data.query.EntityKey; +import org.thingsboard.server.common.data.query.EntityKeyType; +import org.thingsboard.server.common.data.query.EntityTypeFilter; +import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.ExportableEntityDao; +import org.thingsboard.server.dao.entity.EntityService; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.security.permission.AccessControlService; +import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.security.permission.Resource; +import org.thingsboard.server.service.sync.exporting.data.request.CustomEntityFilterExportRequest; +import org.thingsboard.server.service.sync.exporting.data.request.CustomEntityQueryExportRequest; +import org.thingsboard.server.service.sync.exporting.data.request.EntityListExportRequest; +import org.thingsboard.server.service.sync.exporting.data.request.EntityTypeExportRequest; +import org.thingsboard.server.service.sync.exporting.data.request.ExportRequest; +import org.thingsboard.server.service.sync.exporting.data.request.SingleEntityExportRequest; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.thingsboard.server.dao.sql.query.EntityKeyMapping.CREATED_TIME; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +@Slf4j +public class DefaultExportableEntitiesService implements ExportableEntitiesService { + + private final Map> daos = new HashMap<>(); + + private final EntityService entityService; + private final AccessControlService accessControlService; + + + @Override + public , I extends EntityId> E findEntityByTenantIdAndExternalId(TenantId tenantId, I externalId) { + EntityType entityType = externalId.getEntityType(); + Dao dao = getDao(entityType); + + if (dao instanceof ExportableEntityDao) { + ExportableEntityDao exportableEntityDao = (ExportableEntityDao) dao; + return exportableEntityDao.findByTenantIdAndExternalId(tenantId.getId(), externalId.getId()); + } else { + return null; + } + } + + @Override + public , I extends EntityId> E findEntityByTenantIdAndId(TenantId tenantId, I id) { + EntityType entityType = id.getEntityType(); + Dao dao = getDao(entityType); + + E entity = dao.findById(tenantId, id.getId()); + if (((HasTenantId) entity).getTenantId().equals(tenantId)) { + return entity; + } + return null; + } + + @Override + public , I extends EntityId> E findEntityByTenantIdAndName(TenantId tenantId, EntityType entityType, String name) { + Dao dao = getDao(entityType); + + if (dao instanceof ExportableEntityDao) { + ExportableEntityDao exportableEntityDao = (ExportableEntityDao) dao; + try { + return exportableEntityDao.findByTenantIdAndName(tenantId.getId(), name); + } catch (UnsupportedOperationException ignored) { + } + } + return null; + } + + + @Override + public List findEntitiesForRequest(TenantId tenantId, ExportRequest request) { + switch (request.getType()) { + case SINGLE_ENTITY: { + return List.of(((SingleEntityExportRequest) request).getEntityId()); + } + case ENTITY_LIST: { + return ((EntityListExportRequest) request).getEntitiesIds(); + } + case ENTITY_TYPE: { + EntityTypeExportRequest exportRequest = (EntityTypeExportRequest) request; + EntityTypeFilter entityTypeFilter = new EntityTypeFilter(); + entityTypeFilter.setEntityType(exportRequest.getEntityType()); + + CustomerId customerId = Optional.ofNullable(exportRequest.getCustomerId()).orElse(new CustomerId(EntityId.NULL_UUID)); + return findEntitiesByFilter(tenantId, customerId, entityTypeFilter, exportRequest.getPage(), exportRequest.getPageSize()); + } + case CUSTOM_ENTITY_FILTER: { + CustomEntityFilterExportRequest exportRequest = (CustomEntityFilterExportRequest) request; + EntityFilter filter = exportRequest.getFilter(); + + CustomerId customerId = Optional.ofNullable(exportRequest.getCustomerId()).orElse(new CustomerId(EntityId.NULL_UUID)); + return findEntitiesByFilter(tenantId, customerId, filter, exportRequest.getPage(), exportRequest.getPageSize()); + } + case CUSTOM_ENTITY_QUERY: { + CustomEntityQueryExportRequest exportRequest = (CustomEntityQueryExportRequest) request; + EntityDataQuery query = exportRequest.getQuery(); + + CustomerId customerId = Optional.ofNullable(exportRequest.getCustomerId()).orElse(new CustomerId(EntityId.NULL_UUID)); + return findEntitiesByQuery(tenantId, customerId, query); + } + default: { + throw new IllegalArgumentException("Export request is not supported"); + } + } + } + + private List findEntitiesByFilter(TenantId tenantId, CustomerId customerId, EntityFilter filter, int page, int pageSize) { + EntityDataPageLink pageLink = new EntityDataPageLink(); + pageLink.setPage(page); + pageLink.setPageSize(pageSize); + EntityKey sortProperty = new EntityKey(EntityKeyType.ENTITY_FIELD, CREATED_TIME); + pageLink.setSortOrder(new EntityDataSortOrder(sortProperty, EntityDataSortOrder.Direction.DESC)); + + EntityDataQuery query = new EntityDataQuery(filter, pageLink, List.of(sortProperty), Collections.emptyList(), Collections.emptyList()); + return findEntitiesByQuery(tenantId, customerId, query); + } + + private List findEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) { + try { + return entityService.findEntityDataByQuery(tenantId, customerId, query).getData().stream() + .map(EntityData::getEntityId) + .collect(Collectors.toList()); + } catch (DataAccessException e) { + log.error("Failed to find entity data by query: {}", e.getMessage()); + throw new IllegalArgumentException("Entity filter cannot be processed"); + } + } + + + @Override + public void checkPermission(SecurityUser user, HasId entity, EntityType entityType, Operation operation) throws ThingsboardException { + if (entity instanceof HasTenantId) { + accessControlService.checkPermission(user, Resource.of(entityType), operation, entity.getId(), (HasTenantId) entity); + } else { + accessControlService.checkPermission(user, Resource.of(entityType), operation); + } + } + + @Override + public void checkPermission(SecurityUser user, EntityId entityId, Operation operation) throws ThingsboardException { + HasId entity = findEntityByTenantIdAndId(user.getTenantId(), entityId); + checkPermission(user, entity, entityId.getEntityType(), operation); + } + + + @SuppressWarnings("unchecked") + private Dao getDao(EntityType entityType) { + return (Dao) daos.get(entityType); + } + + @Autowired + private void setDaos(Collection> daos) { + daos.forEach(dao -> { + if (dao.getEntityType() != null) { + this.daos.put(dao.getEntityType(), dao); + } + }); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java index 5cf31042e0..9021a03e85 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java @@ -23,6 +23,9 @@ import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.sync.exporting.data.request.ExportRequest; + +import java.util.List; public interface ExportableEntitiesService { @@ -32,6 +35,8 @@ public interface ExportableEntitiesService { , I extends EntityId> E findEntityByTenantIdAndName(TenantId tenantId, EntityType entityType, String name); + List findEntitiesForRequest(TenantId tenantId, ExportRequest request); + void checkPermission(SecurityUser user, HasId entity, EntityType entityType, Operation operation) throws ThingsboardException; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceExportData.java index 30d84c0a44..56633f9c91 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceExportData.java @@ -17,10 +17,12 @@ package org.thingsboard.server.service.sync.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.ToString; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.security.DeviceCredentials; @EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) @Data public class DeviceExportData extends EntityExportData { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/RuleChainExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/RuleChainExportData.java index 025459f27e..2e05e2f1f5 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/RuleChainExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/RuleChainExportData.java @@ -17,10 +17,12 @@ package org.thingsboard.server.service.sync.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.ToString; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; @EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) @Data public class RuleChainExportData extends EntityExportData { From 7e2910ed9549bf5fe6973530293fc46bd751c2d2 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Mon, 25 Apr 2022 14:28:54 +0300 Subject: [PATCH 29/39] Minor improvements --- .../EntitiesExportImportController.java | 20 +++++++++- .../DefaultExportableEntitiesService.java | 39 +++++++++++++------ .../CustomEntityFilterExportRequest.java | 5 ++- .../CustomEntityQueryExportRequest.java | 5 ++- .../data/request/EntityTypeExportRequest.java | 5 ++- 5 files changed, 55 insertions(+), 19 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index 3dff560ac1..afe2a4ac1f 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -29,8 +29,10 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.EntitiesExportImportService; import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; import org.thingsboard.server.service.sync.exporting.data.request.ExportRequest; import org.thingsboard.server.service.sync.importing.data.EntityImportResult; +import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; import org.thingsboard.server.service.sync.importing.data.request.ImportRequest; import java.util.ArrayList; @@ -79,10 +81,16 @@ public class EntitiesExportImportController extends BaseController { private List> exportEntitiesByRequest(SecurityUser user, ExportRequest exportRequest) throws ThingsboardException { List entities = exportableEntitiesService.findEntitiesForRequest(user.getTenantId(), exportRequest); + EntityExportSettings exportSettings = exportRequest.getExportSettings(); + if (exportSettings == null) { + exportSettings = EntityExportSettings.builder() + .exportRelations(false) + .build(); + } List> exportDataList = new ArrayList<>(); for (EntityId entityId : entities) { - EntityExportData exportData = exportImportService.exportEntity(user, entityId, exportRequest.getExportSettings()); + EntityExportData exportData = exportImportService.exportEntity(user, entityId, exportSettings); exportDataList.add(exportData); } return exportDataList; @@ -93,7 +101,15 @@ public class EntitiesExportImportController extends BaseController { public List> importEntities(@RequestBody ImportRequest importRequest) throws ThingsboardException { SecurityUser user = getCurrentUser(); try { - List> importResults = exportImportService.importEntities(user, importRequest.getExportDataList(), importRequest.getImportSettings()); + EntityImportSettings importSettings = importRequest.getImportSettings(); + if (importSettings == null) { + importSettings = EntityImportSettings.builder() + .findExistingByName(false) + .updateRelations(false) + .build(); + } + + List> importResults = exportImportService.importEntities(user, importRequest.getExportDataList(), importSettings); importResults.stream() .map(EntityImportResult::getSendEventsCallback) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java index c2c99c63f5..a616dec6c4 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java @@ -17,6 +17,7 @@ package org.thingsboard.server.service.sync.exporting; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Service; @@ -56,7 +57,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.stream.Collectors; import static org.thingsboard.server.dao.sql.query.EntityKeyMapping.CREATED_TIME; @@ -78,12 +78,17 @@ public class DefaultExportableEntitiesService implements ExportableEntitiesServi EntityType entityType = externalId.getEntityType(); Dao dao = getDao(entityType); + E entity = null; + if (dao instanceof ExportableEntityDao) { ExportableEntityDao exportableEntityDao = (ExportableEntityDao) dao; - return exportableEntityDao.findByTenantIdAndExternalId(tenantId.getId(), externalId.getId()); - } else { + entity = exportableEntityDao.findByTenantIdAndExternalId(tenantId.getId(), externalId.getId()); + } + if (entity == null || !belongsToTenant(entity, tenantId)) { return null; } + + return entity; } @Override @@ -92,24 +97,36 @@ public class DefaultExportableEntitiesService implements ExportableEntitiesServi Dao dao = getDao(entityType); E entity = dao.findById(tenantId, id.getId()); - if (((HasTenantId) entity).getTenantId().equals(tenantId)) { - return entity; + + if (entity == null || !belongsToTenant(entity, tenantId)) { + return null; } - return null; + + return entity; } @Override public , I extends EntityId> E findEntityByTenantIdAndName(TenantId tenantId, EntityType entityType, String name) { Dao dao = getDao(entityType); + E entity = null; + if (dao instanceof ExportableEntityDao) { ExportableEntityDao exportableEntityDao = (ExportableEntityDao) dao; try { - return exportableEntityDao.findByTenantIdAndName(tenantId.getId(), name); + entity = exportableEntityDao.findByTenantIdAndName(tenantId.getId(), name); } catch (UnsupportedOperationException ignored) { } } - return null; + if (entity == null || !belongsToTenant(entity, tenantId)) { + return null; + } + + return entity; + } + + private boolean belongsToTenant(HasId entity, TenantId tenantId) { + return tenantId.equals(((HasTenantId) entity).getTenantId()); } @@ -127,21 +144,21 @@ public class DefaultExportableEntitiesService implements ExportableEntitiesServi EntityTypeFilter entityTypeFilter = new EntityTypeFilter(); entityTypeFilter.setEntityType(exportRequest.getEntityType()); - CustomerId customerId = Optional.ofNullable(exportRequest.getCustomerId()).orElse(new CustomerId(EntityId.NULL_UUID)); + CustomerId customerId = new CustomerId(ObjectUtils.defaultIfNull(exportRequest.getCustomerId(), EntityId.NULL_UUID)); return findEntitiesByFilter(tenantId, customerId, entityTypeFilter, exportRequest.getPage(), exportRequest.getPageSize()); } case CUSTOM_ENTITY_FILTER: { CustomEntityFilterExportRequest exportRequest = (CustomEntityFilterExportRequest) request; EntityFilter filter = exportRequest.getFilter(); - CustomerId customerId = Optional.ofNullable(exportRequest.getCustomerId()).orElse(new CustomerId(EntityId.NULL_UUID)); + CustomerId customerId = new CustomerId(ObjectUtils.defaultIfNull(exportRequest.getCustomerId(), EntityId.NULL_UUID)); return findEntitiesByFilter(tenantId, customerId, filter, exportRequest.getPage(), exportRequest.getPageSize()); } case CUSTOM_ENTITY_QUERY: { CustomEntityQueryExportRequest exportRequest = (CustomEntityQueryExportRequest) request; EntityDataQuery query = exportRequest.getQuery(); - CustomerId customerId = Optional.ofNullable(exportRequest.getCustomerId()).orElse(new CustomerId(EntityId.NULL_UUID)); + CustomerId customerId = new CustomerId(ObjectUtils.defaultIfNull(exportRequest.getCustomerId(), EntityId.NULL_UUID)); return findEntitiesByQuery(tenantId, customerId, query); } default: { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityFilterExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityFilterExportRequest.java index 02cbca98d6..d71ddc8720 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityFilterExportRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityFilterExportRequest.java @@ -17,9 +17,10 @@ package org.thingsboard.server.service.sync.exporting.data.request; import lombok.Data; import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.query.EntityFilter; +import java.util.UUID; + @EqualsAndHashCode(callSuper = true) @Data public class CustomEntityFilterExportRequest extends ExportRequest { @@ -27,7 +28,7 @@ public class CustomEntityFilterExportRequest extends ExportRequest { private EntityFilter filter; private int page; private int pageSize; - private CustomerId customerId; + private UUID customerId; @Override public ExportRequestType getType() { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityQueryExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityQueryExportRequest.java index 76d258e891..946676cf65 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityQueryExportRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityQueryExportRequest.java @@ -17,15 +17,16 @@ package org.thingsboard.server.service.sync.exporting.data.request; import lombok.Data; import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.query.EntityDataQuery; +import java.util.UUID; + @EqualsAndHashCode(callSuper = true) @Data public class CustomEntityQueryExportRequest extends ExportRequest { private EntityDataQuery query; - private CustomerId customerId; + private UUID customerId; @Override public ExportRequestType getType() { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityTypeExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityTypeExportRequest.java index 82cb023258..fd2f659b70 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityTypeExportRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityTypeExportRequest.java @@ -18,7 +18,8 @@ package org.thingsboard.server.service.sync.exporting.data.request; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.id.CustomerId; + +import java.util.UUID; @EqualsAndHashCode(callSuper = true) @Data @@ -27,7 +28,7 @@ public class EntityTypeExportRequest extends ExportRequest { private EntityType entityType; private int page; private int pageSize; - private CustomerId customerId; + private UUID customerId; @Override public ExportRequestType getType() { From 5736e52d3e38a5ffaff6c7f0a6a91ebdba22c788 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Mon, 25 Apr 2022 14:54:53 +0300 Subject: [PATCH 30/39] Fix EntitiesExportImportControllerSqlTest --- .../server/controller/EntitiesExportImportController.java | 1 + .../controller/sql/EntitiesExportImportControllerSqlTest.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index afe2a4ac1f..a6ad9f1ca6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -81,6 +81,7 @@ public class EntitiesExportImportController extends BaseController { private List> exportEntitiesByRequest(SecurityUser user, ExportRequest exportRequest) throws ThingsboardException { List entities = exportableEntitiesService.findEntitiesForRequest(user.getTenantId(), exportRequest); + EntityExportSettings exportSettings = exportRequest.getExportSettings(); if (exportSettings == null) { exportSettings = EntityExportSettings.builder() diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java index 44d8f5f461..bd237e39d0 100644 --- a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java @@ -659,7 +659,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp exportRequest.setExportSettings(new EntityExportSettings()); exportRequest.setPageSize(10); exportRequest.setEntityType(entityType); - exportRequest.setCustomerId(customer.getId()); + exportRequest.setCustomerId(customer.getUuidId()); exportRequests.add(exportRequest); } From ba1ea3521034b2eb9a6cb24c5165bf88396480be Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Tue, 26 Apr 2022 12:08:05 +0300 Subject: [PATCH 31/39] Swagger docs for export-import controller --- .../EntitiesExportImportController.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java index a6ad9f1ca6..c91fd0c292 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.controller; +import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; @@ -39,6 +40,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE; + @RestController @RequestMapping("/api/entities") @TbCoreComponent @@ -50,6 +53,42 @@ public class EntitiesExportImportController extends BaseController { private final ExportableEntitiesService exportableEntitiesService; + @ApiOperation(value = "Export entities by request", notes = "" + + "Takes export request and returns list of export data for each entity found by request. " + + "Supported entity types for export, hence for import, are **DEVICE**, **DEVICE_PROFILE**, **ASSET**, " + + "**CUSTOMER**, **RULE_CHAIN** and **DASHBOARD**." + NEW_LINE + + "For each type of export request, you can set some export settings: \n" + + "- **exportRelations** - whether to export inbound and outbound relations for an entity " + + "(only relations of type group COMMON can be exported)" + NEW_LINE + + "Supported export requests:\n" + + "- **SINGLE_ENTITY**:" + NEW_LINE + + " To export a single entity by id. Example:" + NEW_LINE + + "```\n{\n \"type\": \"SINGLE_ENTITY\",\n \"entityId\": {\n \"entityType\": \"DEVICE\",\n \"id\": \"2eb16d70-989d-11ec-93b5-6de6c2b68078\"\n },\n \"exportSettings\": {\n \"exportRelations\": false\n }\n}\n```" + NEW_LINE + + "- **ENTITY_LIST**:" + NEW_LINE + + " To export a list of entities by their ids. Example:" + NEW_LINE + + "```\n{\n \"type\": \"ENTITY_LIST\",\n \"entitiesIds\": [\n {\n \"entityType\": \"DEVICE\",\n \"id\": \"2eb16d70-989d-11ec-93b5-6de6c2b68078\"\n },\n {\n \"entityType\": \"ASSET\",\n \"id\": \"2f0a3bd0-989d-11ec-93b5-6de6c2b68078\"\n }\n ],\n \"exportSettings\": {\n \"exportRelations\": true\n }\n}\n```" + NEW_LINE + + "- **ENTITY_TYPE**:" + NEW_LINE + + " To export entities of specified entity type. You need to specify page size, " + + "and may specify page index and customer id (to limit the list of entities to the ones owned by a customer). " + + "Entities are ordered by created time descendingly. Example:" + NEW_LINE + + "```\n{\n \"type\": \"ENTITY_TYPE\",\n \"entityType\": \"ASSET\",\n \"page\": 0,\n \"pageSize\": 100,\n \"customerId\": \"2eb16d70-989d-11ec-93b5-6de6c2b68078\"\n}\n```" + NEW_LINE + + "- **CUSTOM_ENTITY_FILTER**:" + NEW_LINE + + " To export entities by custom entity filter. The order used is the same as for ENTITY_TYPE export request. Example:" + NEW_LINE + + "```\n{\n \"type\": \"CUSTOM_ENTITY_FILTER\",\n \"filter\": {\n \"type\": \"deviceType\",\n \"deviceType\": \"Thermostats\",\n \"deviceNameFilter\": \"\"\n },\n \"page\": 0,\n \"pageSize\": 100,\n \"customerId\": null,\n \"exportSettings\": {\n \"exportRelations\": false\n }\n}\n```" + NEW_LINE + + "- **CUSTOM_ENTITY_QUERY**:" + NEW_LINE + + " To export entities by custom entity query. Example: " + NEW_LINE + + "```\n{\n \"type\": \"CUSTOM_ENTITY_QUERY\",\n \"query\": {\n \"entityFilter\": {\n \"type\": \"entityType\",\n \"entityType\": \"DEVICE\"\n },\n \"pageLink\": {\n \"page\": 0,\n \"pageSize\": 200,\n \"textSearch\": \"THB_\",\n \"sortOrder\": {\n \"key\": {\n \"type\": \"ENTITY_FIELD\",\n \"key\": \"name\"\n },\n \"direction\": \"DESC\"\n }\n },\n \"entityFields\": [\n {\n \"type\": \"ENTITY_FIELD\",\n \"key\": \"name\"\n }\n ],\n \"latestValues\": [\n {\n \"type\": \"SERVER_ATTRIBUTE\",\n \"key\": \"lastActivityTime\"\n }\n ],\n \"keyFilters\": [\n {\n \"key\": {\n \"type\": \"SERVER_ATTRIBUTE\",\n \"key\": \"lastActivityTime\"\n },\n \"valueType\": \"NUMERIC\",\n \"predicate\": {\n \"type\": \"NUMERIC\",\n \"operation\": \"GREATER\",\n \"value\": {\n \"defaultValue\": 0\n }\n }\n }\n ]\n },\n \"customerId\": null,\n \"exportSettings\": {\n \"exportRelations\": false\n }\n}\n```" + NEW_LINE + + "Mostly, export data of an entity contains the whole entity itself and its relations " + + "(if option to export relations was enabled):" + NEW_LINE + + "```\n[\n {\n \"entityType\": \"ASSET\",\n \"entity\": {\n \"id\": { ... },\n \"createdTime\": 1648204424029,\n \"additionalInfo\": {\n \"description\": \"\"\n },\n \"tenantId\": { ... },\n \"customerId\": { ... },\n \"name\": \"Asset 1\",\n \"type\": \"A\",\n ...\n },\n \"relations\": [\n {\n \"from\": {\n \"entityType\": \"ASSET\",\n \"id\": ...\n },\n \"to\": {\n \"entityType\": \"DEVICE\",\n \"id\": ...\n },\n \"type\": \"Contains\",\n \"typeGroup\": \"COMMON\",\n \"additionalInfo\": {\n \"a\": \"b\"\n }\n }\n ]\n }\n]\n```" + NEW_LINE + + "For devices, export data will additionally contain device's credentials; for rule chains - its metadata:" + NEW_LINE + + "```\n[\n {\n \"entityType\": \"DEVICE\",\n \"entity\": { ... },\n \"credentials\": {\n \"id\": { ... },\n \"createdTime\": 1648829321209,\n \"deviceId\": { ... },\n \"credentialsType\": \"ACCESS_TOKEN\",\n \"credentialsId\": \"5cZEDo45KGW7JgVNv4Ko\",\n \"credentialsValue\": null\n }\n }\n]\n```" + NEW_LINE + + "```\n[\n {\n \"entityType\": \"RULE_CHAIN\",\n \"entity\": {\n \"id\": { ... },\n \"createdTime\": 1646056614257,\n \"additionalInfo\": null,\n \"tenantId\": { ... },\n \"name\": \"Rule Chain 2\",\n \"type\": \"CORE\",\n \"firstRuleNodeId\": { ... },\n \"root\": false,\n ...\n },\n \"metaData\": {\n \"ruleChainId\": { ... },\n \"firstNodeIndex\": 7,\n \"nodes\": [ ... ],\n \"connections\": [ ... ],\n \"ruleChainConnections\": null\n }\n }\n]\n```" + NEW_LINE + + "Returned export data is to be used later for import request." + NEW_LINE + + "If any entity found by request is of unsupported type - an error will be returned.\n" + + "Also, if a user does not have a READ permission for an entity (or, if relations are exported, for a bounded entity), " + + "access will be denied." + + ControllerConstants.TENANT_AUTHORITY_PARAGRAPH) @PostMapping("/export") @PreAuthorize("hasAuthority('TENANT_ADMIN')") public List> exportEntities(@RequestBody ExportRequest exportRequest) throws ThingsboardException { @@ -62,6 +101,12 @@ public class EntitiesExportImportController extends BaseController { } } + @ApiOperation(value = "Export entities by multiple requests", notes = "" + + "The API behaviour is the same as for exporting entities by single request, " + + "except that this method takes an array of export requests as a request body." + NEW_LINE + + "Example:" + NEW_LINE + + "```\n[\n {\n \"type\": \"SINGLE_ENTITY\",\n \"entityId\": {\n \"entityType\": \"DEVICE_PROFILE\",\n \"id\": \"5f9eda10-b442-11ec-bbf5-adec34031568\"\n }\n },\n {\n \"type\": \"CUSTOM_ENTITY_FILTER\",\n \"filter\": {\n \"type\": \"deviceType\",\n \"deviceType\": \"thermostat\",\n \"deviceNameFilter\": \"\"\n },\n \"pageSize\": 1000\n },\n {\n \"type\": \"ENTITY_TYPE\",\n \"entityType\": \"ASSET\",\n \"pageSize\": 1000,\n \"exportSettings\": {\n \"exportRelations\": true\n }\n },\n {\n \"type\": \"ENTITY_LIST\",\n \"entitiesIds\": [\n {\n \"entityType\": \"RULE_CHAIN\",\n \"id\": \"2ef13590-989d-11ec-93b5-6de6c2b68078\"\n },\n {\n \"entityType\": \"RULE_CHAIN\",\n \"id\": \"e7311ec0-b442-11ec-bbf5-adec34031568\"\n }\n ]\n }\n]\n```" + + ControllerConstants.TENANT_AUTHORITY_PARAGRAPH) @PostMapping(value = "/export", params = {"multiple"}) @PreAuthorize("hasAuthority('TENANT_ADMIN')") public List> exportEntities(@RequestBody List exportRequests) throws ThingsboardException { @@ -98,6 +143,34 @@ public class EntitiesExportImportController extends BaseController { } + @ApiOperation(value = "Import entities by request", notes = "" + + "Takes import request and returns the list of import results. " + + "Import request must contain the list of export data and might contain import settings. " + NEW_LINE + + "The method creates an entity if it is new in the scope of a tenant, or otherwise updates an existing one. " + + "On entity import request, we first try to find an entity within a tenant that has externalId equal " + + "to the id in the export data. If the platform fails to do that, we then search for an entity with " + + "regular (internal) id like the one in the export data (this is useful in case we are exporting and " + + "importing entities within the same tenant). Then, if we still haven't found any entity, if findExistingByName " + + "option of the EntityImportSettings is enabled, we will search for the one by its name (this is also useful " + + "for avoiding conflicts with default device profile or Root Rule Chain when importing all entities from another " + + "tenant). After, if the exported entity is new for this tenant, we simply save it with external id " + + "from the export data, and also create relations if any (if updateRelations option is enabled). " + + "Otherwise, we will reset all fields of the existing entity to the ones from the export data and save it, " + + "and also will update the list of relations (remove the ones that aren't present in the export data, " + + "and update or create others), if updateRelations option is enabled." + NEW_LINE + + "If an entity contains references to some other entities, like device references certain device profile, " + + "we will find this other entity within the tenant by this principle: look for an entity with " + + "such external id, or otherwise, internal id. This requires referenced entities to be imported " + + "before the referencing entity (if we are importing to another tenant). So, when receiving the list " + + "of entities' export data for import, we first try to fix the data order to import 'standalone' entities first." + NEW_LINE + + "As for relations importing, they are processed after all entities in the import batch are already saved, " + + "and the internal id of a bounded entity is found with the regular principle." + NEW_LINE + + "Import of all entities and their relations from the import request is processed in the single transaction, " + + "and so everything will be rolled back if the platform fails to e.g. find internal entity by external id. \n" + + "Example of import request:\n" + + "```\n{\n \"importSettings\": {\n \"findExistingByName\": false,\n \"updateRelations\": false\n },\n \"exportDataList\": [\n {\n \"entityType\": \"DEVICE_PROFILE\",\n \"entity\": {\n \"id\": {\n \"entityType\": \"DEVICE_PROFILE\",\n \"id\": \"f84363d0-b442-11ec-bbf5-adec34031568\"\n },\n \"createdTime\": 1649096026765,\n \"tenantId\": {\n \"entityType\": \"TENANT\",\n \"id\": \"4c9001b0-b442-11ec-bbf5-adec34031568\"\n },\n \"name\": \"Profile 1\",\n ...\n }\n },\n {\n \"entityType\": \"DEVICE\",\n \"entity\": {\n \"id\": {\n \"entityType\": \"DEVICE\",\n \"id\": \"98161420-b4ca-11ec-ab0c-e7744c90d468\"\n },\n \"createdTime\": 1649154276962,\n \"tenantId\": {\n \"entityType\": \"TENANT\",\n \"id\": \"4c9001b0-b442-11ec-bbf5-adec34031568\"\n },\n \"customerId\": {\n \"entityType\": \"CUSTOMER\",\n \"id\": \"13814000-1dd2-11b2-8080-808080808080\"\n },\n \"name\": \"Device 1\",\n \"type\": \"Profile 1\",\n \"label\": \"v1.0\",\n \"deviceProfileId\": {\n \"entityType\": \"DEVICE_PROFILE\",\n \"id\": \"f84363d0-b442-11ec-bbf5-adec34031568\"\n },\n ...\n },\n \"credentials\": {\n \"id\": {\n \"id\": \"981e0360-b4ca-11ec-ab0c-e7744c90d468\"\n },\n \"createdTime\": 1649154277014,\n \"deviceId\": {\n \"entityType\": \"DEVICE\",\n \"id\": \"98161420-b4ca-11ec-ab0c-e7744c90d468\"\n },\n \"credentialsType\": \"ACCESS_TOKEN\",\n \"credentialsId\": \"sGExNdnl71uKmkNvtNdp\",\n \"credentialsValue\": null\n }\n }\n ]\n}\n```" + NEW_LINE + + "The response contains a list of EntityImportResult which has values of savedEntity and oldEntity:\n" + + "```\n[\n {\n \"savedEntity\": {\n \"id\": {\n \"entityType\": \"ASSET\",\n \"id\": \"d73d7690-b4e6-11ec-b9eb-0562e1a20a1b\"\n },\n \"createdTime\": 1649166408825,\n \"additionalInfo\": {\n \"description\": \"\"\n },\n \"tenantId\": {\n \"entityType\": \"TENANT\",\n \"id\": \"c0b2e4f0-b4e6-11ec-b9eb-0562e1a20a1b\"\n },\n \"name\": \"Asset 1\",\n \"type\": \"A\",\n \"label\": \"v2.0\",\n ...\n \"externalId\": {\n \"entityType\": \"ASSET\",\n \"id\": \"6b03ab20-989e-11ec-b446-89df822d7fa2\"\n }\n },\n \"oldEntity\": {\n \"id\": {\n \"entityType\": \"ASSET\",\n \"id\": \"d73d7690-b4e6-11ec-b9eb-0562e1a20a1b\"\n },\n \"createdTime\": 1649166408825,\n \"tenantId\": {\n \"entityType\": \"TENANT\",\n \"id\": \"c0b2e4f0-b4e6-11ec-b9eb-0562e1a20a1b\"\n },\n \"name\": \"Asset 1\",\n \"type\": \"A\",\n \"label\": \"v1.0\",\n ...\n \"externalId\": null\n },\n \"entityType\": \"ASSET\"\n },\n {\n \"savedEntity\": {\n \"id\": {\n \"entityType\": \"ASSET\",\n \"id\": \"387213f0-b4ea-11ec-abfc-6dcc6508d0b5\"\n },\n \"createdTime\": 1649167860399,\n \"tenantId\": {\n \"entityType\": \"TENANT\",\n \"id\": \"c0b2e4f0-b4e6-11ec-b9eb-0562e1a20a1b\"\n },\n \"name\": \"Asset 2\",\n \"type\": \"B\",\n ...\n \"externalId\": {\n \"entityType\": \"ASSET\",\n \"id\": \"0b9ea0d0-ac27-11ec-a2a6-89d15eae3b21\"\n }\n },\n \"oldEntity\": null,\n \"entityType\": \"ASSET\"\n }\n]\n```") @PostMapping("/import") public List> importEntities(@RequestBody ImportRequest importRequest) throws ThingsboardException { SecurityUser user = getCurrentUser(); From a1d62373c9784b32c454b45a0fe3b5a409533814 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Tue, 26 Apr 2022 12:08:28 +0300 Subject: [PATCH 32/39] Transaction timeout for finding entities by request and importing entities batch --- .../server/service/sync/DefaultEntitiesExportImportService.java | 2 +- .../sync/exporting/DefaultExportableEntitiesService.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java index 75c4ff9e12..55384b21db 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java @@ -70,7 +70,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS } - @Transactional(rollbackFor = Exception.class) + @Transactional(rollbackFor = Exception.class, timeout = 120) @Override public List> importEntities(SecurityUser user, List> exportDataList, EntityImportSettings importSettings) throws ThingsboardException { fixOrder(exportDataList); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java index a616dec6c4..bc8f406c54 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java @@ -21,6 +21,7 @@ import org.apache.commons.lang3.ObjectUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.HasTenantId; @@ -130,6 +131,7 @@ public class DefaultExportableEntitiesService implements ExportableEntitiesServi } + @Transactional(readOnly = true, timeout = 40) @Override public List findEntitiesForRequest(TenantId tenantId, ExportRequest request) { switch (request.getType()) { From 335b4ef4652a07d9ddf032941c5c4f7833624ceb Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Sat, 14 May 2022 17:23:34 +0300 Subject: [PATCH 33/39] Improvements for import of rule chains and dashboards --- .../DefaultExportableEntitiesService.java | 3 ++ .../impl/BaseEntityImportService.java | 24 +++++++++++++ .../impl/DashboardImportService.java | 35 +++++------------- .../impl/RuleChainImportService.java | 29 +++++++-------- .../thingsboard/server/utils/RegexUtils.java | 36 +++++++++++++++++++ 5 files changed, 85 insertions(+), 42 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/utils/RegexUtils.java diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java index bc8f406c54..ce6924de93 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java @@ -96,6 +96,9 @@ public class DefaultExportableEntitiesService implements ExportableEntitiesServi public , I extends EntityId> E findEntityByTenantIdAndId(TenantId tenantId, I id) { EntityType entityType = id.getEntityType(); Dao dao = getDao(entityType); + if (dao == null) { + throw new IllegalArgumentException("Unsupported entity type " + entityType); + } E entity = dao.findById(tenantId, id.getId()); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java index e96359a746..28095a7b85 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java @@ -20,11 +20,13 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.cluster.TbClusterService; +import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.HasCustomerId; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.relation.EntityRelation; @@ -42,6 +44,7 @@ import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.UUID; public abstract class BaseEntityImportService, D extends EntityExportData> implements EntityImportService { @@ -201,6 +204,27 @@ public abstract class BaseEntityImportService getInternalIdByUuid(UUID externalUuid) { + for (EntityType entityType : EntityType.values()) { + EntityId externalId; + try { + externalId = EntityIdFactory.getByTypeAndUuid(entityType, externalUuid); + } catch (Exception e) { + continue; + } + + EntityId internalId = null; + try { + internalId = getInternalId(externalId); + } catch (Exception ignored) {} + + if (internalId != null) { + return Optional.of(internalId); + } + } + return Optional.empty(); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java index e7c5ed0ea3..77f32bf94c 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java @@ -16,7 +16,6 @@ package org.thingsboard.server.service.sync.importing.impl; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; @@ -27,22 +26,19 @@ import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.query.EntityFilter; import org.thingsboard.server.dao.dashboard.DashboardService; -import org.thingsboard.server.dao.sql.query.DefaultEntityQueryRepository; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; +import org.thingsboard.server.utils.RegexUtils; import java.util.Collections; import java.util.HashSet; import java.util.Optional; import java.util.Set; -import java.util.regex.Pattern; +import java.util.UUID; import java.util.stream.Collectors; @Service @@ -52,7 +48,6 @@ public class DashboardImportService extends BaseEntityImportService exportData, IdProvider idProvider) { - Optional.ofNullable(dashboard.getConfiguration()) - .flatMap(configuration -> Optional.ofNullable(configuration.get("entityAliases"))) - .filter(JsonNode::isObject) - .ifPresent(entityAliases -> entityAliases.forEach(entityAlias -> { - Optional.ofNullable(entityAlias.get("filter")) - .filter(JsonNode::isObject) - .ifPresent(filter -> { - EntityFilter entityFilter = JacksonUtil.treeToValue(filter, EntityFilter.class); - EntityType entityType = DefaultEntityQueryRepository.resolveEntityType(entityFilter); - - String filterJson = filter.toString(); - String newFilterJson = UUID_PATTERN.matcher(filterJson).replaceAll(matchResult -> { - String uuid = matchResult.group(); - EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, uuid); - return idProvider.getInternalId(entityId).toString(); - }); - ((ObjectNode) entityAlias).set("filter", JacksonUtil.toJsonNode(newFilterJson)); - }); - })); + JsonNode configuration = dashboard.getConfiguration(); + String newConfigurationJson = RegexUtils.replace(configuration.toString(), RegexUtils.UUID_PATTERN, uuid -> { + return idProvider.getInternalIdByUuid(UUID.fromString(uuid)) + .map(entityId -> entityId.getId().toString()).orElse(uuid); + }); + configuration = JacksonUtil.toJsonNode(newConfigurationJson); + dashboard.setConfiguration(configuration); Set assignedCustomers = Optional.ofNullable(dashboard.getAssignedCustomers()).orElse(Collections.emptySet()).stream() .peek(customerInfo -> customerInfo.setCustomerId(idProvider.getInternalId(customerInfo.getCustomerId()))) diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java index 1e4ed958a8..09a13d5f14 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java @@ -16,10 +16,9 @@ package org.thingsboard.server.service.sync.importing.impl; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.node.TextNode; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; @@ -29,11 +28,13 @@ import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainType; +import org.thingsboard.server.common.data.rule.RuleChainUpdateResult; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.RuleChainExportData; import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; +import org.thingsboard.server.utils.RegexUtils; import java.util.Collections; import java.util.Optional; @@ -67,16 +68,15 @@ public class RuleChainImportService extends BaseEntityImportService { ruleNode.setId(null); ruleNode.setRuleChainId(null); + JsonNode ruleNodeConfig = ruleNode.getConfiguration(); - Optional.ofNullable(ruleNodeConfig) - .flatMap(config -> Optional.ofNullable(config.get("ruleChainId")).filter(JsonNode::isTextual)) - .map(JsonNode::asText).map(UUID::fromString) - .ifPresent(otherRuleChainUuid -> { - ((ObjectNode) ruleNodeConfig).set("ruleChainId", new TextNode( - idProvider.getInternalId(new RuleChainId(otherRuleChainUuid)).toString() - )); - ruleNode.setConfiguration(ruleNodeConfig); - }); + String newRuleNodeConfigJson = RegexUtils.replace(ruleNodeConfig.toString(), RegexUtils.UUID_PATTERN, uuid -> { + return idProvider.getInternalIdByUuid(UUID.fromString(uuid)) + .map(entityId -> entityId.getId().toString()) + .orElse(uuid); + }); + ruleNodeConfig = JacksonUtil.toJsonNode(newRuleNodeConfigJson); + ruleNode.setConfiguration(ruleNodeConfig); }); Optional.ofNullable(metaData.getRuleChainConnections()).orElse(Collections.emptyList()) .forEach(ruleChainConnectionInfo -> { @@ -84,13 +84,10 @@ public class RuleChainImportService extends BaseEntityImportService replacer) { + return pattern.matcher(s).replaceAll(matchResult -> { + return replacer.apply(matchResult.group()); + }); + } + +} From f82be0153b4e77aba4d686face2b0861ff2af607 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Sat, 2 Apr 2022 19:42:48 +0300 Subject: [PATCH 34/39] Entities VC with Git - initial implementation --- application/pom.xml | 4 + .../EntitiesVersionControlController.java | 118 ++++++++ .../DefaultEntitiesVersionControlService.java | 274 ++++++++++++++++++ .../vcs/EntitiesVersionControlService.java | 50 ++++ .../data/EntitiesVersionControlSettings.java | 28 ++ .../service/sync/vcs/data/EntityVersion.java | 29 ++ .../service/sync/vcs/data/GitSettings.java | 32 ++ .../server/utils/git/Repository.java | 256 ++++++++++++++++ .../server/utils/git/data/Branch.java | 23 ++ .../server/utils/git/data/Commit.java | 25 ++ .../server/utils/git/data/Diff.java | 25 ++ pom.xml | 6 + 12 files changed, 870 insertions(+) create mode 100644 application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vcs/DefaultEntitiesVersionControlService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vcs/EntitiesVersionControlService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vcs/data/EntitiesVersionControlSettings.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vcs/data/EntityVersion.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vcs/data/GitSettings.java create mode 100644 application/src/main/java/org/thingsboard/server/utils/git/Repository.java create mode 100644 application/src/main/java/org/thingsboard/server/utils/git/data/Branch.java create mode 100644 application/src/main/java/org/thingsboard/server/utils/git/data/Commit.java create mode 100644 application/src/main/java/org/thingsboard/server/utils/git/data/Diff.java diff --git a/application/pom.xml b/application/pom.xml index 1b89752925..2bfab2c858 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -337,6 +337,10 @@ Java-WebSocket test + + org.eclipse.jgit + org.eclipse.jgit + diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java new file mode 100644 index 0000000000..10928f1b51 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java @@ -0,0 +1,118 @@ +/** + * 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.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.importing.EntityImportResult; +import org.thingsboard.server.service.sync.vcs.DefaultEntitiesVersionControlService; +import org.thingsboard.server.service.sync.vcs.data.EntitiesVersionControlSettings; +import org.thingsboard.server.service.sync.vcs.data.EntityVersion; + +import java.util.List; +import java.util.Set; +import java.util.UUID; + +@RestController +@RequestMapping("/api/entities/vc") +@RequiredArgsConstructor +public class EntitiesVersionControlController extends BaseController { + + private final DefaultEntitiesVersionControlService versionControlService; + + + + @PostMapping("/version/{entityType}/{entityId}") + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + public EntityVersion saveEntityVersion(@PathVariable EntityType entityType, + @PathVariable("entityId") UUID entityUuid, + @RequestParam String branch, + @RequestBody String versionName) throws Exception { + EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); + return versionControlService.saveEntityVersion(getTenantId(), entityId, branch, versionName); + } + + @GetMapping("/version/{entityType}/{entityId}") + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + public List listEntityVersions(@PathVariable EntityType entityType, + @PathVariable("entityId") UUID entityUuid, + @RequestParam String branch) throws Exception { + EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); + return versionControlService.listEntityVersions(getTenantId(), entityId, branch, Integer.MAX_VALUE); + } + + + + @GetMapping("/entity/{entityType}/{entityId}/{versionId}") + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + public EntityExportData> getEntityAtVersion(@PathVariable EntityType entityType, + @PathVariable("entityId") UUID entityUuid, + @PathVariable String versionId) throws Exception { + EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); + return versionControlService.getEntityAtVersion(getTenantId(), entityId, versionId); + } + + @PostMapping("/entity/{entityType}/{entityId}/{versionId}") + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + public EntityImportResult> loadEntityVersion(@PathVariable EntityType entityType, + @PathVariable("entityId") UUID entityUuid, + @PathVariable String versionId) throws Exception { + EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); + return versionControlService.loadEntityVersion(getTenantId(), entityId, versionId); + } + + + + @GetMapping("/branches") + public Set getAllowedBranches() throws ThingsboardException { + return versionControlService.getAllowedBranches(getTenantId()); + } + + + @PostMapping("/settings") + @PreAuthorize("hasAuthority('SYS_ADMIN')") + public void saveSettings(@RequestBody EntitiesVersionControlSettings settings) throws Exception { + versionControlService.saveSettings(settings); + } + + @GetMapping("/settings") + @PreAuthorize("hasAuthority('SYS_ADMIN')") + public EntitiesVersionControlSettings getSettings() { + return versionControlService.getSettings(); + } + + + + @PostMapping("/repository/reset") + @PreAuthorize("hasAuthority('SYS_ADMIN')") + public void resetLocalRepository() throws Exception { + versionControlService.resetRepository(); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vcs/DefaultEntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vcs/DefaultEntitiesVersionControlService.java new file mode 100644 index 0000000000..0d78496f16 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vcs/DefaultEntitiesVersionControlService.java @@ -0,0 +1,274 @@ +/** + * 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.sync.vcs; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.SerializationFeature; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.settings.AdminSettingsService; +import org.thingsboard.server.dao.tenant.TenantService; +import org.thingsboard.server.queue.util.AfterStartUp; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.sync.EntitiesExportImportService; +import org.thingsboard.server.service.sync.exporting.EntityExportSettings; +import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.importing.EntityImportResult; +import org.thingsboard.server.service.sync.importing.EntityImportSettings; +import org.thingsboard.server.service.sync.vcs.data.EntitiesVersionControlSettings; +import org.thingsboard.server.service.sync.vcs.data.EntityVersion; +import org.thingsboard.server.service.sync.vcs.data.GitSettings; +import org.thingsboard.server.utils.git.Repository; +import org.thingsboard.server.utils.git.data.Commit; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +@Slf4j +public class DefaultEntitiesVersionControlService implements EntitiesVersionControlService { + // TODO [viacheslav]: start up only on one of the cores + + private final TenantService tenantService; + private final EntitiesExportImportService exportImportService; + private final AdminSettingsService adminSettingsService; + + private final ObjectWriter jsonWriter = new ObjectMapper().writer(SerializationFeature.INDENT_OUTPUT); + private static final String SETTINGS_KEY = "vc"; + + private Repository repository; + private final ReentrantLock fetchLock = new ReentrantLock(); + private final Lock writeLock = new ReentrantLock(); + + @AfterStartUp + public void init() throws Exception { + try { + EntitiesVersionControlSettings settings = getSettings(); + if (settings != null && settings.getGitSettings() != null) { + this.repository = initRepository(settings.getGitSettings()); + } + } catch (Exception e) { + log.error("Failed to initialize entities version control service", e); + } + } + + + @Scheduled(initialDelay = 10 * 1000, fixedDelay = 10 * 1000) + public void fetch() throws Exception { + if (repository == null) return; + + if (fetchLock.tryLock()) { + try { + log.info("Fetching remote repository"); + repository.fetch(); + } finally { + fetchLock.unlock(); + } + } + } + + + @Override + public EntityVersion saveEntityVersion(TenantId tenantId, EntityId entityId, String branch, String versionName) throws Exception { + return saveEntitiesVersion(tenantId, List.of(entityId), branch, versionName); + } + + @Override + public EntityVersion saveEntitiesVersion(TenantId tenantId, List entitiesIds, String branch, String versionName) throws Exception { + checkRepository(); + checkBranch(tenantId, branch); + + EntityExportSettings exportSettings = EntityExportSettings.builder() + .exportInboundRelations(false) + .exportOutboundRelations(false) + .build(); + List>> entityDataList = entitiesIds.stream() + .map(entityId -> { + return exportImportService.exportEntity(tenantId, entityId, exportSettings); + }) + .collect(Collectors.toList()); + + if (fetchLock.tryLock()) { + try { + repository.fetch(); + } finally { + fetchLock.unlock(); + } + } + + writeLock.lock(); + try { + if (repository.listBranches().contains(branch)) { + repository.checkout(branch); + repository.merge(branch); + } else { + repository.createAndCheckoutOrphanBranch(branch); + } + + for (EntityExportData> entityData : entityDataList) { + String entityDataJson = jsonWriter.writeValueAsString(entityData); + FileUtils.write(new File(repository.getDirectory() + "/" + getRelativePathForEntity(entityData.getEntity().getId())), + entityDataJson, StandardCharsets.UTF_8); + } + + Commit commit = repository.commit(versionName, ".", "Tenant " + tenantId); + repository.push(); + return new EntityVersion(commit.getId(), commit.getMessage(), commit.getAuthorName()); + } finally { + writeLock.unlock(); + } + } + + + + @Override + public List listEntityVersions(TenantId tenantId, EntityId entityId, String branch, int limit) throws Exception { + checkRepository(); + checkBranch(tenantId, branch); + + return repository.listCommits(branch, getRelativePathForEntity(entityId), limit).stream() + .map(commit -> new EntityVersion(commit.getId(), commit.getMessage(), commit.getAuthorName())) + .collect(Collectors.toList()); + } + + @Override + public List listEntityTypeVersions(TenantId tenantId, EntityType entityType, String branch, int limit) throws Exception { + checkRepository(); + checkBranch(tenantId, branch); + + return repository.listCommits(branch, getRelativePathForEntityType(entityType), limit).stream() + .map(commit -> new EntityVersion(commit.getId(), commit.getMessage(), commit.getAuthorName())) + .collect(Collectors.toList()); + } + + + + @Override + public , I extends EntityId> EntityExportData getEntityAtVersion(TenantId tenantId, I entityId, String versionId) throws Exception { + checkRepository(); + // FIXME [viacheslav]: validate access + + String entityDataJson = repository.getFileContentAtCommit(getRelativePathForEntity(entityId), versionId); + return JacksonUtil.fromString(entityDataJson, new TypeReference>() {}); + } + + @Override + public , I extends EntityId> EntityImportResult loadEntityVersion(TenantId tenantId, I entityId, String versionId) throws Exception { + EntityExportData entityData = getEntityAtVersion(tenantId, entityId, versionId); + return exportImportService.importEntity(tenantId, entityData, EntityImportSettings.builder() + .importInboundRelations(false) + .importOutboundRelations(false) + .updateReferencesToOtherEntities(true) + .build()); + } + + + + private String getRelativePathForEntity(EntityId entityId) { + return getRelativePathForEntityType(entityId.getEntityType()) + + "/" + entityId.getId() + ".json"; + } + + private String getRelativePathForEntityType(EntityType entityType) { + return entityType.name().toLowerCase(); + } + + + private void checkBranch(TenantId tenantId, String branch) { + if (!getAllowedBranches(tenantId).contains(branch)) { + throw new IllegalArgumentException("Tenant does not have access to this branch"); + } + } + + public Set getAllowedBranches(TenantId tenantId) { + return Optional.ofNullable(getSettings()) + .flatMap(settings -> Optional.ofNullable(settings.getAllowedBranches())) + .flatMap(tenantsAllowedBranches -> Optional.ofNullable(tenantsAllowedBranches.get(tenantId.getId()))) + .orElse(Collections.emptySet()); + } + + @Override + public void saveSettings(EntitiesVersionControlSettings settings) throws Exception { + this.repository = initRepository(settings.getGitSettings()); + + AdminSettings adminSettings = Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, SETTINGS_KEY)) + .orElseGet(() -> { + AdminSettings newSettings = new AdminSettings(); + newSettings.setKey(SETTINGS_KEY); + return newSettings; + }); + adminSettings.setJsonValue(JacksonUtil.valueToTree(settings)); + adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings); + } + + @Override + public EntitiesVersionControlSettings getSettings() { + return Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, SETTINGS_KEY)) + .map(adminSettings -> JacksonUtil.treeToValue(adminSettings.getJsonValue(), EntitiesVersionControlSettings.class)) + .orElse(null); + } + + + + private void checkRepository() { + if (repository == null) { + throw new IllegalStateException("Repository is not initialized"); + } + } + + private static Repository initRepository(GitSettings gitSettings) throws Exception { + if (Files.exists(Path.of(gitSettings.getRepositoryDirectory()))) { + return Repository.open(gitSettings.getRepositoryDirectory(), + gitSettings.getUsername(), gitSettings.getPassword()); + } else { + Files.createDirectories(Path.of(gitSettings.getRepositoryDirectory())); + return Repository.clone(gitSettings.getRepositoryUri(), gitSettings.getRepositoryDirectory(), + gitSettings.getUsername(), gitSettings.getPassword()); + } + } + + public void resetRepository() throws Exception { + if (this.repository != null) { + FileUtils.deleteDirectory(new File(repository.getDirectory())); + this.repository = null; + } + EntitiesVersionControlSettings settings = getSettings(); + this.repository = initRepository(settings.getGitSettings()); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vcs/EntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vcs/EntitiesVersionControlService.java new file mode 100644 index 0000000000..a9a3c0e3e8 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vcs/EntitiesVersionControlService.java @@ -0,0 +1,50 @@ +/** + * 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.sync.vcs; + +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.importing.EntityImportResult; +import org.thingsboard.server.service.sync.vcs.data.EntitiesVersionControlSettings; +import org.thingsboard.server.service.sync.vcs.data.EntityVersion; + +import java.util.List; + +public interface EntitiesVersionControlService { + + EntityVersion saveEntityVersion(TenantId tenantId, EntityId entityId, String branch, String versionName) throws Exception; + + EntityVersion saveEntitiesVersion(TenantId tenantId, List entitiesIds, String branch, String versionName) throws Exception; + + + List listEntityVersions(TenantId tenantId, EntityId entityId, String branch, int limit) throws Exception; + + List listEntityTypeVersions(TenantId tenantId, EntityType entityType, String branch, int limit) throws Exception; + + + , I extends EntityId> EntityExportData getEntityAtVersion(TenantId tenantId, I entityId, String versionId) throws Exception; + + , I extends EntityId> EntityImportResult loadEntityVersion(TenantId tenantId, I entityId, String versionId) throws Exception; + + + void saveSettings(EntitiesVersionControlSettings settings) throws Exception; + + EntitiesVersionControlSettings getSettings(); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vcs/data/EntitiesVersionControlSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/vcs/data/EntitiesVersionControlSettings.java new file mode 100644 index 0000000000..6a46606845 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vcs/data/EntitiesVersionControlSettings.java @@ -0,0 +1,28 @@ +/** + * 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.sync.vcs.data; + +import lombok.Data; + +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +@Data +public class EntitiesVersionControlSettings { + private Map> allowedBranches; + private GitSettings gitSettings; +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vcs/data/EntityVersion.java b/application/src/main/java/org/thingsboard/server/service/sync/vcs/data/EntityVersion.java new file mode 100644 index 0000000000..42a2e2f555 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vcs/data/EntityVersion.java @@ -0,0 +1,29 @@ +/** + * 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.sync.vcs.data; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class EntityVersion { + private String id; + private String name; + private String authorName; +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vcs/data/GitSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/vcs/data/GitSettings.java new file mode 100644 index 0000000000..0d97bed2d1 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vcs/data/GitSettings.java @@ -0,0 +1,32 @@ +/** + * 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.sync.vcs.data; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class GitSettings { + private String repositoryUri; + private String repositoryDirectory; + private String username; + private String password; +} diff --git a/application/src/main/java/org/thingsboard/server/utils/git/Repository.java b/application/src/main/java/org/thingsboard/server/utils/git/Repository.java new file mode 100644 index 0000000000..8099be8259 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/utils/git/Repository.java @@ -0,0 +1,256 @@ +/** + * 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.utils.git; + +import com.google.common.collect.Streams; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.GitCommand; +import org.eclipse.jgit.api.ListBranchCommand; +import org.eclipse.jgit.api.LogCommand; +import org.eclipse.jgit.api.RmCommand; +import org.eclipse.jgit.api.TransportCommand; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectLoader; +import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.filter.RevFilter; +import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.PathFilter; +import org.thingsboard.server.utils.git.data.Commit; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class Repository { + + private final Git git; + private final CredentialsProvider credentialsProvider; + + @Getter + private final String directory; + + private Repository(Git git, CredentialsProvider credentialsProvider, String directory) { + this.git = git; + this.credentialsProvider = credentialsProvider; + this.directory = directory; + } + + public static Repository clone(String uri, String directory, + String username, String password) throws GitAPIException { + CredentialsProvider credentialsProvider = newCredentialsProvider(username, password); + Git git = Git.cloneRepository() + .setURI(uri) + .setDirectory(new java.io.File(directory)) + .setNoCheckout(true) + .setCredentialsProvider(credentialsProvider) + .call(); + return new Repository(git, credentialsProvider, directory); + } + + public static Repository open(String directory, String username, String password) throws IOException { + Git git = Git.open(new java.io.File(directory)); + return new Repository(git, newCredentialsProvider(username, password), directory); + } + + + public void fetch() throws GitAPIException { + execute(git.fetch() + .setRemoveDeletedRefs(true)); + } + + + public List listBranches() throws GitAPIException { + return execute(git.branchList() + .setListMode(ListBranchCommand.ListMode.ALL)).stream() + .filter(ref -> !ref.getName().equals(Constants.HEAD)) + .map(ref -> org.eclipse.jgit.lib.Repository.shortenRefName(ref.getName())) + .map(name -> StringUtils.removeStart(name, "origin/")) + .distinct().collect(Collectors.toList()); + } + + + public List listCommits(String branchName, int limit) throws IOException, GitAPIException { + return listCommits(branchName, null, limit); + } + + public List listCommits(String branchName, String path, int limit) throws IOException, GitAPIException { + ObjectId branchId = resolve("origin/" + branchName); + if (branchId == null) { + throw new IllegalArgumentException("Branch not found"); + } + LogCommand command = git.log() + .add(branchId).setMaxCount(limit) + .setRevFilter(RevFilter.NO_MERGES); + if (StringUtils.isNotEmpty(path)) { + command.addPath(path); + } + return Streams.stream(execute(command)) + .map(this::toCommit) + .collect(Collectors.toList()); + } + + + public List listFilesAtCommit(Commit commit) throws IOException { + return listFilesAtCommit(commit, null); + } + + public List listFilesAtCommit(Commit commit, String path) throws IOException { + List files = new ArrayList<>(); + RevCommit revCommit = resolveCommit(commit.getId()); + try (TreeWalk treeWalk = new TreeWalk(git.getRepository())) { + treeWalk.reset(revCommit.getTree().getId()); + if (StringUtils.isNotEmpty(path)) { + treeWalk.setFilter(PathFilter.create(path)); + } + treeWalk.setRecursive(true); + while (treeWalk.next()) { + files.add(treeWalk.getPathString()); + } + } + return files; + } + + + public String getFileContentAtCommit(String file, String commitId) throws IOException { + RevCommit revCommit = resolveCommit(commitId); + try (TreeWalk treeWalk = TreeWalk.forPath(git.getRepository(), file, revCommit.getTree())) { + if (treeWalk == null) { + throw new IllegalArgumentException("Not found"); + } + ObjectId blobId = treeWalk.getObjectId(0); + try (ObjectReader objectReader = git.getRepository().newObjectReader()) { + ObjectLoader objectLoader = objectReader.open(blobId); + byte[] bytes = objectLoader.getBytes(); + return new String(bytes, StandardCharsets.UTF_8); + } + } + } + + + public void checkout(String branchName) throws GitAPIException { + execute(git.checkout() + .setName(branchName)); + } + + public void merge(String branchName) throws IOException, GitAPIException { + ObjectId branchId = resolve("origin/" + branchName); + if (branchId == null) { + throw new IllegalArgumentException("Branch not found"); + } + execute(git.merge() + .include(branchId)); + } + + public void createAndCheckoutOrphanBranch(String name) throws GitAPIException { + execute(git.checkout() + .setOrphan(true) + .setName(name)); + Set uncommittedChanges = git.status().call().getUncommittedChanges(); + if (!uncommittedChanges.isEmpty()) { + RmCommand rm = git.rm(); + uncommittedChanges.forEach(rm::addFilepattern); + execute(rm); + } + execute(git.clean()); + } + + public void clean() throws GitAPIException { + execute(git.clean().setCleanDirectories(true)); + } + + public Commit commit(String message, String filePattern, String author) throws GitAPIException { + execute(git.add().addFilepattern(filePattern)); + RevCommit revCommit = execute(git.commit() + .setMessage(message) + .setAuthor(author, author)); + return toCommit(revCommit); + } + + + public void push() throws GitAPIException { + execute(git.push()); + } + + +// public List getCommitChanges(Commit commit) throws IOException, GitAPIException { +// RevCommit revCommit = resolveCommit(commit.getId()); +// if (revCommit.getParentCount() == 0) { +// return null; // just takes the first parent of a commit, but should find a parent in branch provided +// } +// return execute(git.diff() +// .setOldTree(prepareTreeParser(git.getRepository().parseCommit(revCommit.getParent(0)))) +// .setNewTree(prepareTreeParser(revCommit))).stream() +// .map(diffEntry -> new Diff(diffEntry.getChangeType().name(), diffEntry.getOldPath(), diffEntry.getNewPath())) +// .collect(Collectors.toList()); +// } +// +// +// private AbstractTreeIterator prepareTreeParser(RevCommit revCommit) throws IOException { +// // from the commit we can build the tree which allows us to construct the TreeParser +// //noinspection Duplicates +// org.eclipse.jgit.lib.Repository repository = git.getRepository(); +// try (RevWalk walk = new RevWalk(repository)) { +// RevTree tree = walk.parseTree(revCommit.getTree().getId()); +// +// CanonicalTreeParser treeParser = new CanonicalTreeParser(); +// try (ObjectReader reader = repository.newObjectReader()) { +// treeParser.reset(reader, tree.getId()); +// } +// +// walk.dispose(); +// +// return treeParser; +// } +// } + + private Commit toCommit(RevCommit revCommit) { + return new Commit(revCommit.getName(), revCommit.getFullMessage(), revCommit.getAuthorIdent().getName()); + } + + private RevCommit resolveCommit(String id) throws IOException { + return git.getRepository().parseCommit(resolve(id)); + } + + private ObjectId resolve(String rev) throws IOException { + return git.getRepository().resolve(rev); + } + + private , T> T execute(C command) throws GitAPIException { + if (command instanceof TransportCommand) { + ((TransportCommand) command).setCredentialsProvider(credentialsProvider); +// SshSessionFactory sshSessionFactory = SshSessionFactory.getInstance(); +// transportCommand.setTransportConfigCallback(transport -> { +// ((SshTransport) transport).setSshSessionFactory(sshSessionFactory); +// }); + } + return command.call(); + } + + private static CredentialsProvider newCredentialsProvider(String username, String password) { + return new UsernamePasswordCredentialsProvider(username, password); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/utils/git/data/Branch.java b/application/src/main/java/org/thingsboard/server/utils/git/data/Branch.java new file mode 100644 index 0000000000..a458f9f5aa --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/utils/git/data/Branch.java @@ -0,0 +1,23 @@ +/** + * 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.utils.git.data; + +import lombok.Data; + +@Data +public class Branch { + private final String shortName; +} diff --git a/application/src/main/java/org/thingsboard/server/utils/git/data/Commit.java b/application/src/main/java/org/thingsboard/server/utils/git/data/Commit.java new file mode 100644 index 0000000000..7567dba1c5 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/utils/git/data/Commit.java @@ -0,0 +1,25 @@ +/** + * 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.utils.git.data; + +import lombok.Data; + +@Data +public class Commit { + private final String id; + private final String message; + private final String authorName; +} diff --git a/application/src/main/java/org/thingsboard/server/utils/git/data/Diff.java b/application/src/main/java/org/thingsboard/server/utils/git/data/Diff.java new file mode 100644 index 0000000000..7572a003d2 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/utils/git/data/Diff.java @@ -0,0 +1,25 @@ +/** + * 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.utils.git.data; + +import lombok.Data; + +@Data +public class Diff { + private final String type; + private final String oldPath; + private final String newPath; +} diff --git a/pom.xml b/pom.xml index 9713e86993..ea5df86409 100755 --- a/pom.xml +++ b/pom.xml @@ -134,6 +134,7 @@ 1.16.0 1.12 + 6.1.0.202203080745-r @@ -1875,6 +1876,11 @@ ${zeroturnaround.version} test + + org.eclipse.jgit + org.eclipse.jgit + ${jgit.version} + From b3dfed5badc997144dc69588aaef2e5ff44b0c79 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Mon, 4 Apr 2022 12:00:02 +0300 Subject: [PATCH 35/39] API for listing entities at version, loading all entities at version, listing all available versions --- .../EntitiesVersionControlController.java | 64 +++++- .../DefaultEntitiesVersionControlService.java | 184 +++++++++++++----- .../vcs/EntitiesVersionControlService.java | 11 +- .../server/utils/git/Repository.java | 8 +- 4 files changed, 207 insertions(+), 60 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java index 10928f1b51..798556998c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java @@ -29,15 +29,18 @@ import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.exporting.data.EntityExportData; import org.thingsboard.server.service.sync.importing.EntityImportResult; import org.thingsboard.server.service.sync.vcs.DefaultEntitiesVersionControlService; import org.thingsboard.server.service.sync.vcs.data.EntitiesVersionControlSettings; import org.thingsboard.server.service.sync.vcs.data.EntityVersion; +import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.UUID; +import java.util.stream.Collectors; @RestController @RequestMapping("/api/entities/vc") @@ -51,13 +54,27 @@ public class EntitiesVersionControlController extends BaseController { @PostMapping("/version/{entityType}/{entityId}") @PreAuthorize("hasAuthority('TENANT_ADMIN')") public EntityVersion saveEntityVersion(@PathVariable EntityType entityType, - @PathVariable("entityId") UUID entityUuid, + @PathVariable("entityId") UUID id, @RequestParam String branch, @RequestBody String versionName) throws Exception { - EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); + EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, id); return versionControlService.saveEntityVersion(getTenantId(), entityId, branch, versionName); } + @PostMapping("/version/{entityType}") + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + public EntityVersion saveEntitiesVersion(@PathVariable EntityType entityType, + @RequestParam UUID[] ids, + @RequestParam String branch, + @RequestBody String versionName) throws Exception { + List entitiesIds = Arrays.stream(ids) + .map(id -> EntityIdFactory.getByTypeAndUuid(entityType, id)) + .collect(Collectors.toList()); + return versionControlService.saveEntitiesVersion(getTenantId(), entitiesIds, branch, versionName); + } + + + @GetMapping("/version/{entityType}/{entityId}") @PreAuthorize("hasAuthority('TENANT_ADMIN')") public List listEntityVersions(@PathVariable EntityType entityType, @@ -67,29 +84,68 @@ public class EntitiesVersionControlController extends BaseController { return versionControlService.listEntityVersions(getTenantId(), entityId, branch, Integer.MAX_VALUE); } + @GetMapping("/version/{entityType}") + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + public List listEntityTypeVersions(@PathVariable EntityType entityType, + @RequestParam String branch) throws Exception { + return versionControlService.listEntityTypeVersions(getTenantId(), entityType, branch, Integer.MAX_VALUE); + } + + @GetMapping("/version") + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + public List listVersions(@RequestParam String branch) throws Exception { + return versionControlService.listVersions(getTenantId(), branch, Integer.MAX_VALUE); + } + + + + @GetMapping("/files/version/{versionId}") + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + public List listFilesAtVersion(@RequestParam String branch, + @PathVariable String versionId) throws Exception { + return versionControlService.listFilesAtVersion(getTenantId(), branch, versionId); + } + @GetMapping("/entity/{entityType}/{entityId}/{versionId}") @PreAuthorize("hasAuthority('TENANT_ADMIN')") public EntityExportData> getEntityAtVersion(@PathVariable EntityType entityType, @PathVariable("entityId") UUID entityUuid, + @RequestParam String branch, @PathVariable String versionId) throws Exception { EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); - return versionControlService.getEntityAtVersion(getTenantId(), entityId, versionId); + return versionControlService.getEntityAtVersion(getTenantId(), entityId, branch, versionId); } @PostMapping("/entity/{entityType}/{entityId}/{versionId}") @PreAuthorize("hasAuthority('TENANT_ADMIN')") public EntityImportResult> loadEntityVersion(@PathVariable EntityType entityType, @PathVariable("entityId") UUID entityUuid, + @RequestParam String branch, @PathVariable String versionId) throws Exception { EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); - return versionControlService.loadEntityVersion(getTenantId(), entityId, versionId); + EntityImportResult> result = versionControlService.loadEntityVersion(getTenantId(), entityId, branch, versionId); + onEntityUpdatedOrCreated(getCurrentUser(), result.getSavedEntity(), result.getOldEntity(), result.getOldEntity() == null); + return result; + } + + @PostMapping("/entity/{versionId}") + @PreAuthorize("hasAuthority('TENANT_ADMIN')") + public List>> loadAllAtVersion(@RequestParam String branch, + @PathVariable String versionId) throws Exception { + SecurityUser user = getCurrentUser(); + List>> resultList = versionControlService.loadAllAtVersion(user.getTenantId(), branch, versionId); + resultList.forEach(result -> { + onEntityUpdatedOrCreated(user, result.getSavedEntity(), result.getOldEntity(), result.getOldEntity() == null); + }); + return resultList; } @GetMapping("/branches") + @PreAuthorize("hasAuthority('TENANT_ADMIN')") public Set getAllowedBranches() throws ThingsboardException { return versionControlService.getAllowedBranches(getTenantId()); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vcs/DefaultEntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vcs/DefaultEntitiesVersionControlService.java index 0d78496f16..32da1309fa 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vcs/DefaultEntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vcs/DefaultEntitiesVersionControlService.java @@ -22,6 +22,7 @@ import com.fasterxml.jackson.databind.SerializationFeature; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; +import org.eclipse.jgit.api.errors.GitAPIException; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; @@ -46,6 +47,8 @@ import org.thingsboard.server.utils.git.Repository; import org.thingsboard.server.utils.git.data.Commit; import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -54,7 +57,9 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; @Service @@ -72,8 +77,8 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont private static final String SETTINGS_KEY = "vc"; private Repository repository; - private final ReentrantLock fetchLock = new ReentrantLock(); - private final Lock writeLock = new ReentrantLock(); + private final Lock fetchLock = new ReentrantLock(); + private final ReadWriteLock repositoryLock = new ReentrantReadWriteLock(); @AfterStartUp public void init() throws Exception { @@ -89,17 +94,9 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont @Scheduled(initialDelay = 10 * 1000, fixedDelay = 10 * 1000) - public void fetch() throws Exception { + private void fetch() throws Exception { if (repository == null) return; - - if (fetchLock.tryLock()) { - try { - log.info("Fetching remote repository"); - repository.fetch(); - } finally { - fetchLock.unlock(); - } - } + tryFetch(); } @@ -118,20 +115,12 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont .exportOutboundRelations(false) .build(); List>> entityDataList = entitiesIds.stream() - .map(entityId -> { - return exportImportService.exportEntity(tenantId, entityId, exportSettings); - }) + .map(entityId -> exportImportService.exportEntity(tenantId, entityId, exportSettings)) .collect(Collectors.toList()); - if (fetchLock.tryLock()) { - try { - repository.fetch(); - } finally { - fetchLock.unlock(); - } - } + tryFetch(); - writeLock.lock(); + repositoryLock.writeLock().lock(); try { if (repository.listBranches().contains(branch)) { repository.checkout(branch); @@ -148,9 +137,9 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont Commit commit = repository.commit(versionName, ".", "Tenant " + tenantId); repository.push(); - return new EntityVersion(commit.getId(), commit.getMessage(), commit.getAuthorName()); + return toVersion(commit); } finally { - writeLock.unlock(); + repositoryLock.writeLock().unlock(); } } @@ -158,38 +147,72 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont @Override public List listEntityVersions(TenantId tenantId, EntityId entityId, String branch, int limit) throws Exception { - checkRepository(); - checkBranch(tenantId, branch); - - return repository.listCommits(branch, getRelativePathForEntity(entityId), limit).stream() - .map(commit -> new EntityVersion(commit.getId(), commit.getMessage(), commit.getAuthorName())) - .collect(Collectors.toList()); + return listVersions(tenantId, branch, getRelativePathForEntity(entityId), limit); } @Override public List listEntityTypeVersions(TenantId tenantId, EntityType entityType, String branch, int limit) throws Exception { - checkRepository(); - checkBranch(tenantId, branch); + return listVersions(tenantId, getRelativePathForEntityType(entityType), limit); + } - return repository.listCommits(branch, getRelativePathForEntityType(entityType), limit).stream() - .map(commit -> new EntityVersion(commit.getId(), commit.getMessage(), commit.getAuthorName())) - .collect(Collectors.toList()); + @Override + public List listVersions(TenantId tenantId, String branch, int limit) throws Exception { + return listVersions(tenantId, branch, null, limit); + } + + private List listVersions(TenantId tenantId, String branch, String path, int limit) throws Exception { + repositoryLock.readLock().lock(); + try { + checkRepository(); + checkBranch(tenantId, branch); + + return repository.listCommits(branch, path, limit).stream() + .map(this::toVersion) + .collect(Collectors.toList()); + + } finally { + repositoryLock.readLock().unlock(); + } } @Override - public , I extends EntityId> EntityExportData getEntityAtVersion(TenantId tenantId, I entityId, String versionId) throws Exception { - checkRepository(); - // FIXME [viacheslav]: validate access + public List listFilesAtVersion(TenantId tenantId, String branch, String versionId) throws Exception { + repositoryLock.readLock().lock(); + try { + if (listVersions(tenantId, branch, Integer.MAX_VALUE).stream() + .noneMatch(version -> version.getId().equals(versionId))) { + throw new IllegalArgumentException("Unknown version"); + } + return repository.listFilesAtCommit(versionId); + } finally { + repositoryLock.readLock().unlock(); + } + } - String entityDataJson = repository.getFileContentAtCommit(getRelativePathForEntity(entityId), versionId); - return JacksonUtil.fromString(entityDataJson, new TypeReference>() {}); + + + @Override + public , I extends EntityId> EntityExportData getEntityAtVersion(TenantId tenantId, I entityId, String branch, String versionId) throws Exception { + repositoryLock.readLock().lock(); + try { + if (listEntityVersions(tenantId, entityId, branch, Integer.MAX_VALUE).stream() + .noneMatch(version -> version.getId().equals(versionId))) { + throw new IllegalArgumentException("Unknown version"); + } + + String entityDataJson = repository.getFileContentAtCommit(getRelativePathForEntity(entityId), versionId); + return parseEntityData(entityDataJson); + } finally { + repositoryLock.readLock().unlock(); + } } + @Override - public , I extends EntityId> EntityImportResult loadEntityVersion(TenantId tenantId, I entityId, String versionId) throws Exception { - EntityExportData entityData = getEntityAtVersion(tenantId, entityId, versionId); + public , I extends EntityId> EntityImportResult loadEntityVersion(TenantId tenantId, I entityId, String branch, String versionId) throws Exception { + EntityExportData entityData = getEntityAtVersion(tenantId, entityId, branch, versionId); return exportImportService.importEntity(tenantId, entityData, EntityImportSettings.builder() .importInboundRelations(false) .importOutboundRelations(false) @@ -197,6 +220,47 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont .build()); } + @Override + public List>> loadAllAtVersion(TenantId tenantId, String branch, String versionId) throws Exception { + repositoryLock.readLock().lock(); + try { + List>> entityDataList = listFilesAtVersion(tenantId, branch, versionId).stream() + .map(entityDataFilePath -> { + String entityDataJson; + try { + entityDataJson = repository.getFileContentAtCommit(entityDataFilePath, versionId); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + return parseEntityData(entityDataJson); + }) + .collect(Collectors.toList()); + + return exportImportService.importEntities(tenantId, entityDataList, EntityImportSettings.builder() + .importInboundRelations(false) + .importOutboundRelations(false) + .updateReferencesToOtherEntities(true) + .build()); + } finally { + repositoryLock.readLock().unlock(); + } + } + + private void tryFetch() throws GitAPIException { + repositoryLock.readLock().lock(); + try { + if (fetchLock.tryLock()) { + try { + log.info("Fetching remote repository"); + repository.fetch(); + } finally { + fetchLock.unlock(); + } + } + } finally { + repositoryLock.readLock().unlock(); + } + } private String getRelativePathForEntity(EntityId entityId) { @@ -210,6 +274,7 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont private void checkBranch(TenantId tenantId, String branch) { + // TODO [viacheslav]: all branches are available by default? if (!getAllowedBranches(tenantId).contains(branch)) { throw new IllegalArgumentException("Tenant does not have access to this branch"); } @@ -222,9 +287,24 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont .orElse(Collections.emptySet()); } + private EntityVersion toVersion(Commit commit) { + return new EntityVersion(commit.getId(), commit.getMessage(), commit.getAuthorName()); + } + + private , I extends EntityId> EntityExportData parseEntityData(String entityDataJson) { + return JacksonUtil.fromString(entityDataJson, new TypeReference>() {}); + } + + + @Override public void saveSettings(EntitiesVersionControlSettings settings) throws Exception { - this.repository = initRepository(settings.getGitSettings()); + repositoryLock.writeLock().lock(); + try { + this.repository = initRepository(settings.getGitSettings()); + } finally { + repositoryLock.writeLock().unlock(); + } AdminSettings adminSettings = Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, SETTINGS_KEY)) .orElseGet(() -> { @@ -244,7 +324,6 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont } - private void checkRepository() { if (repository == null) { throw new IllegalStateException("Repository is not initialized"); @@ -263,12 +342,17 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont } public void resetRepository() throws Exception { - if (this.repository != null) { - FileUtils.deleteDirectory(new File(repository.getDirectory())); - this.repository = null; + repositoryLock.writeLock().lock(); + try { + if (this.repository != null) { + FileUtils.deleteDirectory(new File(repository.getDirectory())); + this.repository = null; + } + EntitiesVersionControlSettings settings = getSettings(); + this.repository = initRepository(settings.getGitSettings()); + } finally { + repositoryLock.writeLock().unlock(); } - EntitiesVersionControlSettings settings = getSettings(); - this.repository = initRepository(settings.getGitSettings()); } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vcs/EntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vcs/EntitiesVersionControlService.java index a9a3c0e3e8..e05ad17826 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vcs/EntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vcs/EntitiesVersionControlService.java @@ -37,10 +37,17 @@ public interface EntitiesVersionControlService { List listEntityTypeVersions(TenantId tenantId, EntityType entityType, String branch, int limit) throws Exception; + List listVersions(TenantId tenantId, String branch, int limit) throws Exception; - , I extends EntityId> EntityExportData getEntityAtVersion(TenantId tenantId, I entityId, String versionId) throws Exception; - , I extends EntityId> EntityImportResult loadEntityVersion(TenantId tenantId, I entityId, String versionId) throws Exception; + List listFilesAtVersion(TenantId tenantId, String branch, String versionId) throws Exception; + + + , I extends EntityId> EntityExportData getEntityAtVersion(TenantId tenantId, I entityId, String branch, String versionId) throws Exception; + + , I extends EntityId> EntityImportResult loadEntityVersion(TenantId tenantId, I entityId, String branch, String versionId) throws Exception; + + List>> loadAllAtVersion(TenantId tenantId, String branch, String versionId) throws Exception; void saveSettings(EntitiesVersionControlSettings settings) throws Exception; diff --git a/application/src/main/java/org/thingsboard/server/utils/git/Repository.java b/application/src/main/java/org/thingsboard/server/utils/git/Repository.java index 8099be8259..23eb7d7db4 100644 --- a/application/src/main/java/org/thingsboard/server/utils/git/Repository.java +++ b/application/src/main/java/org/thingsboard/server/utils/git/Repository.java @@ -113,13 +113,13 @@ public class Repository { } - public List listFilesAtCommit(Commit commit) throws IOException { - return listFilesAtCommit(commit, null); + public List listFilesAtCommit(String commitId) throws IOException { + return listFilesAtCommit(commitId, null); } - public List listFilesAtCommit(Commit commit, String path) throws IOException { + public List listFilesAtCommit(String commitId, String path) throws IOException { List files = new ArrayList<>(); - RevCommit revCommit = resolveCommit(commit.getId()); + RevCommit revCommit = resolveCommit(commitId); try (TreeWalk treeWalk = new TreeWalk(git.getRepository())) { treeWalk.reset(revCommit.getTree().getId()); if (StringUtils.isNotEmpty(path)) { From 073875f406ed4437ec844a3a29a116effba5be44 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 28 Apr 2022 13:26:11 +0300 Subject: [PATCH 36/39] Entities VC improvements and refactoring --- .../EntitiesVersionControlController.java | 272 ++++++------ .../DefaultEntitiesVersionControlService.java | 395 ++++++++++++++++++ .../vc/EntitiesVersionControlService.java | 64 +++ .../data/EntitiesVersionControlSettings.java} | 20 +- .../sync/{vcs => vc}/data/EntityVersion.java | 5 +- .../data/EntityVersionLoadResult.java} | 21 +- .../vc/data/EntityVersionLoadSettings.java} | 9 +- .../vc/data/EntityVersionSaveSettings.java} | 6 +- .../sync/vc/data/VersionedEntityInfo.java} | 11 +- .../DefaultEntitiesVersionControlService.java | 358 ---------------- .../vcs/EntitiesVersionControlService.java | 57 --- .../Repository.java => GitRepository.java} | 79 ++-- 12 files changed, 665 insertions(+), 632 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java rename application/src/main/java/org/thingsboard/server/service/sync/{vcs/data/GitSettings.java => vc/data/EntitiesVersionControlSettings.java} (75%) rename application/src/main/java/org/thingsboard/server/service/sync/{vcs => vc}/data/EntityVersion.java (90%) rename application/src/main/java/org/thingsboard/server/service/sync/{vcs/data/EntitiesVersionControlSettings.java => vc/data/EntityVersionLoadResult.java} (57%) rename application/src/main/java/org/thingsboard/server/{utils/git/data/Commit.java => service/sync/vc/data/EntityVersionLoadSettings.java} (78%) rename application/src/main/java/org/thingsboard/server/{utils/git/data/Branch.java => service/sync/vc/data/EntityVersionSaveSettings.java} (83%) rename application/src/main/java/org/thingsboard/server/{utils/git/data/Diff.java => service/sync/vc/data/VersionedEntityInfo.java} (74%) delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vcs/DefaultEntitiesVersionControlService.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vcs/EntitiesVersionControlService.java rename application/src/main/java/org/thingsboard/server/utils/{git/Repository.java => GitRepository.java} (82%) diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java index 798556998c..c39442341e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java @@ -16,159 +16,139 @@ package org.thingsboard.server.controller; import lombok.RequiredArgsConstructor; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.ExportableEntity; -import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.EntityIdFactory; -import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.importing.EntityImportResult; -import org.thingsboard.server.service.sync.vcs.DefaultEntitiesVersionControlService; -import org.thingsboard.server.service.sync.vcs.data.EntitiesVersionControlSettings; -import org.thingsboard.server.service.sync.vcs.data.EntityVersion; - -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.UUID; -import java.util.stream.Collectors; +import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService; @RestController @RequestMapping("/api/entities/vc") @RequiredArgsConstructor public class EntitiesVersionControlController extends BaseController { - private final DefaultEntitiesVersionControlService versionControlService; - - - - @PostMapping("/version/{entityType}/{entityId}") - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public EntityVersion saveEntityVersion(@PathVariable EntityType entityType, - @PathVariable("entityId") UUID id, - @RequestParam String branch, - @RequestBody String versionName) throws Exception { - EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, id); - return versionControlService.saveEntityVersion(getTenantId(), entityId, branch, versionName); - } - - @PostMapping("/version/{entityType}") - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public EntityVersion saveEntitiesVersion(@PathVariable EntityType entityType, - @RequestParam UUID[] ids, - @RequestParam String branch, - @RequestBody String versionName) throws Exception { - List entitiesIds = Arrays.stream(ids) - .map(id -> EntityIdFactory.getByTypeAndUuid(entityType, id)) - .collect(Collectors.toList()); - return versionControlService.saveEntitiesVersion(getTenantId(), entitiesIds, branch, versionName); - } - - - - @GetMapping("/version/{entityType}/{entityId}") - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public List listEntityVersions(@PathVariable EntityType entityType, - @PathVariable("entityId") UUID entityUuid, - @RequestParam String branch) throws Exception { - EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); - return versionControlService.listEntityVersions(getTenantId(), entityId, branch, Integer.MAX_VALUE); - } - - @GetMapping("/version/{entityType}") - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public List listEntityTypeVersions(@PathVariable EntityType entityType, - @RequestParam String branch) throws Exception { - return versionControlService.listEntityTypeVersions(getTenantId(), entityType, branch, Integer.MAX_VALUE); - } - - @GetMapping("/version") - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public List listVersions(@RequestParam String branch) throws Exception { - return versionControlService.listVersions(getTenantId(), branch, Integer.MAX_VALUE); - } - - - - @GetMapping("/files/version/{versionId}") - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public List listFilesAtVersion(@RequestParam String branch, - @PathVariable String versionId) throws Exception { - return versionControlService.listFilesAtVersion(getTenantId(), branch, versionId); - } - - - - @GetMapping("/entity/{entityType}/{entityId}/{versionId}") - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public EntityExportData> getEntityAtVersion(@PathVariable EntityType entityType, - @PathVariable("entityId") UUID entityUuid, - @RequestParam String branch, - @PathVariable String versionId) throws Exception { - EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); - return versionControlService.getEntityAtVersion(getTenantId(), entityId, branch, versionId); - } - - @PostMapping("/entity/{entityType}/{entityId}/{versionId}") - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public EntityImportResult> loadEntityVersion(@PathVariable EntityType entityType, - @PathVariable("entityId") UUID entityUuid, - @RequestParam String branch, - @PathVariable String versionId) throws Exception { - EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); - EntityImportResult> result = versionControlService.loadEntityVersion(getTenantId(), entityId, branch, versionId); - onEntityUpdatedOrCreated(getCurrentUser(), result.getSavedEntity(), result.getOldEntity(), result.getOldEntity() == null); - return result; - } - - @PostMapping("/entity/{versionId}") - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public List>> loadAllAtVersion(@RequestParam String branch, - @PathVariable String versionId) throws Exception { - SecurityUser user = getCurrentUser(); - List>> resultList = versionControlService.loadAllAtVersion(user.getTenantId(), branch, versionId); - resultList.forEach(result -> { - onEntityUpdatedOrCreated(user, result.getSavedEntity(), result.getOldEntity(), result.getOldEntity() == null); - }); - return resultList; - } - - - - @GetMapping("/branches") - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public Set getAllowedBranches() throws ThingsboardException { - return versionControlService.getAllowedBranches(getTenantId()); - } - - - @PostMapping("/settings") - @PreAuthorize("hasAuthority('SYS_ADMIN')") - public void saveSettings(@RequestBody EntitiesVersionControlSettings settings) throws Exception { - versionControlService.saveSettings(settings); - } - - @GetMapping("/settings") - @PreAuthorize("hasAuthority('SYS_ADMIN')") - public EntitiesVersionControlSettings getSettings() { - return versionControlService.getSettings(); - } - - - - @PostMapping("/repository/reset") - @PreAuthorize("hasAuthority('SYS_ADMIN')") - public void resetLocalRepository() throws Exception { - versionControlService.resetRepository(); - } + private final EntitiesVersionControlService versionControlService; + + + // search request - export request with settings + +// +// @PostMapping("/version/{entityType}/{entityId}") +// @PreAuthorize("hasAuthority('TENANT_ADMIN')") +// public EntityVersion saveEntityVersion(@PathVariable EntityType entityType, +// @PathVariable("entityId") UUID id, +// @RequestParam String branch, +// @RequestBody String versionName) throws Exception { +// EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, id); +// return versionControlService.saveEntityVersion(getTenantId(), entityId, branch, versionName); +// } +// +// @PostMapping("/version/{entityType}") +// @PreAuthorize("hasAuthority('TENANT_ADMIN')") +// public EntityVersion saveEntitiesVersion(@PathVariable EntityType entityType, +// @RequestParam UUID[] ids, +// @RequestParam String branch, +// @RequestBody String versionName) throws Exception { +// List entitiesIds = Arrays.stream(ids) +// .map(id -> EntityIdFactory.getByTypeAndUuid(entityType, id)) +// .collect(Collectors.toList()); +// return versionControlService.saveEntitiesVersion(getTenantId(), entitiesIds, branch, versionName); +// } +// +// +// +// @GetMapping("/version/{entityType}/{entityId}") +// @PreAuthorize("hasAuthority('TENANT_ADMIN')") +// public List listEntityVersions(@PathVariable EntityType entityType, +// @PathVariable("entityId") UUID entityUuid, +// @RequestParam String branch) throws Exception { +// EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); +// return versionControlService.listEntityVersions(getTenantId(), entityId, branch, Integer.MAX_VALUE); +// } +// +// @GetMapping("/version/{entityType}") +// @PreAuthorize("hasAuthority('TENANT_ADMIN')") +// public List listEntityTypeVersions(@PathVariable EntityType entityType, +// @RequestParam String branch) throws Exception { +// return versionControlService.listEntityTypeVersions(getTenantId(), entityType, branch, Integer.MAX_VALUE); +// } +// +// @GetMapping("/version") +// @PreAuthorize("hasAuthority('TENANT_ADMIN')") +// public List listVersions(@RequestParam String branch) throws Exception { +// return versionControlService.listVersions(getTenantId(), branch, Integer.MAX_VALUE); +// } +// +// +// +// @GetMapping("/files/version/{versionId}") +// @PreAuthorize("hasAuthority('TENANT_ADMIN')") +// public List listFilesAtVersion(@RequestParam String branch, +// @PathVariable String versionId) throws Exception { +// return versionControlService.listFilesAtVersion(getTenantId(), branch, versionId); +// } +// +// +// +// @GetMapping("/entity/{entityType}/{entityId}/{versionId}") +// @PreAuthorize("hasAuthority('TENANT_ADMIN')") +// public EntityExportData> getEntityAtVersion(@PathVariable EntityType entityType, +// @PathVariable("entityId") UUID entityUuid, +// @RequestParam String branch, +// @PathVariable String versionId) throws Exception { +// EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); +// return versionControlService.getEntityAtVersion(getTenantId(), entityId, branch, versionId); +// } +// +// @PostMapping("/entity/{entityType}/{entityId}/{versionId}") +// @PreAuthorize("hasAuthority('TENANT_ADMIN')") +// public EntityImportResult> loadEntityVersion(@PathVariable EntityType entityType, +// @PathVariable("entityId") UUID entityUuid, +// @RequestParam String branch, +// @PathVariable String versionId) throws Exception { +// EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); +// EntityImportResult> result = versionControlService.loadEntityVersion(getTenantId(), entityId, branch, versionId); +// onEntityUpdatedOrCreated(getCurrentUser(), result.getSavedEntity(), result.getOldEntity(), result.getOldEntity() == null); +// return result; +// } +// +// @PostMapping("/entity/{versionId}") +// @PreAuthorize("hasAuthority('TENANT_ADMIN')") +// public List>> loadAllAtVersion(@RequestParam String branch, +// @PathVariable String versionId) throws Exception { +// SecurityUser user = getCurrentUser(); +// List>> resultList = versionControlService.loadAllAtVersion(user.getTenantId(), branch, versionId); +// resultList.forEach(result -> { +// onEntityUpdatedOrCreated(user, result.getSavedEntity(), result.getOldEntity(), result.getOldEntity() == null); +// }); +// return resultList; +// } +// +// +// +// @GetMapping("/branches") +// @PreAuthorize("hasAuthority('TENANT_ADMIN')") +// public Set getAllowedBranches() throws ThingsboardException { +// return versionControlService.getAllowedBranches(getTenantId()); +// } +// +// +// @PostMapping("/settings") +// @PreAuthorize("hasAuthority('SYS_ADMIN')") +// public void saveSettings(@RequestBody EntitiesVersionControlSettings settings) throws Exception { +// versionControlService.saveSettings(settings); +// } +// +// @GetMapping("/settings") +// @PreAuthorize("hasAuthority('SYS_ADMIN')") +// public EntitiesVersionControlSettings getSettings() { +// return versionControlService.getSettings(); +// } +// +// +// +// @PostMapping("/repository/reset") +// @PreAuthorize("hasAuthority('SYS_ADMIN')") +// public void resetLocalRepository() throws Exception { +// versionControlService.resetRepository(); +// } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java new file mode 100644 index 0000000000..a298d64ccc --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java @@ -0,0 +1,395 @@ +/** + * 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.sync.vc; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.SerializationFeature; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.settings.AdminSettingsService; +import org.thingsboard.server.queue.util.AfterStartUp; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.sync.EntitiesExportImportService; +import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; +import org.thingsboard.server.service.sync.importing.data.EntityImportResult; +import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; +import org.thingsboard.server.service.sync.vc.data.EntitiesVersionControlSettings; +import org.thingsboard.server.service.sync.vc.data.VersionedEntityInfo; +import org.thingsboard.server.service.sync.vc.data.EntityVersion; +import org.thingsboard.server.service.sync.vc.data.EntityVersionLoadResult; +import org.thingsboard.server.service.sync.vc.data.EntityVersionLoadSettings; +import org.thingsboard.server.service.sync.vc.data.EntityVersionSaveSettings; +import org.thingsboard.server.utils.GitRepository; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.stream.Collectors; + +@Service +@TbCoreComponent +@RequiredArgsConstructor +@Slf4j +public class DefaultEntitiesVersionControlService implements EntitiesVersionControlService { + + private final EntitiesExportImportService exportImportService; + + private GitRepository repository; + private final ReadWriteLock repositoryLock = new ReentrantReadWriteLock(); + + private ScheduledExecutorService fetchExecutor; + private ScheduledFuture fetchTask; + + private final AdminSettingsService adminSettingsService; + private static final String SETTINGS_KEY = "vc"; + private final ObjectWriter jsonWriter = new ObjectMapper().writer(SerializationFeature.INDENT_OUTPUT); + + + @AfterStartUp + public void init() { + EntitiesVersionControlSettings settings = getSettings(); + if (settings != null) { + try { + initRepository(settings); + } catch (Exception e) { + log.debug("Failed to init repository", e); + } + } + + int fetchPeriod = settings == null || settings.getFetchPeriod() == 0 ? 10 : settings.getFetchPeriod(); + fetchExecutor = Executors.newSingleThreadScheduledExecutor(); + fetchTask = scheduleFetch(fetchPeriod); + } + + + @Override + public EntityVersion saveEntityVersion(SecurityUser user, EntityId entityId, String branch, String versionName, EntityVersionSaveSettings settings) throws Exception { + return saveEntitiesVersion(user, List.of(entityId), branch, versionName, settings); + } + + @Override + public EntityVersion saveEntitiesVersion(SecurityUser user, List entitiesIds, String branch, String versionName, EntityVersionSaveSettings settings) throws Exception { + repositoryLock.writeLock().lock(); + try { + checkRepository(); + checkBranch(user.getTenantId(), branch); + + List> entityDataList = new ArrayList<>(); + EntityExportSettings exportSettings = EntityExportSettings.builder() + .exportRelations(settings.isSaveRelations()) + .build(); + for (EntityId entityId : entitiesIds) { + EntityExportData> entityData = exportImportService.exportEntity(user, entityId, exportSettings); + entityDataList.add(entityData); + } + + fetch(); + if (repository.listBranches().contains(branch)) { + repository.checkout(branch); + repository.merge(branch); + } else { + repository.createAndCheckoutOrphanBranch(branch); + } + + for (EntityExportData entityData : entityDataList) { + String entityDataJson = jsonWriter.writeValueAsString(entityData); + FileUtils.write(new File(repository.getDirectory() + "/" + getRelativePath(entityData.getEntityType(), + entityData.getEntity().getId().toString())), entityDataJson, StandardCharsets.UTF_8); + } + + GitRepository.Commit commit = repository.commit(versionName, "."); + repository.push(); + return toVersion(commit); + } finally { + repositoryLock.writeLock().unlock(); + } + } + + + @Override + public List listEntityVersions(TenantId tenantId, String branch, EntityId externalId) throws Exception { + return listVersions(tenantId, branch, getRelativePath(externalId.getEntityType(), externalId.getId().toString())); + } + + @Override + public List listEntityTypeVersions(TenantId tenantId, String branch, EntityType entityType) throws Exception { + return listVersions(tenantId, branch, getRelativePath(entityType, null)); + } + + @Override + public List listVersions(TenantId tenantId, String branch) throws Exception { + return listVersions(tenantId, branch, null); + } + + private List listVersions(TenantId tenantId, String branch, String path) throws Exception { + repositoryLock.readLock().lock(); + try { + checkRepository(); + checkBranch(tenantId, branch); + + return repository.listCommits(branch, path, Integer.MAX_VALUE).stream() + .map(this::toVersion) + .collect(Collectors.toList()); + } finally { + repositoryLock.readLock().unlock(); + } + } + + + @Override + public List listEntitiesAtVersion(TenantId tenantId, EntityType entityType, String branch, String versionId) throws Exception { + return listEntitiesAtVersion(tenantId, branch, versionId, getRelativePath(entityType, null)); + } + + @Override + public List listAllEntitiesAtVersion(TenantId tenantId, String branch, String versionId) throws Exception { + return listEntitiesAtVersion(tenantId, branch, versionId, null); + } + + private List listEntitiesAtVersion(TenantId tenantId, String branch, String versionId, String path) throws Exception { + repositoryLock.readLock().lock(); + try { + checkRepository(); + checkBranch(tenantId, branch); + checkVersion(tenantId, branch, versionId, path); + + return repository.listFilesAtCommit(versionId, path).stream() + .map(filePath -> { + EntityId entityId = fromRelativePath(filePath); + EntityExportData entityData = getEntityDataAtVersion(entityId, versionId); + + VersionedEntityInfo info = new VersionedEntityInfo(); + info.setExternalId(entityId); + info.setEntityName(entityData.getEntity().getName()); + return info; + }) + .collect(Collectors.toList()); + } finally { + repositoryLock.readLock().unlock(); + } + } + + + @Override + public EntityVersionLoadResult loadEntityVersion(SecurityUser user, EntityId externalId, String branch, String versionId, EntityVersionLoadSettings settings) throws Exception { + return loadAtVersion(user, branch, versionId, getRelativePath(externalId.getEntityType(), externalId.getId().toString()), settings).get(0); + } + + @Override + public List loadEntityTypeVersion(SecurityUser user, EntityType entityType, String branch, String versionId, EntityVersionLoadSettings settings) throws Exception { + return loadAtVersion(user, branch, versionId, getRelativePath(entityType, null), settings); + } + + @Override + public List loadAllAtVersion(SecurityUser user, String branch, String versionId, EntityVersionLoadSettings settings) throws Exception { + return loadAtVersion(user, branch, versionId, null, settings); + } + + private List loadAtVersion(SecurityUser user, String branch, String versionId, String path, EntityVersionLoadSettings settings) throws Exception { + List> entityDataList = new ArrayList<>(); + repositoryLock.readLock().lock(); + try { + for (VersionedEntityInfo info : listEntitiesAtVersion(user.getTenantId(), branch, versionId, path)) { + EntityExportData entityData = getEntityDataAtVersion(info.getExternalId(), versionId); + entityDataList.add(entityData); + } + } finally { + repositoryLock.readLock().unlock(); + } + + EntityImportSettings importSettings = EntityImportSettings.builder() + .updateRelations(settings.isLoadRelations()) + .findExistingByName(settings.isFindExistingEntityByName()) + .build(); + List> importResults = exportImportService.importEntities(user, entityDataList, importSettings); + + return importResults.stream() + .map(importResult -> EntityVersionLoadResult.builder() + .previousEntityVersion(importResult.getOldEntity()) + .newEntityVersion(importResult.getSavedEntity()) + .entityType(importResult.getEntityType()) + .build()) + .collect(Collectors.toList()); + } + + @SneakyThrows + private EntityExportData getEntityDataAtVersion(EntityId externalId, String versionId) { + repositoryLock.readLock().lock(); + try { + String entityDataJson = repository.getFileContentAtCommit(getRelativePath(externalId.getEntityType(), externalId.getId().toString()), versionId); + return JacksonUtil.fromString(entityDataJson, EntityExportData.class); + } finally { + repositoryLock.readLock().unlock(); + } + } + + + private void fetch() throws GitAPIException { + repositoryLock.writeLock().lock(); + try { + repository.fetch(); + } finally { + repositoryLock.writeLock().unlock(); + } + } + + private ScheduledFuture scheduleFetch(int fetchPeriod) { + return fetchExecutor.scheduleWithFixedDelay(() -> { + if (repository == null) return; + try { + fetch(); + } catch (Exception e) { + log.error("Failed to fetch remote repository", e); + } + }, fetchPeriod, fetchPeriod, TimeUnit.SECONDS); + } + + + private void checkVersion(TenantId tenantId, String branch, String versionId, String path) throws Exception { + if (listVersions(tenantId, branch, path).stream().noneMatch(version -> version.getId().equals(versionId))) { + throw new IllegalArgumentException("Version not found"); + } + } + + @Override + public List listAllowedBranches(TenantId tenantId) { + return Optional.ofNullable(getSettings()) + .flatMap(settings -> Optional.ofNullable(settings.getTenantsAllowedBranches())) + .flatMap(tenantsAllowedBranches -> Optional.ofNullable(tenantsAllowedBranches.get(tenantId.getId()))) + .orElse(Collections.emptyList()); + } + + private void checkBranch(TenantId tenantId, String branch) { + if (!listAllowedBranches(tenantId).contains(branch)) { + throw new IllegalArgumentException("Tenant does not have access to the branch"); + } + } + + + private void checkRepository() { + if (repository == null) { + throw new IllegalStateException("Repository is not initialized"); + } + } + + private void initRepository(EntitiesVersionControlSettings settings) throws Exception { + repositoryLock.writeLock().lock(); + try { + if (Files.exists(Path.of(settings.getRepositoryDirectory()))) { + this.repository = GitRepository.open(settings.getRepositoryDirectory(), settings.getUsername(), settings.getPassword()); + } else { + Files.createDirectories(Path.of(settings.getRepositoryDirectory())); + this.repository = GitRepository.clone(settings.getRepositoryUri(), settings.getRepositoryDirectory(), + settings.getUsername(), settings.getPassword()); + } + } finally { + repositoryLock.writeLock().unlock(); + } + } + + private void clearRepository() throws IOException { + repositoryLock.writeLock().lock(); + try { + if (repository != null) { + FileUtils.deleteDirectory(new File(repository.getDirectory())); + repository = null; + } + } finally { + repositoryLock.writeLock().unlock(); + } + } + + + @SneakyThrows + @Override + public void saveSettings(EntitiesVersionControlSettings settings) { + AdminSettings adminSettings = Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "vc")) + .orElseGet(() -> { + AdminSettings newAdminSettings = new AdminSettings(); + newAdminSettings.setKey(SETTINGS_KEY); + return newAdminSettings; + }); + adminSettings.setJsonValue(JacksonUtil.valueToTree(settings)); + adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings); + + repositoryLock.writeLock().lock(); + try { + clearRepository(); + initRepository(settings); + } finally { + repositoryLock.writeLock().unlock(); + } + + if (settings.getFetchPeriod() != 0) { + fetchTask.cancel(true); + fetchTask = scheduleFetch(settings.getFetchPeriod()); + } + } + + @Override + public EntitiesVersionControlSettings getSettings() { + return Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "vc")) + .map(adminSettings -> JacksonUtil.treeToValue(adminSettings.getJsonValue(), EntitiesVersionControlSettings.class)) + .orElse(null); + } + + + private EntityVersion toVersion(GitRepository.Commit commit) { + return new EntityVersion(commit.getId(), commit.getMessage()); + } + + private String getRelativePath(EntityType entityType, String entityId) { + String path = entityType.name().toLowerCase(); + if (entityId != null) { + path += "/" + entityId + ".json"; + } + return path; + } + + private EntityId fromRelativePath(String path) { + EntityType entityType = EntityType.valueOf(StringUtils.substringBefore(path, "/")); + String entityId = StringUtils.substringBetween(path, "/", ".json"); + return EntityIdFactory.getByTypeAndUuid(entityType, entityId); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java new file mode 100644 index 0000000000..435c661b02 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java @@ -0,0 +1,64 @@ +/** + * 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.sync.vc; + +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.service.security.model.SecurityUser; +import org.thingsboard.server.service.sync.vc.data.EntitiesVersionControlSettings; +import org.thingsboard.server.service.sync.vc.data.VersionedEntityInfo; +import org.thingsboard.server.service.sync.vc.data.EntityVersion; +import org.thingsboard.server.service.sync.vc.data.EntityVersionLoadResult; +import org.thingsboard.server.service.sync.vc.data.EntityVersionLoadSettings; +import org.thingsboard.server.service.sync.vc.data.EntityVersionSaveSettings; + +import java.util.List; + +public interface EntitiesVersionControlService { + + EntityVersion saveEntityVersion(SecurityUser user, EntityId entityId, String branch, String versionName, EntityVersionSaveSettings settings) throws Exception; + + EntityVersion saveEntitiesVersion(SecurityUser user, List entitiesIds, String branch, String versionName, EntityVersionSaveSettings settings) throws Exception; + + + List listEntityVersions(TenantId tenantId, String branch, EntityId externalId) throws Exception; + + List listEntityTypeVersions(TenantId tenantId, String branch, EntityType entityType) throws Exception; + + List listVersions(TenantId tenantId, String branch) throws Exception; + + + List listEntitiesAtVersion(TenantId tenantId, EntityType entityType, String branch, String versionId) throws Exception; // will be good to return entity name also + + List listAllEntitiesAtVersion(TenantId tenantId, String branch, String versionId) throws Exception; + + + EntityVersionLoadResult loadEntityVersion(SecurityUser user, EntityId externalId, String branch, String versionId, EntityVersionLoadSettings settings) throws Exception; + + List loadEntityTypeVersion(SecurityUser user, EntityType entityType, String branch, String versionId, EntityVersionLoadSettings settings) throws Exception; + + List loadAllAtVersion(SecurityUser user, String branch, String versionId, EntityVersionLoadSettings settings) throws Exception; + + + List listAllowedBranches(TenantId tenantId); + + + void saveSettings(EntitiesVersionControlSettings settings); + + EntitiesVersionControlSettings getSettings(); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vcs/data/GitSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntitiesVersionControlSettings.java similarity index 75% rename from application/src/main/java/org/thingsboard/server/service/sync/vcs/data/GitSettings.java rename to application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntitiesVersionControlSettings.java index 0d97bed2d1..06e0a9c7aa 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vcs/data/GitSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntitiesVersionControlSettings.java @@ -13,20 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.vcs.data; +package org.thingsboard.server.service.sync.vc.data; -import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; -import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Map; +import java.util.UUID; @Data -@AllArgsConstructor -@NoArgsConstructor -@Builder -public class GitSettings { - private String repositoryUri; +public class EntitiesVersionControlSettings { private String repositoryDirectory; + private String repositoryUri; private String username; private String password; + + private int fetchPeriod; + + private Map> tenantsAllowedBranches; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vcs/data/EntityVersion.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersion.java similarity index 90% rename from application/src/main/java/org/thingsboard/server/service/sync/vcs/data/EntityVersion.java rename to application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersion.java index 42a2e2f555..3547f89e3c 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vcs/data/EntityVersion.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersion.java @@ -13,17 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.vcs.data; +package org.thingsboard.server.service.sync.vc.data; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data -@NoArgsConstructor @AllArgsConstructor +@NoArgsConstructor public class EntityVersion { private String id; private String name; - private String authorName; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vcs/data/EntitiesVersionControlSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionLoadResult.java similarity index 57% rename from application/src/main/java/org/thingsboard/server/service/sync/vcs/data/EntitiesVersionControlSettings.java rename to application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionLoadResult.java index 6a46606845..4cf0ee2b0f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vcs/data/EntitiesVersionControlSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionLoadResult.java @@ -13,16 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.vcs.data; +package org.thingsboard.server.service.sync.vc.data; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; - -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.ExportableEntity; @Data -public class EntitiesVersionControlSettings { - private Map> allowedBranches; - private GitSettings gitSettings; +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class EntityVersionLoadResult { + private ExportableEntity newEntityVersion; + private ExportableEntity previousEntityVersion; + private EntityType entityType; } diff --git a/application/src/main/java/org/thingsboard/server/utils/git/data/Commit.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionLoadSettings.java similarity index 78% rename from application/src/main/java/org/thingsboard/server/utils/git/data/Commit.java rename to application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionLoadSettings.java index 7567dba1c5..842f6ddd4e 100644 --- a/application/src/main/java/org/thingsboard/server/utils/git/data/Commit.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionLoadSettings.java @@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.utils.git.data; +package org.thingsboard.server.service.sync.vc.data; import lombok.Data; @Data -public class Commit { - private final String id; - private final String message; - private final String authorName; +public class EntityVersionLoadSettings { + private boolean loadRelations; + private boolean findExistingEntityByName; } diff --git a/application/src/main/java/org/thingsboard/server/utils/git/data/Branch.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionSaveSettings.java similarity index 83% rename from application/src/main/java/org/thingsboard/server/utils/git/data/Branch.java rename to application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionSaveSettings.java index a458f9f5aa..1fc36463ba 100644 --- a/application/src/main/java/org/thingsboard/server/utils/git/data/Branch.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionSaveSettings.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.utils.git.data; +package org.thingsboard.server.service.sync.vc.data; import lombok.Data; @Data -public class Branch { - private final String shortName; +public class EntityVersionSaveSettings { + private boolean saveRelations; } diff --git a/application/src/main/java/org/thingsboard/server/utils/git/data/Diff.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionedEntityInfo.java similarity index 74% rename from application/src/main/java/org/thingsboard/server/utils/git/data/Diff.java rename to application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionedEntityInfo.java index 7572a003d2..97bb6ada12 100644 --- a/application/src/main/java/org/thingsboard/server/utils/git/data/Diff.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionedEntityInfo.java @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.utils.git.data; +package org.thingsboard.server.service.sync.vc.data; import lombok.Data; +import org.thingsboard.server.common.data.id.EntityId; @Data -public class Diff { - private final String type; - private final String oldPath; - private final String newPath; +public class VersionedEntityInfo { + private EntityId externalId; + private String entityName; + // etc.. } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vcs/DefaultEntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vcs/DefaultEntitiesVersionControlService.java deleted file mode 100644 index 32da1309fa..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/vcs/DefaultEntitiesVersionControlService.java +++ /dev/null @@ -1,358 +0,0 @@ -/** - * 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.sync.vcs; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectWriter; -import com.fasterxml.jackson.databind.SerializationFeature; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.io.FileUtils; -import org.eclipse.jgit.api.errors.GitAPIException; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.AdminSettings; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.ExportableEntity; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.settings.AdminSettingsService; -import org.thingsboard.server.dao.tenant.TenantService; -import org.thingsboard.server.queue.util.AfterStartUp; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.sync.EntitiesExportImportService; -import org.thingsboard.server.service.sync.exporting.EntityExportSettings; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.importing.EntityImportResult; -import org.thingsboard.server.service.sync.importing.EntityImportSettings; -import org.thingsboard.server.service.sync.vcs.data.EntitiesVersionControlSettings; -import org.thingsboard.server.service.sync.vcs.data.EntityVersion; -import org.thingsboard.server.service.sync.vcs.data.GitSettings; -import org.thingsboard.server.utils.git.Repository; -import org.thingsboard.server.utils.git.data.Commit; - -import java.io.File; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.stream.Collectors; - -@Service -@TbCoreComponent -@RequiredArgsConstructor -@Slf4j -public class DefaultEntitiesVersionControlService implements EntitiesVersionControlService { - // TODO [viacheslav]: start up only on one of the cores - - private final TenantService tenantService; - private final EntitiesExportImportService exportImportService; - private final AdminSettingsService adminSettingsService; - - private final ObjectWriter jsonWriter = new ObjectMapper().writer(SerializationFeature.INDENT_OUTPUT); - private static final String SETTINGS_KEY = "vc"; - - private Repository repository; - private final Lock fetchLock = new ReentrantLock(); - private final ReadWriteLock repositoryLock = new ReentrantReadWriteLock(); - - @AfterStartUp - public void init() throws Exception { - try { - EntitiesVersionControlSettings settings = getSettings(); - if (settings != null && settings.getGitSettings() != null) { - this.repository = initRepository(settings.getGitSettings()); - } - } catch (Exception e) { - log.error("Failed to initialize entities version control service", e); - } - } - - - @Scheduled(initialDelay = 10 * 1000, fixedDelay = 10 * 1000) - private void fetch() throws Exception { - if (repository == null) return; - tryFetch(); - } - - - @Override - public EntityVersion saveEntityVersion(TenantId tenantId, EntityId entityId, String branch, String versionName) throws Exception { - return saveEntitiesVersion(tenantId, List.of(entityId), branch, versionName); - } - - @Override - public EntityVersion saveEntitiesVersion(TenantId tenantId, List entitiesIds, String branch, String versionName) throws Exception { - checkRepository(); - checkBranch(tenantId, branch); - - EntityExportSettings exportSettings = EntityExportSettings.builder() - .exportInboundRelations(false) - .exportOutboundRelations(false) - .build(); - List>> entityDataList = entitiesIds.stream() - .map(entityId -> exportImportService.exportEntity(tenantId, entityId, exportSettings)) - .collect(Collectors.toList()); - - tryFetch(); - - repositoryLock.writeLock().lock(); - try { - if (repository.listBranches().contains(branch)) { - repository.checkout(branch); - repository.merge(branch); - } else { - repository.createAndCheckoutOrphanBranch(branch); - } - - for (EntityExportData> entityData : entityDataList) { - String entityDataJson = jsonWriter.writeValueAsString(entityData); - FileUtils.write(new File(repository.getDirectory() + "/" + getRelativePathForEntity(entityData.getEntity().getId())), - entityDataJson, StandardCharsets.UTF_8); - } - - Commit commit = repository.commit(versionName, ".", "Tenant " + tenantId); - repository.push(); - return toVersion(commit); - } finally { - repositoryLock.writeLock().unlock(); - } - } - - - - @Override - public List listEntityVersions(TenantId tenantId, EntityId entityId, String branch, int limit) throws Exception { - return listVersions(tenantId, branch, getRelativePathForEntity(entityId), limit); - } - - @Override - public List listEntityTypeVersions(TenantId tenantId, EntityType entityType, String branch, int limit) throws Exception { - return listVersions(tenantId, getRelativePathForEntityType(entityType), limit); - } - - @Override - public List listVersions(TenantId tenantId, String branch, int limit) throws Exception { - return listVersions(tenantId, branch, null, limit); - } - - private List listVersions(TenantId tenantId, String branch, String path, int limit) throws Exception { - repositoryLock.readLock().lock(); - try { - checkRepository(); - checkBranch(tenantId, branch); - - return repository.listCommits(branch, path, limit).stream() - .map(this::toVersion) - .collect(Collectors.toList()); - - } finally { - repositoryLock.readLock().unlock(); - } - } - - - - @Override - public List listFilesAtVersion(TenantId tenantId, String branch, String versionId) throws Exception { - repositoryLock.readLock().lock(); - try { - if (listVersions(tenantId, branch, Integer.MAX_VALUE).stream() - .noneMatch(version -> version.getId().equals(versionId))) { - throw new IllegalArgumentException("Unknown version"); - } - return repository.listFilesAtCommit(versionId); - } finally { - repositoryLock.readLock().unlock(); - } - } - - - - @Override - public , I extends EntityId> EntityExportData getEntityAtVersion(TenantId tenantId, I entityId, String branch, String versionId) throws Exception { - repositoryLock.readLock().lock(); - try { - if (listEntityVersions(tenantId, entityId, branch, Integer.MAX_VALUE).stream() - .noneMatch(version -> version.getId().equals(versionId))) { - throw new IllegalArgumentException("Unknown version"); - } - - String entityDataJson = repository.getFileContentAtCommit(getRelativePathForEntity(entityId), versionId); - return parseEntityData(entityDataJson); - } finally { - repositoryLock.readLock().unlock(); - } - } - - - @Override - public , I extends EntityId> EntityImportResult loadEntityVersion(TenantId tenantId, I entityId, String branch, String versionId) throws Exception { - EntityExportData entityData = getEntityAtVersion(tenantId, entityId, branch, versionId); - return exportImportService.importEntity(tenantId, entityData, EntityImportSettings.builder() - .importInboundRelations(false) - .importOutboundRelations(false) - .updateReferencesToOtherEntities(true) - .build()); - } - - @Override - public List>> loadAllAtVersion(TenantId tenantId, String branch, String versionId) throws Exception { - repositoryLock.readLock().lock(); - try { - List>> entityDataList = listFilesAtVersion(tenantId, branch, versionId).stream() - .map(entityDataFilePath -> { - String entityDataJson; - try { - entityDataJson = repository.getFileContentAtCommit(entityDataFilePath, versionId); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - return parseEntityData(entityDataJson); - }) - .collect(Collectors.toList()); - - return exportImportService.importEntities(tenantId, entityDataList, EntityImportSettings.builder() - .importInboundRelations(false) - .importOutboundRelations(false) - .updateReferencesToOtherEntities(true) - .build()); - } finally { - repositoryLock.readLock().unlock(); - } - } - - private void tryFetch() throws GitAPIException { - repositoryLock.readLock().lock(); - try { - if (fetchLock.tryLock()) { - try { - log.info("Fetching remote repository"); - repository.fetch(); - } finally { - fetchLock.unlock(); - } - } - } finally { - repositoryLock.readLock().unlock(); - } - } - - - private String getRelativePathForEntity(EntityId entityId) { - return getRelativePathForEntityType(entityId.getEntityType()) - + "/" + entityId.getId() + ".json"; - } - - private String getRelativePathForEntityType(EntityType entityType) { - return entityType.name().toLowerCase(); - } - - - private void checkBranch(TenantId tenantId, String branch) { - // TODO [viacheslav]: all branches are available by default? - if (!getAllowedBranches(tenantId).contains(branch)) { - throw new IllegalArgumentException("Tenant does not have access to this branch"); - } - } - - public Set getAllowedBranches(TenantId tenantId) { - return Optional.ofNullable(getSettings()) - .flatMap(settings -> Optional.ofNullable(settings.getAllowedBranches())) - .flatMap(tenantsAllowedBranches -> Optional.ofNullable(tenantsAllowedBranches.get(tenantId.getId()))) - .orElse(Collections.emptySet()); - } - - private EntityVersion toVersion(Commit commit) { - return new EntityVersion(commit.getId(), commit.getMessage(), commit.getAuthorName()); - } - - private , I extends EntityId> EntityExportData parseEntityData(String entityDataJson) { - return JacksonUtil.fromString(entityDataJson, new TypeReference>() {}); - } - - - - @Override - public void saveSettings(EntitiesVersionControlSettings settings) throws Exception { - repositoryLock.writeLock().lock(); - try { - this.repository = initRepository(settings.getGitSettings()); - } finally { - repositoryLock.writeLock().unlock(); - } - - AdminSettings adminSettings = Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, SETTINGS_KEY)) - .orElseGet(() -> { - AdminSettings newSettings = new AdminSettings(); - newSettings.setKey(SETTINGS_KEY); - return newSettings; - }); - adminSettings.setJsonValue(JacksonUtil.valueToTree(settings)); - adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings); - } - - @Override - public EntitiesVersionControlSettings getSettings() { - return Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, SETTINGS_KEY)) - .map(adminSettings -> JacksonUtil.treeToValue(adminSettings.getJsonValue(), EntitiesVersionControlSettings.class)) - .orElse(null); - } - - - private void checkRepository() { - if (repository == null) { - throw new IllegalStateException("Repository is not initialized"); - } - } - - private static Repository initRepository(GitSettings gitSettings) throws Exception { - if (Files.exists(Path.of(gitSettings.getRepositoryDirectory()))) { - return Repository.open(gitSettings.getRepositoryDirectory(), - gitSettings.getUsername(), gitSettings.getPassword()); - } else { - Files.createDirectories(Path.of(gitSettings.getRepositoryDirectory())); - return Repository.clone(gitSettings.getRepositoryUri(), gitSettings.getRepositoryDirectory(), - gitSettings.getUsername(), gitSettings.getPassword()); - } - } - - public void resetRepository() throws Exception { - repositoryLock.writeLock().lock(); - try { - if (this.repository != null) { - FileUtils.deleteDirectory(new File(repository.getDirectory())); - this.repository = null; - } - EntitiesVersionControlSettings settings = getSettings(); - this.repository = initRepository(settings.getGitSettings()); - } finally { - repositoryLock.writeLock().unlock(); - } - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vcs/EntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vcs/EntitiesVersionControlService.java deleted file mode 100644 index e05ad17826..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/vcs/EntitiesVersionControlService.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * 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.sync.vcs; - -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.ExportableEntity; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.importing.EntityImportResult; -import org.thingsboard.server.service.sync.vcs.data.EntitiesVersionControlSettings; -import org.thingsboard.server.service.sync.vcs.data.EntityVersion; - -import java.util.List; - -public interface EntitiesVersionControlService { - - EntityVersion saveEntityVersion(TenantId tenantId, EntityId entityId, String branch, String versionName) throws Exception; - - EntityVersion saveEntitiesVersion(TenantId tenantId, List entitiesIds, String branch, String versionName) throws Exception; - - - List listEntityVersions(TenantId tenantId, EntityId entityId, String branch, int limit) throws Exception; - - List listEntityTypeVersions(TenantId tenantId, EntityType entityType, String branch, int limit) throws Exception; - - List listVersions(TenantId tenantId, String branch, int limit) throws Exception; - - - List listFilesAtVersion(TenantId tenantId, String branch, String versionId) throws Exception; - - - , I extends EntityId> EntityExportData getEntityAtVersion(TenantId tenantId, I entityId, String branch, String versionId) throws Exception; - - , I extends EntityId> EntityImportResult loadEntityVersion(TenantId tenantId, I entityId, String branch, String versionId) throws Exception; - - List>> loadAllAtVersion(TenantId tenantId, String branch, String versionId) throws Exception; - - - void saveSettings(EntitiesVersionControlSettings settings) throws Exception; - - EntitiesVersionControlSettings getSettings(); - -} diff --git a/application/src/main/java/org/thingsboard/server/utils/git/Repository.java b/application/src/main/java/org/thingsboard/server/utils/GitRepository.java similarity index 82% rename from application/src/main/java/org/thingsboard/server/utils/git/Repository.java rename to application/src/main/java/org/thingsboard/server/utils/GitRepository.java index 23eb7d7db4..205bbca50e 100644 --- a/application/src/main/java/org/thingsboard/server/utils/git/Repository.java +++ b/application/src/main/java/org/thingsboard/server/utils/GitRepository.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.utils.git; +package org.thingsboard.server.utils; import com.google.common.collect.Streams; +import lombok.Data; import lombok.Getter; import org.apache.commons.lang3.StringUtils; import org.eclipse.jgit.api.Git; @@ -32,19 +33,21 @@ import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.filter.RevFilter; import org.eclipse.jgit.transport.CredentialsProvider; +import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.PathFilter; -import org.thingsboard.server.utils.git.data.Commit; +import java.io.File; import java.io.IOException; +import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -public class Repository { +public class GitRepository { private final Git git; private final CredentialsProvider credentialsProvider; @@ -52,13 +55,13 @@ public class Repository { @Getter private final String directory; - private Repository(Git git, CredentialsProvider credentialsProvider, String directory) { + private GitRepository(Git git, CredentialsProvider credentialsProvider, String directory) { this.git = git; this.credentialsProvider = credentialsProvider; this.directory = directory; } - public static Repository clone(String uri, String directory, + public static GitRepository clone(String uri, String directory, String username, String password) throws GitAPIException { CredentialsProvider credentialsProvider = newCredentialsProvider(username, password); Git git = Git.cloneRepository() @@ -67,12 +70,12 @@ public class Repository { .setNoCheckout(true) .setCredentialsProvider(credentialsProvider) .call(); - return new Repository(git, credentialsProvider, directory); + return new GitRepository(git, credentialsProvider, directory); } - public static Repository open(String directory, String username, String password) throws IOException { + public static GitRepository open(String directory, String username, String password) throws IOException { Git git = Git.open(new java.io.File(directory)); - return new Repository(git, newCredentialsProvider(username, password), directory); + return new GitRepository(git, newCredentialsProvider(username, password), directory); } @@ -81,6 +84,20 @@ public class Repository { .setRemoveDeletedRefs(true)); } + public void checkout(String branch) throws GitAPIException { + execute(git.checkout() + .setName(branch)); + } + + public void merge(String branch) throws IOException, GitAPIException { + ObjectId branchId = resolve("origin/" + branch); + if (branchId == null) { + throw new IllegalArgumentException("Branch not found"); + } + execute(git.merge() + .include(branchId)); + } + public List listBranches() throws GitAPIException { return execute(git.branchList() @@ -92,12 +109,12 @@ public class Repository { } - public List listCommits(String branchName, int limit) throws IOException, GitAPIException { - return listCommits(branchName, null, limit); + public List listCommits(String branch, int limit) throws IOException, GitAPIException { + return listCommits(branch, null, limit); } - public List listCommits(String branchName, String path, int limit) throws IOException, GitAPIException { - ObjectId branchId = resolve("origin/" + branchName); + public List listCommits(String branch, String path, int limit) throws IOException, GitAPIException { + ObjectId branchId = resolve("origin/" + branch); if (branchId == null) { throw new IllegalArgumentException("Branch not found"); } @@ -150,20 +167,6 @@ public class Repository { } - public void checkout(String branchName) throws GitAPIException { - execute(git.checkout() - .setName(branchName)); - } - - public void merge(String branchName) throws IOException, GitAPIException { - ObjectId branchId = resolve("origin/" + branchName); - if (branchId == null) { - throw new IllegalArgumentException("Branch not found"); - } - execute(git.merge() - .include(branchId)); - } - public void createAndCheckoutOrphanBranch(String name) throws GitAPIException { execute(git.checkout() .setOrphan(true) @@ -177,15 +180,11 @@ public class Repository { execute(git.clean()); } - public void clean() throws GitAPIException { - execute(git.clean().setCleanDirectories(true)); - } - public Commit commit(String message, String filePattern, String author) throws GitAPIException { - execute(git.add().addFilepattern(filePattern)); + public Commit commit(String message, String filesPattern) throws GitAPIException { + execute(git.add().addFilepattern(filesPattern)); RevCommit revCommit = execute(git.commit() - .setMessage(message) - .setAuthor(author, author)); + .setMessage(message)); // TODO [viacheslav]: set configurable author for commit return toCommit(revCommit); } @@ -239,12 +238,8 @@ public class Repository { } private , T> T execute(C command) throws GitAPIException { - if (command instanceof TransportCommand) { + if (command instanceof TransportCommand && credentialsProvider != null) { ((TransportCommand) command).setCredentialsProvider(credentialsProvider); -// SshSessionFactory sshSessionFactory = SshSessionFactory.getInstance(); -// transportCommand.setTransportConfigCallback(transport -> { -// ((SshTransport) transport).setSshSessionFactory(sshSessionFactory); -// }); } return command.call(); } @@ -253,4 +248,12 @@ public class Repository { return new UsernamePasswordCredentialsProvider(username, password); } + + @Data + public static class Commit { + private final String id; + private final String message; + private final String authorName; + } + } From 074ea8af6cdd9d723f57dd55615cf602d16738a8 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Mon, 16 May 2022 16:09:00 +0300 Subject: [PATCH 37/39] VC refactoring --- .../server/controller/AssetController.java | 4 +- .../server/controller/DeviceController.java | 4 +- .../server/controller/EdgeController.java | 4 +- .../EntitiesExportImportController.java | 206 -------- .../EntitiesVersionControlController.java | 293 ++++++----- .../service/asset/AssetBulkImportService.java | 4 +- .../device/DeviceBulkImportService.java | 4 +- .../service/edge/EdgeBulkImportService.java | 4 +- .../DefaultEntitiesExportImportService.java | 30 +- .../EntitiesExportImportService.java | 10 +- .../DefaultExportableEntitiesService.java | 56 ++- .../exporting/EntityExportService.java | 6 +- .../exporting/ExportableEntitiesService.java | 12 +- .../exporting/data/DeviceExportData.java | 2 +- .../exporting/data/EntityExportData.java | 2 +- .../exporting/data}/EntityExportSettings.java | 2 +- .../exporting/data/RuleChainExportData.java | 2 +- .../impl/BaseEntityExportService.java | 6 +- .../impl/DefaultEntityExportService.java | 10 +- .../exporting/impl/DeviceExportService.java | 4 +- .../impl/RuleChainExportService.java | 4 +- .../importing/EntityImportService.java | 8 +- .../csv/AbstractBulkImportService.java | 9 +- .../importing/csv/BulkImportColumnType.java | 2 +- .../importing/csv/BulkImportRequest.java | 2 +- .../importing/csv/BulkImportResult.java | 2 +- .../importing/csv/ImportedEntityInfo.java | 2 +- .../importing/data/EntityImportResult.java | 2 +- .../importing/data/EntityImportSettings.java | 2 +- .../importing/impl/AssetImportService.java | 4 +- .../impl/BaseEntityImportService.java | 12 +- .../importing/impl/CustomerImportService.java | 4 +- .../impl/DashboardImportService.java | 6 +- .../importing/impl/DeviceImportService.java | 4 +- .../impl/DeviceProfileImportService.java | 4 +- .../impl/RuleChainImportService.java | 6 +- .../importing/data/request/ImportRequest.java | 32 -- .../DefaultEntitiesVersionControlService.java | 463 +++++++++++------- .../vc/EntitiesVersionControlService.java | 27 +- .../data/EntitiesVersionControlSettings.java | 10 +- .../sync/vc/data/VersionCreationResult.java | 11 + ...LoadResult.java => VersionLoadResult.java} | 8 +- ...iesByCustomFilterVersionCreateConfig.java} | 14 +- ...tiesByCustomQueryVersionCreateConfig.java} | 12 +- .../EntityListVersionCreateConfig.java} | 10 +- .../EntityTypeVersionCreateConfig.java} | 14 +- .../SingleEntityVersionCreateConfig.java} | 10 +- .../request/create/VersionCreateConfig.java} | 18 +- .../create/VersionCreateConfigType.java} | 4 +- .../create/VersionCreateRequest.java} | 14 +- .../load/EntityTypeVersionLoadConfig.java | 12 + .../load/EntityTypeVersionLoadRequest.java | 20 + .../request/load/EntityVersionLoadConfig.java | 11 + .../load/SingleEntityVersionLoadRequest.java | 20 + .../load/VersionLoadRequest.java} | 11 +- .../request/load/VersionLoadRequestType.java | 6 + .../server/utils/GitRepository.java | 39 +- ...aseEntitiesExportImportControllerTest.java | 20 +- ...EntitiesExportImportControllerSqlTest.java | 53 +- .../org/thingsboard/server/dao/DaoUtil.java | 20 +- .../server/dao/ExportableEntityDao.java | 4 + .../server/dao/sql/asset/JpaAssetDao.java | 5 + .../dao/sql/customer/JpaCustomerDao.java | 5 + .../sql/dashboard/DashboardRepository.java | 4 + .../dao/sql/dashboard/JpaDashboardDao.java | 7 + .../server/dao/sql/device/JpaDeviceDao.java | 5 + .../dao/sql/device/JpaDeviceProfileDao.java | 5 + .../server/dao/sql/rule/JpaRuleChainDao.java | 5 + 68 files changed, 860 insertions(+), 777 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/DefaultEntitiesExportImportService.java (84%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/EntitiesExportImportService.java (75%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/exporting/DefaultExportableEntitiesService.java (77%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/exporting/EntityExportService.java (81%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/exporting/ExportableEntitiesService.java (74%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/exporting/data/DeviceExportData.java (93%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/exporting/data/EntityExportData.java (96%) rename application/src/main/java/org/thingsboard/server/service/sync/{exporting/data/request => exportimport/exporting/data}/EntityExportSettings.java (92%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/exporting/data/RuleChainExportData.java (93%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/exporting/impl/BaseEntityExportService.java (86%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/exporting/impl/DefaultEntityExportService.java (89%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/exporting/impl/DeviceExportService.java (91%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/exporting/impl/RuleChainExportService.java (91%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/importing/EntityImportService.java (78%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/importing/csv/AbstractBulkImportService.java (97%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/importing/csv/BulkImportColumnType.java (96%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/importing/csv/BulkImportRequest.java (93%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/importing/csv/BulkImportResult.java (93%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/importing/csv/ImportedEntityInfo.java (91%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/importing/data/EntityImportResult.java (95%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/importing/data/EntityImportSettings.java (92%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/importing/impl/AssetImportService.java (93%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/importing/impl/BaseEntityImportService.java (95%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/importing/impl/CustomerImportService.java (93%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/importing/impl/DashboardImportService.java (95%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/importing/impl/DeviceImportService.java (94%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/importing/impl/DeviceProfileImportService.java (95%) rename application/src/main/java/org/thingsboard/server/service/sync/{ => exportimport}/importing/impl/RuleChainImportService.java (95%) delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/importing/data/request/ImportRequest.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionCreationResult.java rename application/src/main/java/org/thingsboard/server/service/sync/vc/data/{EntityVersionLoadResult.java => VersionLoadResult.java} (81%) rename application/src/main/java/org/thingsboard/server/service/sync/{exporting/data/request/CustomEntityFilterExportRequest.java => vc/data/request/create/EntitiesByCustomFilterVersionCreateConfig.java} (69%) rename application/src/main/java/org/thingsboard/server/service/sync/{exporting/data/request/CustomEntityQueryExportRequest.java => vc/data/request/create/EntitiesByCustomQueryVersionCreateConfig.java} (72%) rename application/src/main/java/org/thingsboard/server/service/sync/{exporting/data/request/EntityListExportRequest.java => vc/data/request/create/EntityListVersionCreateConfig.java} (73%) rename application/src/main/java/org/thingsboard/server/service/sync/{exporting/data/request/EntityTypeExportRequest.java => vc/data/request/create/EntityTypeVersionCreateConfig.java} (74%) rename application/src/main/java/org/thingsboard/server/service/sync/{exporting/data/request/SingleEntityExportRequest.java => vc/data/request/create/SingleEntityVersionCreateConfig.java} (77%) rename application/src/main/java/org/thingsboard/server/service/sync/{exporting/data/request/ExportRequest.java => vc/data/request/create/VersionCreateConfig.java} (58%) rename application/src/main/java/org/thingsboard/server/service/sync/{exporting/data/request/ExportRequestType.java => vc/data/request/create/VersionCreateConfigType.java} (87%) rename application/src/main/java/org/thingsboard/server/service/sync/vc/data/{EntityVersionLoadSettings.java => request/create/VersionCreateRequest.java} (73%) create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadConfig.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadRequest.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityVersionLoadConfig.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/SingleEntityVersionLoadRequest.java rename application/src/main/java/org/thingsboard/server/service/sync/vc/data/{EntityVersionSaveSettings.java => request/load/VersionLoadRequest.java} (74%) create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadRequestType.java diff --git a/application/src/main/java/org/thingsboard/server/controller/AssetController.java b/application/src/main/java/org/thingsboard/server/controller/AssetController.java index e48f2dcf23..66b570cf1d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AssetController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AssetController.java @@ -54,8 +54,8 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.asset.AssetBulkImportService; -import org.thingsboard.server.service.sync.importing.csv.BulkImportRequest; -import org.thingsboard.server.service.sync.importing.csv.BulkImportResult; +import org.thingsboard.server.service.sync.exportimport.importing.csv.BulkImportRequest; +import org.thingsboard.server.service.sync.exportimport.importing.csv.BulkImportResult; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 03f0996ea9..4b6eca7879 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -74,8 +74,8 @@ import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.device.DeviceBulkImportService; import org.thingsboard.server.service.gateway_device.GatewayNotificationsService; -import org.thingsboard.server.service.sync.importing.csv.BulkImportRequest; -import org.thingsboard.server.service.sync.importing.csv.BulkImportResult; +import org.thingsboard.server.service.sync.exportimport.importing.csv.BulkImportRequest; +import org.thingsboard.server.service.sync.exportimport.importing.csv.BulkImportResult; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; diff --git a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java index 8f45427603..948a380fcd 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EdgeController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EdgeController.java @@ -55,8 +55,8 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.edge.EdgeBulkImportService; -import org.thingsboard.server.service.sync.importing.csv.BulkImportRequest; -import org.thingsboard.server.service.sync.importing.csv.BulkImportResult; +import org.thingsboard.server.service.sync.exportimport.importing.csv.BulkImportRequest; +import org.thingsboard.server.service.sync.exportimport.importing.csv.BulkImportResult; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java deleted file mode 100644 index c91fd0c292..0000000000 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesExportImportController.java +++ /dev/null @@ -1,206 +0,0 @@ -/** - * 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.controller; - -import io.swagger.annotations.ApiOperation; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.EntitiesExportImportService; -import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; -import org.thingsboard.server.service.sync.exporting.data.request.ExportRequest; -import org.thingsboard.server.service.sync.importing.data.EntityImportResult; -import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; -import org.thingsboard.server.service.sync.importing.data.request.ImportRequest; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE; - -@RestController -@RequestMapping("/api/entities") -@TbCoreComponent -@RequiredArgsConstructor -@Slf4j -public class EntitiesExportImportController extends BaseController { - - private final EntitiesExportImportService exportImportService; - private final ExportableEntitiesService exportableEntitiesService; - - - @ApiOperation(value = "Export entities by request", notes = "" + - "Takes export request and returns list of export data for each entity found by request. " + - "Supported entity types for export, hence for import, are **DEVICE**, **DEVICE_PROFILE**, **ASSET**, " + - "**CUSTOMER**, **RULE_CHAIN** and **DASHBOARD**." + NEW_LINE + - "For each type of export request, you can set some export settings: \n" + - "- **exportRelations** - whether to export inbound and outbound relations for an entity " + - "(only relations of type group COMMON can be exported)" + NEW_LINE + - "Supported export requests:\n" + - "- **SINGLE_ENTITY**:" + NEW_LINE + - " To export a single entity by id. Example:" + NEW_LINE + - "```\n{\n \"type\": \"SINGLE_ENTITY\",\n \"entityId\": {\n \"entityType\": \"DEVICE\",\n \"id\": \"2eb16d70-989d-11ec-93b5-6de6c2b68078\"\n },\n \"exportSettings\": {\n \"exportRelations\": false\n }\n}\n```" + NEW_LINE + - "- **ENTITY_LIST**:" + NEW_LINE + - " To export a list of entities by their ids. Example:" + NEW_LINE + - "```\n{\n \"type\": \"ENTITY_LIST\",\n \"entitiesIds\": [\n {\n \"entityType\": \"DEVICE\",\n \"id\": \"2eb16d70-989d-11ec-93b5-6de6c2b68078\"\n },\n {\n \"entityType\": \"ASSET\",\n \"id\": \"2f0a3bd0-989d-11ec-93b5-6de6c2b68078\"\n }\n ],\n \"exportSettings\": {\n \"exportRelations\": true\n }\n}\n```" + NEW_LINE + - "- **ENTITY_TYPE**:" + NEW_LINE + - " To export entities of specified entity type. You need to specify page size, " + - "and may specify page index and customer id (to limit the list of entities to the ones owned by a customer). " + - "Entities are ordered by created time descendingly. Example:" + NEW_LINE + - "```\n{\n \"type\": \"ENTITY_TYPE\",\n \"entityType\": \"ASSET\",\n \"page\": 0,\n \"pageSize\": 100,\n \"customerId\": \"2eb16d70-989d-11ec-93b5-6de6c2b68078\"\n}\n```" + NEW_LINE + - "- **CUSTOM_ENTITY_FILTER**:" + NEW_LINE + - " To export entities by custom entity filter. The order used is the same as for ENTITY_TYPE export request. Example:" + NEW_LINE + - "```\n{\n \"type\": \"CUSTOM_ENTITY_FILTER\",\n \"filter\": {\n \"type\": \"deviceType\",\n \"deviceType\": \"Thermostats\",\n \"deviceNameFilter\": \"\"\n },\n \"page\": 0,\n \"pageSize\": 100,\n \"customerId\": null,\n \"exportSettings\": {\n \"exportRelations\": false\n }\n}\n```" + NEW_LINE + - "- **CUSTOM_ENTITY_QUERY**:" + NEW_LINE + - " To export entities by custom entity query. Example: " + NEW_LINE + - "```\n{\n \"type\": \"CUSTOM_ENTITY_QUERY\",\n \"query\": {\n \"entityFilter\": {\n \"type\": \"entityType\",\n \"entityType\": \"DEVICE\"\n },\n \"pageLink\": {\n \"page\": 0,\n \"pageSize\": 200,\n \"textSearch\": \"THB_\",\n \"sortOrder\": {\n \"key\": {\n \"type\": \"ENTITY_FIELD\",\n \"key\": \"name\"\n },\n \"direction\": \"DESC\"\n }\n },\n \"entityFields\": [\n {\n \"type\": \"ENTITY_FIELD\",\n \"key\": \"name\"\n }\n ],\n \"latestValues\": [\n {\n \"type\": \"SERVER_ATTRIBUTE\",\n \"key\": \"lastActivityTime\"\n }\n ],\n \"keyFilters\": [\n {\n \"key\": {\n \"type\": \"SERVER_ATTRIBUTE\",\n \"key\": \"lastActivityTime\"\n },\n \"valueType\": \"NUMERIC\",\n \"predicate\": {\n \"type\": \"NUMERIC\",\n \"operation\": \"GREATER\",\n \"value\": {\n \"defaultValue\": 0\n }\n }\n }\n ]\n },\n \"customerId\": null,\n \"exportSettings\": {\n \"exportRelations\": false\n }\n}\n```" + NEW_LINE + - "Mostly, export data of an entity contains the whole entity itself and its relations " + - "(if option to export relations was enabled):" + NEW_LINE + - "```\n[\n {\n \"entityType\": \"ASSET\",\n \"entity\": {\n \"id\": { ... },\n \"createdTime\": 1648204424029,\n \"additionalInfo\": {\n \"description\": \"\"\n },\n \"tenantId\": { ... },\n \"customerId\": { ... },\n \"name\": \"Asset 1\",\n \"type\": \"A\",\n ...\n },\n \"relations\": [\n {\n \"from\": {\n \"entityType\": \"ASSET\",\n \"id\": ...\n },\n \"to\": {\n \"entityType\": \"DEVICE\",\n \"id\": ...\n },\n \"type\": \"Contains\",\n \"typeGroup\": \"COMMON\",\n \"additionalInfo\": {\n \"a\": \"b\"\n }\n }\n ]\n }\n]\n```" + NEW_LINE + - "For devices, export data will additionally contain device's credentials; for rule chains - its metadata:" + NEW_LINE + - "```\n[\n {\n \"entityType\": \"DEVICE\",\n \"entity\": { ... },\n \"credentials\": {\n \"id\": { ... },\n \"createdTime\": 1648829321209,\n \"deviceId\": { ... },\n \"credentialsType\": \"ACCESS_TOKEN\",\n \"credentialsId\": \"5cZEDo45KGW7JgVNv4Ko\",\n \"credentialsValue\": null\n }\n }\n]\n```" + NEW_LINE + - "```\n[\n {\n \"entityType\": \"RULE_CHAIN\",\n \"entity\": {\n \"id\": { ... },\n \"createdTime\": 1646056614257,\n \"additionalInfo\": null,\n \"tenantId\": { ... },\n \"name\": \"Rule Chain 2\",\n \"type\": \"CORE\",\n \"firstRuleNodeId\": { ... },\n \"root\": false,\n ...\n },\n \"metaData\": {\n \"ruleChainId\": { ... },\n \"firstNodeIndex\": 7,\n \"nodes\": [ ... ],\n \"connections\": [ ... ],\n \"ruleChainConnections\": null\n }\n }\n]\n```" + NEW_LINE + - "Returned export data is to be used later for import request." + NEW_LINE + - "If any entity found by request is of unsupported type - an error will be returned.\n" + - "Also, if a user does not have a READ permission for an entity (or, if relations are exported, for a bounded entity), " + - "access will be denied." + - ControllerConstants.TENANT_AUTHORITY_PARAGRAPH) - @PostMapping("/export") - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public List> exportEntities(@RequestBody ExportRequest exportRequest) throws ThingsboardException { - SecurityUser user = getCurrentUser(); - try { - return exportEntitiesByRequest(user, exportRequest); - } catch (Exception e) { - log.warn("Failed to export entities for request {}", exportRequest, e); - throw handleException(e); - } - } - - @ApiOperation(value = "Export entities by multiple requests", notes = "" + - "The API behaviour is the same as for exporting entities by single request, " + - "except that this method takes an array of export requests as a request body." + NEW_LINE + - "Example:" + NEW_LINE + - "```\n[\n {\n \"type\": \"SINGLE_ENTITY\",\n \"entityId\": {\n \"entityType\": \"DEVICE_PROFILE\",\n \"id\": \"5f9eda10-b442-11ec-bbf5-adec34031568\"\n }\n },\n {\n \"type\": \"CUSTOM_ENTITY_FILTER\",\n \"filter\": {\n \"type\": \"deviceType\",\n \"deviceType\": \"thermostat\",\n \"deviceNameFilter\": \"\"\n },\n \"pageSize\": 1000\n },\n {\n \"type\": \"ENTITY_TYPE\",\n \"entityType\": \"ASSET\",\n \"pageSize\": 1000,\n \"exportSettings\": {\n \"exportRelations\": true\n }\n },\n {\n \"type\": \"ENTITY_LIST\",\n \"entitiesIds\": [\n {\n \"entityType\": \"RULE_CHAIN\",\n \"id\": \"2ef13590-989d-11ec-93b5-6de6c2b68078\"\n },\n {\n \"entityType\": \"RULE_CHAIN\",\n \"id\": \"e7311ec0-b442-11ec-bbf5-adec34031568\"\n }\n ]\n }\n]\n```" + - ControllerConstants.TENANT_AUTHORITY_PARAGRAPH) - @PostMapping(value = "/export", params = {"multiple"}) - @PreAuthorize("hasAuthority('TENANT_ADMIN')") - public List> exportEntities(@RequestBody List exportRequests) throws ThingsboardException { - SecurityUser user = getCurrentUser(); - try { - List> result = new ArrayList<>(); - for (ExportRequest exportRequest : exportRequests) { - List> exportDataList = exportEntitiesByRequest(user, exportRequest); - result.addAll(exportDataList); - } - return result; - } catch (Exception e) { - log.warn("Failed to export entities for requests {}", exportRequests, e); - throw handleException(e); - } - } - - private List> exportEntitiesByRequest(SecurityUser user, ExportRequest exportRequest) throws ThingsboardException { - List entities = exportableEntitiesService.findEntitiesForRequest(user.getTenantId(), exportRequest); - - EntityExportSettings exportSettings = exportRequest.getExportSettings(); - if (exportSettings == null) { - exportSettings = EntityExportSettings.builder() - .exportRelations(false) - .build(); - } - - List> exportDataList = new ArrayList<>(); - for (EntityId entityId : entities) { - EntityExportData exportData = exportImportService.exportEntity(user, entityId, exportSettings); - exportDataList.add(exportData); - } - return exportDataList; - } - - - @ApiOperation(value = "Import entities by request", notes = "" + - "Takes import request and returns the list of import results. " + - "Import request must contain the list of export data and might contain import settings. " + NEW_LINE + - "The method creates an entity if it is new in the scope of a tenant, or otherwise updates an existing one. " + - "On entity import request, we first try to find an entity within a tenant that has externalId equal " + - "to the id in the export data. If the platform fails to do that, we then search for an entity with " + - "regular (internal) id like the one in the export data (this is useful in case we are exporting and " + - "importing entities within the same tenant). Then, if we still haven't found any entity, if findExistingByName " + - "option of the EntityImportSettings is enabled, we will search for the one by its name (this is also useful " + - "for avoiding conflicts with default device profile or Root Rule Chain when importing all entities from another " + - "tenant). After, if the exported entity is new for this tenant, we simply save it with external id " + - "from the export data, and also create relations if any (if updateRelations option is enabled). " + - "Otherwise, we will reset all fields of the existing entity to the ones from the export data and save it, " + - "and also will update the list of relations (remove the ones that aren't present in the export data, " + - "and update or create others), if updateRelations option is enabled." + NEW_LINE + - "If an entity contains references to some other entities, like device references certain device profile, " + - "we will find this other entity within the tenant by this principle: look for an entity with " + - "such external id, or otherwise, internal id. This requires referenced entities to be imported " + - "before the referencing entity (if we are importing to another tenant). So, when receiving the list " + - "of entities' export data for import, we first try to fix the data order to import 'standalone' entities first." + NEW_LINE + - "As for relations importing, they are processed after all entities in the import batch are already saved, " + - "and the internal id of a bounded entity is found with the regular principle." + NEW_LINE + - "Import of all entities and their relations from the import request is processed in the single transaction, " + - "and so everything will be rolled back if the platform fails to e.g. find internal entity by external id. \n" + - "Example of import request:\n" + - "```\n{\n \"importSettings\": {\n \"findExistingByName\": false,\n \"updateRelations\": false\n },\n \"exportDataList\": [\n {\n \"entityType\": \"DEVICE_PROFILE\",\n \"entity\": {\n \"id\": {\n \"entityType\": \"DEVICE_PROFILE\",\n \"id\": \"f84363d0-b442-11ec-bbf5-adec34031568\"\n },\n \"createdTime\": 1649096026765,\n \"tenantId\": {\n \"entityType\": \"TENANT\",\n \"id\": \"4c9001b0-b442-11ec-bbf5-adec34031568\"\n },\n \"name\": \"Profile 1\",\n ...\n }\n },\n {\n \"entityType\": \"DEVICE\",\n \"entity\": {\n \"id\": {\n \"entityType\": \"DEVICE\",\n \"id\": \"98161420-b4ca-11ec-ab0c-e7744c90d468\"\n },\n \"createdTime\": 1649154276962,\n \"tenantId\": {\n \"entityType\": \"TENANT\",\n \"id\": \"4c9001b0-b442-11ec-bbf5-adec34031568\"\n },\n \"customerId\": {\n \"entityType\": \"CUSTOMER\",\n \"id\": \"13814000-1dd2-11b2-8080-808080808080\"\n },\n \"name\": \"Device 1\",\n \"type\": \"Profile 1\",\n \"label\": \"v1.0\",\n \"deviceProfileId\": {\n \"entityType\": \"DEVICE_PROFILE\",\n \"id\": \"f84363d0-b442-11ec-bbf5-adec34031568\"\n },\n ...\n },\n \"credentials\": {\n \"id\": {\n \"id\": \"981e0360-b4ca-11ec-ab0c-e7744c90d468\"\n },\n \"createdTime\": 1649154277014,\n \"deviceId\": {\n \"entityType\": \"DEVICE\",\n \"id\": \"98161420-b4ca-11ec-ab0c-e7744c90d468\"\n },\n \"credentialsType\": \"ACCESS_TOKEN\",\n \"credentialsId\": \"sGExNdnl71uKmkNvtNdp\",\n \"credentialsValue\": null\n }\n }\n ]\n}\n```" + NEW_LINE + - "The response contains a list of EntityImportResult which has values of savedEntity and oldEntity:\n" + - "```\n[\n {\n \"savedEntity\": {\n \"id\": {\n \"entityType\": \"ASSET\",\n \"id\": \"d73d7690-b4e6-11ec-b9eb-0562e1a20a1b\"\n },\n \"createdTime\": 1649166408825,\n \"additionalInfo\": {\n \"description\": \"\"\n },\n \"tenantId\": {\n \"entityType\": \"TENANT\",\n \"id\": \"c0b2e4f0-b4e6-11ec-b9eb-0562e1a20a1b\"\n },\n \"name\": \"Asset 1\",\n \"type\": \"A\",\n \"label\": \"v2.0\",\n ...\n \"externalId\": {\n \"entityType\": \"ASSET\",\n \"id\": \"6b03ab20-989e-11ec-b446-89df822d7fa2\"\n }\n },\n \"oldEntity\": {\n \"id\": {\n \"entityType\": \"ASSET\",\n \"id\": \"d73d7690-b4e6-11ec-b9eb-0562e1a20a1b\"\n },\n \"createdTime\": 1649166408825,\n \"tenantId\": {\n \"entityType\": \"TENANT\",\n \"id\": \"c0b2e4f0-b4e6-11ec-b9eb-0562e1a20a1b\"\n },\n \"name\": \"Asset 1\",\n \"type\": \"A\",\n \"label\": \"v1.0\",\n ...\n \"externalId\": null\n },\n \"entityType\": \"ASSET\"\n },\n {\n \"savedEntity\": {\n \"id\": {\n \"entityType\": \"ASSET\",\n \"id\": \"387213f0-b4ea-11ec-abfc-6dcc6508d0b5\"\n },\n \"createdTime\": 1649167860399,\n \"tenantId\": {\n \"entityType\": \"TENANT\",\n \"id\": \"c0b2e4f0-b4e6-11ec-b9eb-0562e1a20a1b\"\n },\n \"name\": \"Asset 2\",\n \"type\": \"B\",\n ...\n \"externalId\": {\n \"entityType\": \"ASSET\",\n \"id\": \"0b9ea0d0-ac27-11ec-a2a6-89d15eae3b21\"\n }\n },\n \"oldEntity\": null,\n \"entityType\": \"ASSET\"\n }\n]\n```") - @PostMapping("/import") - public List> importEntities(@RequestBody ImportRequest importRequest) throws ThingsboardException { - SecurityUser user = getCurrentUser(); - try { - EntityImportSettings importSettings = importRequest.getImportSettings(); - if (importSettings == null) { - importSettings = EntityImportSettings.builder() - .findExistingByName(false) - .updateRelations(false) - .build(); - } - - List> importResults = exportImportService.importEntities(user, importRequest.getExportDataList(), importSettings); - - importResults.stream() - .map(EntityImportResult::getSendEventsCallback) - .filter(Objects::nonNull) - .forEach(sendEventsCallback -> { - try { - sendEventsCallback.run(); - } catch (Exception e) { - log.error("Failed to send event for entity", e); - } - }); - - return importResults; - } catch (Exception e) { - log.warn("Failed to import entities for request {}", importRequest, e); - throw handleException(e); - } - } - -} diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java index c39442341e..0625dbeb38 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java @@ -15,140 +15,189 @@ */ package org.thingsboard.server.controller; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.EntityIdFactory; +import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService; +import org.thingsboard.server.service.sync.vc.data.EntitiesVersionControlSettings; +import org.thingsboard.server.service.sync.vc.data.EntityVersion; +import org.thingsboard.server.service.sync.vc.data.VersionCreationResult; +import org.thingsboard.server.service.sync.vc.data.VersionLoadResult; +import org.thingsboard.server.service.sync.vc.data.VersionedEntityInfo; +import org.thingsboard.server.service.sync.vc.data.request.create.VersionCreateRequest; +import org.thingsboard.server.service.sync.vc.data.request.load.VersionLoadRequest; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; @RestController @RequestMapping("/api/entities/vc") +@PreAuthorize("hasAuthority('TENANT_ADMIN')") @RequiredArgsConstructor public class EntitiesVersionControlController extends BaseController { private final EntitiesVersionControlService versionControlService; - // search request - export request with settings - -// -// @PostMapping("/version/{entityType}/{entityId}") -// @PreAuthorize("hasAuthority('TENANT_ADMIN')") -// public EntityVersion saveEntityVersion(@PathVariable EntityType entityType, -// @PathVariable("entityId") UUID id, -// @RequestParam String branch, -// @RequestBody String versionName) throws Exception { -// EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, id); -// return versionControlService.saveEntityVersion(getTenantId(), entityId, branch, versionName); -// } -// -// @PostMapping("/version/{entityType}") -// @PreAuthorize("hasAuthority('TENANT_ADMIN')") -// public EntityVersion saveEntitiesVersion(@PathVariable EntityType entityType, -// @RequestParam UUID[] ids, -// @RequestParam String branch, -// @RequestBody String versionName) throws Exception { -// List entitiesIds = Arrays.stream(ids) -// .map(id -> EntityIdFactory.getByTypeAndUuid(entityType, id)) -// .collect(Collectors.toList()); -// return versionControlService.saveEntitiesVersion(getTenantId(), entitiesIds, branch, versionName); -// } -// -// -// -// @GetMapping("/version/{entityType}/{entityId}") -// @PreAuthorize("hasAuthority('TENANT_ADMIN')") -// public List listEntityVersions(@PathVariable EntityType entityType, -// @PathVariable("entityId") UUID entityUuid, -// @RequestParam String branch) throws Exception { -// EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); -// return versionControlService.listEntityVersions(getTenantId(), entityId, branch, Integer.MAX_VALUE); -// } -// -// @GetMapping("/version/{entityType}") -// @PreAuthorize("hasAuthority('TENANT_ADMIN')") -// public List listEntityTypeVersions(@PathVariable EntityType entityType, -// @RequestParam String branch) throws Exception { -// return versionControlService.listEntityTypeVersions(getTenantId(), entityType, branch, Integer.MAX_VALUE); -// } -// -// @GetMapping("/version") -// @PreAuthorize("hasAuthority('TENANT_ADMIN')") -// public List listVersions(@RequestParam String branch) throws Exception { -// return versionControlService.listVersions(getTenantId(), branch, Integer.MAX_VALUE); -// } -// -// -// -// @GetMapping("/files/version/{versionId}") -// @PreAuthorize("hasAuthority('TENANT_ADMIN')") -// public List listFilesAtVersion(@RequestParam String branch, -// @PathVariable String versionId) throws Exception { -// return versionControlService.listFilesAtVersion(getTenantId(), branch, versionId); -// } -// -// -// -// @GetMapping("/entity/{entityType}/{entityId}/{versionId}") -// @PreAuthorize("hasAuthority('TENANT_ADMIN')") -// public EntityExportData> getEntityAtVersion(@PathVariable EntityType entityType, -// @PathVariable("entityId") UUID entityUuid, -// @RequestParam String branch, -// @PathVariable String versionId) throws Exception { -// EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); -// return versionControlService.getEntityAtVersion(getTenantId(), entityId, branch, versionId); -// } -// -// @PostMapping("/entity/{entityType}/{entityId}/{versionId}") -// @PreAuthorize("hasAuthority('TENANT_ADMIN')") -// public EntityImportResult> loadEntityVersion(@PathVariable EntityType entityType, -// @PathVariable("entityId") UUID entityUuid, -// @RequestParam String branch, -// @PathVariable String versionId) throws Exception { -// EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, entityUuid); -// EntityImportResult> result = versionControlService.loadEntityVersion(getTenantId(), entityId, branch, versionId); -// onEntityUpdatedOrCreated(getCurrentUser(), result.getSavedEntity(), result.getOldEntity(), result.getOldEntity() == null); -// return result; -// } -// -// @PostMapping("/entity/{versionId}") -// @PreAuthorize("hasAuthority('TENANT_ADMIN')") -// public List>> loadAllAtVersion(@RequestParam String branch, -// @PathVariable String versionId) throws Exception { -// SecurityUser user = getCurrentUser(); -// List>> resultList = versionControlService.loadAllAtVersion(user.getTenantId(), branch, versionId); -// resultList.forEach(result -> { -// onEntityUpdatedOrCreated(user, result.getSavedEntity(), result.getOldEntity(), result.getOldEntity() == null); -// }); -// return resultList; -// } -// -// -// -// @GetMapping("/branches") -// @PreAuthorize("hasAuthority('TENANT_ADMIN')") -// public Set getAllowedBranches() throws ThingsboardException { -// return versionControlService.getAllowedBranches(getTenantId()); -// } -// -// -// @PostMapping("/settings") -// @PreAuthorize("hasAuthority('SYS_ADMIN')") -// public void saveSettings(@RequestBody EntitiesVersionControlSettings settings) throws Exception { -// versionControlService.saveSettings(settings); -// } -// -// @GetMapping("/settings") -// @PreAuthorize("hasAuthority('SYS_ADMIN')") -// public EntitiesVersionControlSettings getSettings() { -// return versionControlService.getSettings(); -// } -// -// -// -// @PostMapping("/repository/reset") -// @PreAuthorize("hasAuthority('SYS_ADMIN')") -// public void resetLocalRepository() throws Exception { -// versionControlService.resetRepository(); -// } + @PostMapping("/version") + public VersionCreationResult saveEntitiesVersion(@RequestBody VersionCreateRequest request) throws ThingsboardException { + SecurityUser user = getCurrentUser(); + try { + return versionControlService.saveEntitiesVersion(user, request); + } catch (Exception e) { + throw handleException(e); + } + } + + + @GetMapping("/version/{branch}/{entityType}/{externalEntityUuid}") + public List listEntityVersions(@PathVariable String branch, + @PathVariable EntityType entityType, + @PathVariable UUID externalEntityUuid) throws ThingsboardException { + try { + EntityId externalEntityId = EntityIdFactory.getByTypeAndUuid(entityType, externalEntityUuid); + return versionControlService.listEntityVersions(getTenantId(), branch, externalEntityId); + } catch (Exception e) { + throw handleException(e); + } + } + + @GetMapping("/version/{branch}/{entityType}") + public List listEntityTypeVersions(@PathVariable String branch, + @PathVariable EntityType entityType) throws ThingsboardException { + try { + return versionControlService.listEntityTypeVersions(getTenantId(), branch, entityType); + } catch (Exception e) { + throw handleException(e); + } + } + + @GetMapping("/version/{branch}") + public List listVersions(@PathVariable String branch) throws ThingsboardException { + try { + return versionControlService.listVersions(getTenantId(), branch); + } catch (Exception e) { + throw handleException(e); + } + } + + + @GetMapping("/entity/{branch}/{entityType}/{versionId}") + public List listEntitiesAtVersion(@PathVariable String branch, + @PathVariable EntityType entityType, + @PathVariable String versionId) throws ThingsboardException { + try { + return versionControlService.listEntitiesAtVersion(getTenantId(), entityType, branch, versionId); + } catch (Exception e) { + throw handleException(e); + } + } + + @GetMapping("/entity/{branch}/{versionId}") + public List listAllEntitiesAtVersion(@PathVariable String branch, + @PathVariable String versionId) throws ThingsboardException { + try { + return versionControlService.listAllEntitiesAtVersion(getTenantId(), branch, versionId); + } catch (Exception e) { + throw handleException(e); + } + } + + + @PostMapping("/entity") + public List loadEntitiesVersion(@RequestBody VersionLoadRequest request) throws ThingsboardException { + SecurityUser user = getCurrentUser(); + try { + String versionId = request.getVersionId(); + if (versionId == null) { + List versions = versionControlService.listVersions(user.getTenantId(), request.getBranch()); + if (versions.size() > 0) { + versionId = versions.get(0).getId(); + } else { + throw new IllegalArgumentException("No versions available in branch"); + } + } + + return versionControlService.loadEntitiesVersion(user, request); + } catch (Exception e) { + throw handleException(e); + } + } + + + @ApiModelProperty(notes = "" + + "") + @GetMapping("/branches") + public List listBranches() throws ThingsboardException { + try { + List remoteBranches = versionControlService.listBranches(getTenantId()); + List infos = new ArrayList<>(); + + String defaultBranch = getSettings().getDefaultBranch(); + if (StringUtils.isNotEmpty(defaultBranch)) { + remoteBranches.remove(defaultBranch); + infos.add(new BranchInfo(defaultBranch, true)); + } + + remoteBranches.forEach(branch -> infos.add(new BranchInfo(branch, false))); + return infos; + } catch (Exception e) { + throw handleException(e); + } + } + + + @ApiModelProperty(notes = "" + + "```\n{\n" + + " \"repositoryUri\": \"https://github.com/User/repo.git\",\n" + + " \"username\": \"User\",\n" + + " \"password\": \"api_key\",\n" + + " \"defaultBranch\": \"master\"\n" + + "}\n```") + @GetMapping("/settings") + public EntitiesVersionControlSettings getSettings() throws ThingsboardException { + try { + return versionControlService.getSettings(getTenantId()); + } catch (Exception e) { + throw handleException(e); + } + } + + @ApiModelProperty(notes = "" + + "```\n{\n" + + " \"repositoryUri\": \"https://github.com/User/repo.git\",\n" + + " \"username\": \"User\",\n" + + " \"password\": \"api_key\",\n" + + " \"defaultBranch\": \"master\"\n" + + "}\n```") + @PostMapping("/settings") + public void saveSettings(@RequestBody EntitiesVersionControlSettings settings) throws ThingsboardException { + try { + versionControlService.saveSettings(getTenantId(), settings); + } catch (Exception e) { + throw handleException(e); + } + } + + + @Data + public static class BranchInfo { + private final String name; + private final boolean isDefault; + } } diff --git a/application/src/main/java/org/thingsboard/server/service/asset/AssetBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/asset/AssetBulkImportService.java index cfefcafbf4..179c9c697d 100644 --- a/application/src/main/java/org/thingsboard/server/service/asset/AssetBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/asset/AssetBulkImportService.java @@ -25,8 +25,8 @@ import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.sync.importing.csv.AbstractBulkImportService; -import org.thingsboard.server.service.sync.importing.csv.BulkImportColumnType; +import org.thingsboard.server.service.sync.exportimport.importing.csv.AbstractBulkImportService; +import org.thingsboard.server.service.sync.exportimport.importing.csv.BulkImportColumnType; import org.thingsboard.server.service.security.model.SecurityUser; import java.util.Map; diff --git a/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java index 3aac9c3641..3e63652b97 100644 --- a/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/device/DeviceBulkImportService.java @@ -49,8 +49,8 @@ import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.exception.DeviceCredentialsValidationException; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.sync.importing.csv.AbstractBulkImportService; -import org.thingsboard.server.service.sync.importing.csv.BulkImportColumnType; +import org.thingsboard.server.service.sync.exportimport.importing.csv.AbstractBulkImportService; +import org.thingsboard.server.service.sync.exportimport.importing.csv.BulkImportColumnType; import org.thingsboard.server.service.security.model.SecurityUser; import java.util.Collection; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java index b09ac532df..75f7133721 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeBulkImportService.java @@ -25,8 +25,8 @@ import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.sync.importing.csv.AbstractBulkImportService; -import org.thingsboard.server.service.sync.importing.csv.BulkImportColumnType; +import org.thingsboard.server.service.sync.exportimport.importing.csv.AbstractBulkImportService; +import org.thingsboard.server.service.sync.exportimport.importing.csv.BulkImportColumnType; import org.thingsboard.server.service.security.model.SecurityUser; import java.util.Map; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/DefaultEntitiesExportImportService.java similarity index 84% rename from application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/DefaultEntitiesExportImportService.java index 55384b21db..608a256514 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/DefaultEntitiesExportImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync; +package org.thingsboard.server.service.sync.exportimport; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -27,14 +27,14 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.exporting.EntityExportService; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; -import org.thingsboard.server.service.sync.exporting.impl.BaseEntityExportService; -import org.thingsboard.server.service.sync.exporting.impl.DefaultEntityExportService; -import org.thingsboard.server.service.sync.importing.EntityImportService; -import org.thingsboard.server.service.sync.importing.data.EntityImportResult; -import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; +import org.thingsboard.server.service.sync.exportimport.exporting.EntityExportService; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportSettings; +import org.thingsboard.server.service.sync.exportimport.exporting.impl.BaseEntityExportService; +import org.thingsboard.server.service.sync.exportimport.exporting.impl.DefaultEntityExportService; +import org.thingsboard.server.service.sync.exportimport.importing.EntityImportService; +import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportResult; +import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportSettings; import org.thingsboard.server.utils.ThrowingRunnable; import java.util.ArrayList; @@ -74,6 +74,7 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS @Override public List> importEntities(SecurityUser user, List> exportDataList, EntityImportSettings importSettings) throws ThingsboardException { fixOrder(exportDataList); + List> importResults = new ArrayList<>(); for (EntityExportData exportData : exportDataList) { @@ -88,6 +89,17 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS saveReferencesCallback.run(); } + importResults.stream() + .map(EntityImportResult::getSendEventsCallback) + .filter(Objects::nonNull) + .forEach(sendEventsCallback -> { + try { + sendEventsCallback.run(); + } catch (Exception e) { + log.error("Failed to send event for entity", e); + } + }); + return importResults; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/EntitiesExportImportService.java similarity index 75% rename from application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/EntitiesExportImportService.java index eaaf01ba2c..5c29feb57c 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/EntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/EntitiesExportImportService.java @@ -13,16 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync; +package org.thingsboard.server.service.sync.exportimport; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; -import org.thingsboard.server.service.sync.importing.data.EntityImportResult; -import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportSettings; +import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportResult; +import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportSettings; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/DefaultExportableEntitiesService.java similarity index 77% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/DefaultExportableEntitiesService.java index ce6924de93..4d772ad935 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/DefaultExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/DefaultExportableEntitiesService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting; +package org.thingsboard.server.service.sync.exportimport.exporting; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -30,6 +30,8 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.query.EntityData; import org.thingsboard.server.common.data.query.EntityDataPageLink; import org.thingsboard.server.common.data.query.EntityDataQuery; @@ -37,7 +39,6 @@ import org.thingsboard.server.common.data.query.EntityDataSortOrder; import org.thingsboard.server.common.data.query.EntityFilter; import org.thingsboard.server.common.data.query.EntityKey; import org.thingsboard.server.common.data.query.EntityKeyType; -import org.thingsboard.server.common.data.query.EntityTypeFilter; import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.ExportableEntityDao; import org.thingsboard.server.dao.entity.EntityService; @@ -46,12 +47,12 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.AccessControlService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; -import org.thingsboard.server.service.sync.exporting.data.request.CustomEntityFilterExportRequest; -import org.thingsboard.server.service.sync.exporting.data.request.CustomEntityQueryExportRequest; -import org.thingsboard.server.service.sync.exporting.data.request.EntityListExportRequest; -import org.thingsboard.server.service.sync.exporting.data.request.EntityTypeExportRequest; -import org.thingsboard.server.service.sync.exporting.data.request.ExportRequest; -import org.thingsboard.server.service.sync.exporting.data.request.SingleEntityExportRequest; +import org.thingsboard.server.service.sync.vc.data.request.create.EntitiesByCustomFilterVersionCreateConfig; +import org.thingsboard.server.service.sync.vc.data.request.create.EntitiesByCustomQueryVersionCreateConfig; +import org.thingsboard.server.service.sync.vc.data.request.create.EntityListVersionCreateConfig; +import org.thingsboard.server.service.sync.vc.data.request.create.EntityTypeVersionCreateConfig; +import org.thingsboard.server.service.sync.vc.data.request.create.VersionCreateConfig; +import org.thingsboard.server.service.sync.vc.data.request.create.SingleEntityVersionCreateConfig; import java.util.Collection; import java.util.Collections; @@ -129,6 +130,16 @@ public class DefaultExportableEntitiesService implements ExportableEntitiesServi return entity; } + @Override + public , I extends EntityId> PageData findEntitiesByTenantId(TenantId tenantId, EntityType entityType, PageLink pageLink) { + Dao dao = getDao(entityType); + if (dao instanceof ExportableEntityDao) { + ExportableEntityDao exportableEntityDao = (ExportableEntityDao) dao; + return exportableEntityDao.findByTenantId(tenantId.getId(), pageLink); + } + return new PageData<>(); + } + private boolean belongsToTenant(HasId entity, TenantId tenantId) { return tenantId.equals(((HasTenantId) entity).getTenantId()); } @@ -136,31 +147,31 @@ public class DefaultExportableEntitiesService implements ExportableEntitiesServi @Transactional(readOnly = true, timeout = 40) @Override - public List findEntitiesForRequest(TenantId tenantId, ExportRequest request) { + public List findEntitiesByFilter(TenantId tenantId, VersionCreateConfig request) { switch (request.getType()) { case SINGLE_ENTITY: { - return List.of(((SingleEntityExportRequest) request).getEntityId()); + return List.of(((SingleEntityVersionCreateConfig) request).getEntityId()); } case ENTITY_LIST: { - return ((EntityListExportRequest) request).getEntitiesIds(); + return ((EntityListVersionCreateConfig) request).getEntitiesIds(); } case ENTITY_TYPE: { - EntityTypeExportRequest exportRequest = (EntityTypeExportRequest) request; - EntityTypeFilter entityTypeFilter = new EntityTypeFilter(); + EntityTypeVersionCreateConfig exportRequest = (EntityTypeVersionCreateConfig) request; + org.thingsboard.server.common.data.query.EntityTypeFilter entityTypeFilter = new org.thingsboard.server.common.data.query.EntityTypeFilter(); entityTypeFilter.setEntityType(exportRequest.getEntityType()); CustomerId customerId = new CustomerId(ObjectUtils.defaultIfNull(exportRequest.getCustomerId(), EntityId.NULL_UUID)); - return findEntitiesByFilter(tenantId, customerId, entityTypeFilter, exportRequest.getPage(), exportRequest.getPageSize()); + return findEntitiesByFilter(tenantId, customerId, entityTypeFilter, 0, Integer.MAX_VALUE); } case CUSTOM_ENTITY_FILTER: { - CustomEntityFilterExportRequest exportRequest = (CustomEntityFilterExportRequest) request; + EntitiesByCustomFilterVersionCreateConfig exportRequest = (EntitiesByCustomFilterVersionCreateConfig) request; EntityFilter filter = exportRequest.getFilter(); CustomerId customerId = new CustomerId(ObjectUtils.defaultIfNull(exportRequest.getCustomerId(), EntityId.NULL_UUID)); - return findEntitiesByFilter(tenantId, customerId, filter, exportRequest.getPage(), exportRequest.getPageSize()); + return findEntitiesByFilter(tenantId, customerId, filter, 0, Integer.MAX_VALUE); } case CUSTOM_ENTITY_QUERY: { - CustomEntityQueryExportRequest exportRequest = (CustomEntityQueryExportRequest) request; + EntitiesByCustomQueryVersionCreateConfig exportRequest = (EntitiesByCustomQueryVersionCreateConfig) request; EntityDataQuery query = exportRequest.getQuery(); CustomerId customerId = new CustomerId(ObjectUtils.defaultIfNull(exportRequest.getCustomerId(), EntityId.NULL_UUID)); @@ -194,6 +205,17 @@ public class DefaultExportableEntitiesService implements ExportableEntitiesServi } } + @Override + public void deleteByTenantIdAndId(TenantId tenantId, I id) { + EntityType entityType = id.getEntityType(); + Dao dao = getDao(entityType); + if (dao == null) { + throw new IllegalArgumentException("Unsupported entity type " + entityType); + } + + dao.removeById(tenantId, id.getId()); + } + @Override public void checkPermission(SecurityUser user, HasId entity, EntityType entityType, Operation operation) throws ThingsboardException { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/EntityExportService.java similarity index 81% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/EntityExportService.java index 73f946e722..5532f30125 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/EntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/EntityExportService.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting; +package org.thingsboard.server.service.sync.exportimport.exporting; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportSettings; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportData; public interface EntityExportService, D extends EntityExportData> { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/ExportableEntitiesService.java similarity index 74% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/ExportableEntitiesService.java index 9021a03e85..043ec91e20 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/ExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/ExportableEntitiesService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting; +package org.thingsboard.server.service.sync.exportimport.exporting; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; @@ -21,9 +21,11 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.HasId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; -import org.thingsboard.server.service.sync.exporting.data.request.ExportRequest; +import org.thingsboard.server.service.sync.vc.data.request.create.VersionCreateConfig; import java.util.List; @@ -35,7 +37,11 @@ public interface ExportableEntitiesService { , I extends EntityId> E findEntityByTenantIdAndName(TenantId tenantId, EntityType entityType, String name); - List findEntitiesForRequest(TenantId tenantId, ExportRequest request); + , I extends EntityId> PageData findEntitiesByTenantId(TenantId tenantId, EntityType entityType, PageLink pageLink); + + List findEntitiesByFilter(TenantId tenantId, VersionCreateConfig filter); // FIXME [viacheslav]: + + void deleteByTenantIdAndId(TenantId tenantId, I id); void checkPermission(SecurityUser user, HasId entity, EntityType entityType, Operation operation) throws ThingsboardException; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/data/DeviceExportData.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceExportData.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/data/DeviceExportData.java index 56633f9c91..4be338d7cc 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/DeviceExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/data/DeviceExportData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting.data; +package org.thingsboard.server.service.sync.exportimport.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/data/EntityExportData.java similarity index 96% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/data/EntityExportData.java index 728d8212c4..81e3b344d0 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/EntityExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/data/EntityExportData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting.data; +package org.thingsboard.server.service.sync.exportimport.exporting.data; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityExportSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/data/EntityExportSettings.java similarity index 92% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityExportSettings.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/data/EntityExportSettings.java index 5454105ee5..0fb318b5fb 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityExportSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/data/EntityExportSettings.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting.data.request; +package org.thingsboard.server.service.sync.exportimport.exporting.data; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/RuleChainExportData.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/data/RuleChainExportData.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/data/RuleChainExportData.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/data/RuleChainExportData.java index 2e05e2f1f5..e1d870473f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/RuleChainExportData.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/data/RuleChainExportData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting.data; +package org.thingsboard.server.service.sync.exportimport.exporting.data; import lombok.Data; import lombok.EqualsAndHashCode; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/impl/BaseEntityExportService.java similarity index 86% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/impl/BaseEntityExportService.java index 7b5f758f93..46428a763f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/BaseEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/impl/BaseEntityExportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting.impl; +package org.thingsboard.server.service.sync.exportimport.exporting.impl; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; @@ -21,8 +21,8 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportSettings; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportData; import java.util.Set; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/impl/DefaultEntityExportService.java similarity index 89% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/impl/DefaultEntityExportService.java index a051f0e59f..69af613480 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DefaultEntityExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/impl/DefaultEntityExportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting.impl; +package org.thingsboard.server.service.sync.exportimport.exporting.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; @@ -28,10 +28,10 @@ import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; -import org.thingsboard.server.service.sync.exporting.EntityExportService; -import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportSettings; +import org.thingsboard.server.service.sync.exportimport.exporting.EntityExportService; +import org.thingsboard.server.service.sync.exportimport.exporting.ExportableEntitiesService; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportData; import java.util.ArrayList; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/impl/DeviceExportService.java similarity index 91% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceExportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/impl/DeviceExportService.java index 21df8beda2..93f9c51655 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/DeviceExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/impl/DeviceExportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting.impl; +package org.thingsboard.server.service.sync.exportimport.exporting.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.sync.exporting.data.DeviceExportData; +import org.thingsboard.server.service.sync.exportimport.exporting.data.DeviceExportData; import java.util.Set; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/RuleChainExportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/impl/RuleChainExportService.java similarity index 91% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/RuleChainExportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/impl/RuleChainExportService.java index e814cd6ce7..f40ee6884a 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/impl/RuleChainExportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/impl/RuleChainExportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting.impl; +package org.thingsboard.server.service.sync.exportimport.exporting.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.sync.exporting.data.RuleChainExportData; +import org.thingsboard.server.service.sync.exportimport.exporting.data.RuleChainExportData; import java.util.Set; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/EntityImportService.java similarity index 78% rename from application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/EntityImportService.java index 6c82530273..be6f62efe4 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/EntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/EntityImportService.java @@ -13,16 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.importing; +package org.thingsboard.server.service.sync.exportimport.importing; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.importing.data.EntityImportResult; -import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; +import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportResult; +import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportSettings; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportData; public interface EntityImportService, D extends EntityExportData> { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/AbstractBulkImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/csv/AbstractBulkImportService.java similarity index 97% rename from application/src/main/java/org/thingsboard/server/service/sync/importing/csv/AbstractBulkImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/csv/AbstractBulkImportService.java index 06cd7a93be..b2bfafc695 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/AbstractBulkImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/csv/AbstractBulkImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.importing.csv; +package org.thingsboard.server.service.sync.exportimport.importing.csv; import com.google.common.util.concurrent.FutureCallback; import com.google.gson.JsonObject; @@ -44,7 +44,6 @@ import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.controller.BaseController; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.service.action.EntityActionService; -import org.thingsboard.server.service.sync.importing.csv.BulkImportRequest.ColumnMapping; import org.thingsboard.server.service.security.AccessValidator; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.AccessControlService; @@ -166,7 +165,7 @@ public abstract class AbstractBulkImportService data) { + private void saveKvs(SecurityUser user, E entity, Map data) { Arrays.stream(BulkImportColumnType.values()) .filter(BulkImportColumnType::isKv) .map(kvType -> { @@ -250,7 +249,7 @@ public abstract class AbstractBulkImportService columnsMappings = request.getMapping().getColumns(); + List columnsMappings = request.getMapping().getColumns(); return records.stream() .map(record -> { EntityData entityData = new EntityData(); @@ -281,7 +280,7 @@ public abstract class AbstractBulkImportService fields = new LinkedHashMap<>(); - private final Map kvs = new LinkedHashMap<>(); + private final Map kvs = new LinkedHashMap<>(); private int lineNumber; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportColumnType.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/csv/BulkImportColumnType.java similarity index 96% rename from application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportColumnType.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/csv/BulkImportColumnType.java index beafe5e95f..9c0e6b6491 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportColumnType.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/csv/BulkImportColumnType.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.importing.csv; +package org.thingsboard.server.service.sync.exportimport.importing.csv; import lombok.Getter; import org.thingsboard.server.common.data.DataConstants; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/csv/BulkImportRequest.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportRequest.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/csv/BulkImportRequest.java index f600289ab6..6347910cab 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/csv/BulkImportRequest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.importing.csv; +package org.thingsboard.server.service.sync.exportimport.importing.csv; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportResult.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/csv/BulkImportResult.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportResult.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/csv/BulkImportResult.java index da12a6baeb..550d4a72f3 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/BulkImportResult.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/csv/BulkImportResult.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.importing.csv; +package org.thingsboard.server.service.sync.exportimport.importing.csv; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/ImportedEntityInfo.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/csv/ImportedEntityInfo.java similarity index 91% rename from application/src/main/java/org/thingsboard/server/service/sync/importing/csv/ImportedEntityInfo.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/csv/ImportedEntityInfo.java index 68e7a49b18..a39dcc7481 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/csv/ImportedEntityInfo.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/csv/ImportedEntityInfo.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.importing.csv; +package org.thingsboard.server.service.sync.exportimport.importing.csv; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportResult.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/data/EntityImportResult.java similarity index 95% rename from application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportResult.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/data/EntityImportResult.java index 62c574d5f0..242856bfcb 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportResult.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/data/EntityImportResult.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.importing.data; +package org.thingsboard.server.service.sync.exportimport.importing.data; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/data/EntityImportSettings.java similarity index 92% rename from application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/data/EntityImportSettings.java index a5de7d8744..075a6896ef 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/EntityImportSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/data/EntityImportSettings.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.importing.data; +package org.thingsboard.server.service.sync.exportimport.importing.data; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/AssetImportService.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/AssetImportService.java index ae54a988f3..80a942c73f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/AssetImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/AssetImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.importing.impl; +package org.thingsboard.server.service.sync.exportimport.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/BaseEntityImportService.java similarity index 95% rename from application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/BaseEntityImportService.java index 28095a7b85..7ecfa7d3ab 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/BaseEntityImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/BaseEntityImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.importing.impl; +package org.thingsboard.server.service.sync.exportimport.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; @@ -35,11 +35,11 @@ import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.service.action.EntityActionService; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.Operation; -import org.thingsboard.server.service.sync.exporting.ExportableEntitiesService; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.importing.EntityImportService; -import org.thingsboard.server.service.sync.importing.data.EntityImportResult; -import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; +import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportResult; +import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportSettings; +import org.thingsboard.server.service.sync.exportimport.exporting.ExportableEntitiesService; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exportimport.importing.EntityImportService; import java.util.ArrayList; import java.util.List; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/CustomerImportService.java similarity index 93% rename from application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/CustomerImportService.java index 1a79fe666b..b69c5a0d5b 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/CustomerImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/CustomerImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.importing.impl; +package org.thingsboard.server.service.sync.exportimport.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -26,7 +26,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.customer.CustomerService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/DashboardImportService.java similarity index 95% rename from application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/DashboardImportService.java index 77f32bf94c..b85e403f87 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DashboardImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/DashboardImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.importing.impl; +package org.thingsboard.server.service.sync.exportimport.importing.impl; import com.fasterxml.jackson.databind.JsonNode; import lombok.RequiredArgsConstructor; @@ -30,8 +30,8 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; +import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportSettings; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportData; import org.thingsboard.server.utils.RegexUtils; import java.util.Collections; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/DeviceImportService.java similarity index 94% rename from application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/DeviceImportService.java index 091b6eb856..112152365b 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/DeviceImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.importing.impl; +package org.thingsboard.server.service.sync.exportimport.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -25,7 +25,7 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.exporting.data.DeviceExportData; +import org.thingsboard.server.service.sync.exportimport.exporting.data.DeviceExportData; @Service @TbCoreComponent diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/DeviceProfileImportService.java similarity index 95% rename from application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/DeviceProfileImportService.java index 5bc45dc567..f97950aaf8 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/DeviceProfileImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/DeviceProfileImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.importing.impl; +package org.thingsboard.server.service.sync.exportimport.importing.impl; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -28,7 +28,7 @@ import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.ota.OtaPackageStateService; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportData; import java.util.Objects; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/RuleChainImportService.java similarity index 95% rename from application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java rename to application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/RuleChainImportService.java index 09a13d5f14..993f9fa27a 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/impl/RuleChainImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/importing/impl/RuleChainImportService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.importing.impl; +package org.thingsboard.server.service.sync.exportimport.importing.impl; import com.fasterxml.jackson.databind.JsonNode; import lombok.RequiredArgsConstructor; @@ -32,8 +32,8 @@ import org.thingsboard.server.common.data.rule.RuleChainUpdateResult; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.exporting.data.RuleChainExportData; -import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; +import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportSettings; +import org.thingsboard.server.service.sync.exportimport.exporting.data.RuleChainExportData; import org.thingsboard.server.utils.RegexUtils; import java.util.Collections; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/request/ImportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/importing/data/request/ImportRequest.java deleted file mode 100644 index 7943e1a404..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/importing/data/request/ImportRequest.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 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.sync.importing.data.request; - -import lombok.Data; -import org.thingsboard.server.common.data.ExportableEntity; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; - -import java.util.List; - -@Data -public class ImportRequest { - - private List> exportDataList; - private EntityImportSettings importSettings; - -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java index a298d64ccc..919960ce62 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java @@ -18,35 +18,62 @@ package org.thingsboard.server.service.sync.vc; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; -import org.eclipse.jgit.api.errors.GitAPIException; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.dao.settings.AdminSettingsService; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.JsonDataEntry; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.query.EntityData; +import org.thingsboard.server.common.data.query.EntityDataPageLink; +import org.thingsboard.server.common.data.query.EntityDataQuery; +import org.thingsboard.server.common.data.query.EntityDataSortOrder; +import org.thingsboard.server.common.data.query.EntityKey; +import org.thingsboard.server.common.data.query.EntityKeyType; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.entity.EntityService; +import org.thingsboard.server.dao.tenant.TenantDao; import org.thingsboard.server.queue.util.AfterStartUp; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.model.SecurityUser; -import org.thingsboard.server.service.sync.EntitiesExportImportService; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; -import org.thingsboard.server.service.sync.importing.data.EntityImportResult; -import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; +import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.sync.exportimport.EntitiesExportImportService; +import org.thingsboard.server.service.sync.exportimport.exporting.ExportableEntitiesService; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportSettings; +import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportResult; +import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportSettings; import org.thingsboard.server.service.sync.vc.data.EntitiesVersionControlSettings; -import org.thingsboard.server.service.sync.vc.data.VersionedEntityInfo; import org.thingsboard.server.service.sync.vc.data.EntityVersion; -import org.thingsboard.server.service.sync.vc.data.EntityVersionLoadResult; -import org.thingsboard.server.service.sync.vc.data.EntityVersionLoadSettings; -import org.thingsboard.server.service.sync.vc.data.EntityVersionSaveSettings; +import org.thingsboard.server.service.sync.vc.data.VersionCreationResult; +import org.thingsboard.server.service.sync.vc.data.VersionLoadResult; +import org.thingsboard.server.service.sync.vc.data.VersionedEntityInfo; +import org.thingsboard.server.service.sync.vc.data.request.load.VersionLoadRequest; +import org.thingsboard.server.service.sync.vc.data.request.load.VersionLoadSettings; +import org.thingsboard.server.service.sync.vc.data.request.create.VersionCreateRequest; +import org.thingsboard.server.service.sync.vc.data.request.create.EntitiesByCustomFilterVersionCreateConfig; +import org.thingsboard.server.service.sync.vc.data.request.create.EntitiesByCustomQueryVersionCreateConfig; +import org.thingsboard.server.service.sync.vc.data.request.create.EntityListVersionCreateConfig; +import org.thingsboard.server.service.sync.vc.data.request.create.EntityTypeVersionCreateConfig; +import org.thingsboard.server.service.sync.vc.data.request.create.SingleEntityVersionCreateConfig; +import org.thingsboard.server.service.sync.vc.data.request.create.VersionCreateConfig; import org.thingsboard.server.utils.GitRepository; import java.io.File; @@ -56,16 +83,19 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +import static org.thingsboard.server.dao.sql.query.EntityKeyMapping.CREATED_TIME; + @Service @TbCoreComponent @RequiredArgsConstructor @@ -73,75 +103,152 @@ import java.util.stream.Collectors; public class DefaultEntitiesVersionControlService implements EntitiesVersionControlService { private final EntitiesExportImportService exportImportService; + private final ExportableEntitiesService exportableEntitiesService; + private final AttributesService attributesService; + private final EntityService entityService; + private final TenantDao tenantDao; - private GitRepository repository; - private final ReadWriteLock repositoryLock = new ReentrantReadWriteLock(); - - private ScheduledExecutorService fetchExecutor; - private ScheduledFuture fetchTask; + // TODO [viacheslav]: concurrency + private final Map repositories = new ConcurrentHashMap<>(); + @Value("${java.io.tmpdir}") + private String repositoriesFolder; - private final AdminSettingsService adminSettingsService; private static final String SETTINGS_KEY = "vc"; private final ObjectWriter jsonWriter = new ObjectMapper().writer(SerializationFeature.INDENT_OUTPUT); @AfterStartUp public void init() { - EntitiesVersionControlSettings settings = getSettings(); - if (settings != null) { - try { - initRepository(settings); - } catch (Exception e) { - log.debug("Failed to init repository", e); + DaoUtil.processInBatches(tenantDao::findTenantsIds, 100, tenantId -> { + EntitiesVersionControlSettings settings = getSettings(tenantId); + if (settings != null) { + try { + initRepository(tenantId, settings); + } catch (Exception e) { + log.warn("Failed to init repository for tenant {}", tenantId, e); + } } - } - - int fetchPeriod = settings == null || settings.getFetchPeriod() == 0 ? 10 : settings.getFetchPeriod(); - fetchExecutor = Executors.newSingleThreadScheduledExecutor(); - fetchTask = scheduleFetch(fetchPeriod); + }); + Executors.newSingleThreadScheduledExecutor().scheduleWithFixedDelay(() -> { + repositories.forEach((tenantId, repository) -> { + try { + repository.fetch(); + log.info("Fetching remote repository for tenant {}", tenantId); + } catch (Exception e) { + log.warn("Failed to fetch repository for tenant {}", tenantId, e); + } + }); + }, 5, 5, TimeUnit.SECONDS); } @Override - public EntityVersion saveEntityVersion(SecurityUser user, EntityId entityId, String branch, String versionName, EntityVersionSaveSettings settings) throws Exception { - return saveEntitiesVersion(user, List.of(entityId), branch, versionName, settings); - } + public VersionCreationResult saveEntitiesVersion(SecurityUser user, VersionCreateRequest request) throws Exception { + GitRepository repository = checkRepository(user.getTenantId()); + repository.getLock().writeLock().lock(); - @Override - public EntityVersion saveEntitiesVersion(SecurityUser user, List entitiesIds, String branch, String versionName, EntityVersionSaveSettings settings) throws Exception { - repositoryLock.writeLock().lock(); try { - checkRepository(); - checkBranch(user.getTenantId(), branch); - - List> entityDataList = new ArrayList<>(); - EntityExportSettings exportSettings = EntityExportSettings.builder() - .exportRelations(settings.isSaveRelations()) - .build(); - for (EntityId entityId : entitiesIds) { - EntityExportData> entityData = exportImportService.exportEntity(user, entityId, exportSettings); - entityDataList.add(entityData); + repository.fetch(); + if (repository.listBranches().contains(request.getBranch())) { + repository.checkout(request.getBranch()); + repository.merge(request.getBranch()); + } else { // FIXME [viacheslav]: rollback orphan branch on failure + repository.createAndCheckoutOrphanBranch(request.getBranch()); // FIXME [viacheslav]: Checkout returned unexpected result NO_CHANGE for master branch } - fetch(); - if (repository.listBranches().contains(branch)) { - repository.checkout(branch); - repository.merge(branch); - } else { - repository.createAndCheckoutOrphanBranch(branch); + for (VersionCreateRequest.Config config : request.getConfigs()) { + EntityExportSettings exportSettings = EntityExportSettings.builder() + .exportRelations(config.isSaveRelations()) + .build(); + + List> entityDataList = new ArrayList<>(); + for (EntityId entityId : findEntities()) { + EntityExportData> entityData = exportImportService.exportEntity(user, entityId, exportSettings); + entityDataList.add(entityData); + } + + if (config.isRemoveOtherRemoteEntitiesOfType()) { + entityDataList.stream() + .map(EntityExportData::getEntityType) + .distinct() + .forEach(entityType -> { + try { + FileUtils.deleteDirectory(Path.of(repository.getDirectory(), getRelativePath(entityType, null)).toFile()); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + + for (EntityExportData entityData : entityDataList) { + String entityDataJson = jsonWriter.writeValueAsString(entityData); + FileUtils.write(Path.of(repository.getDirectory(), getRelativePath(entityData.getEntityType(), + entityData.getEntity().getId().toString())).toFile(), entityDataJson, StandardCharsets.UTF_8); + } } + // TODO [viacheslav]: find with pagination - for (EntityExportData entityData : entityDataList) { - String entityDataJson = jsonWriter.writeValueAsString(entityData); - FileUtils.write(new File(repository.getDirectory() + "/" + getRelativePath(entityData.getEntityType(), - entityData.getEntity().getId().toString())), entityDataJson, StandardCharsets.UTF_8); - } + repository.add("."); + + VersionCreationResult result = new VersionCreationResult(); + GitRepository.Status status = repository.status(); + result.setAdded(status.getAdded().size()); + result.setModified(status.getModified().size()); + result.setRemoved(status.getRemoved().size()); - GitRepository.Commit commit = repository.commit(versionName, "."); + GitRepository.Commit commit = repository.commit(request.getVersionName()); repository.push(); - return toVersion(commit); + + result.setVersion(toVersion(commit)); + return result; } finally { - repositoryLock.writeLock().unlock(); + repository.getLock().writeLock().unlock(); + } + } + + private List findEntities(SecurityUser user, VersionCreateConfig entityFilter, int page, int pageSize) { + switch (entityFilter.getType()) { + case SINGLE_ENTITY: { + SingleEntityVersionCreateConfig filter = (SingleEntityVersionCreateConfig) entityFilter; + return List.of(filter.getEntityId()); + } + case ENTITY_LIST: { + EntityListVersionCreateConfig filter = (EntityListVersionCreateConfig) entityFilter; + return filter.getEntitiesIds(); + } + case ENTITY_TYPE: { + EntityTypeVersionCreateConfig filter = (EntityTypeVersionCreateConfig) entityFilter; + EntitiesByCustomFilterVersionCreateConfig newFilter = new EntitiesByCustomFilterVersionCreateConfig(); + + org.thingsboard.server.common.data.query.EntityTypeFilter entityTypeFilter = new org.thingsboard.server.common.data.query.EntityTypeFilter(); + entityTypeFilter.setEntityType(filter.getEntityType()); + + newFilter.setFilter(entityTypeFilter); + newFilter.setCustomerId(filter.getCustomerId()); + return findEntities(user, newFilter, page, pageSize); + } + case CUSTOM_ENTITY_FILTER: { + EntitiesByCustomFilterVersionCreateConfig filter = (EntitiesByCustomFilterVersionCreateConfig) entityFilter; + EntitiesByCustomQueryVersionCreateConfig newFilter = new EntitiesByCustomQueryVersionCreateConfig(); + + EntityDataPageLink pageLink = new EntityDataPageLink(); + pageLink.setPage(page); + pageLink.setPageSize(pageSize); + EntityKey sortProperty = new EntityKey(EntityKeyType.ENTITY_FIELD, CREATED_TIME); + pageLink.setSortOrder(new EntityDataSortOrder(sortProperty, EntityDataSortOrder.Direction.DESC)); + EntityDataQuery query = new EntityDataQuery(filter.getFilter(), pageLink, List.of(sortProperty), Collections.emptyList(), Collections.emptyList()); + + newFilter.setQuery(query); + newFilter.setCustomerId(filter.getCustomerId()); + return findEntities(user, newFilter, page, pageSize); + } + case CUSTOM_ENTITY_QUERY: { + EntitiesByCustomQueryVersionCreateConfig filter = (EntitiesByCustomQueryVersionCreateConfig) entityFilter; + CustomerId customerId = new CustomerId(ObjectUtils.defaultIfNull(filter.getCustomerId(), EntityId.NULL_UUID)); + return entityService.findEntityDataByQuery(user.getTenantId(), customerId, filter.getQuery()).getData() + .stream().map(EntityData::getEntityId) + .collect(Collectors.toList()); + } } } @@ -162,16 +269,14 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont } private List listVersions(TenantId tenantId, String branch, String path) throws Exception { - repositoryLock.readLock().lock(); + GitRepository repository = checkRepository(tenantId); + repository.getLock().readLock().lock(); try { - checkRepository(); - checkBranch(tenantId, branch); - return repository.listCommits(branch, path, Integer.MAX_VALUE).stream() .map(this::toVersion) .collect(Collectors.toList()); } finally { - repositoryLock.readLock().unlock(); + repository.getLock().readLock().unlock(); } } @@ -187,16 +292,14 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont } private List listEntitiesAtVersion(TenantId tenantId, String branch, String versionId, String path) throws Exception { - repositoryLock.readLock().lock(); + GitRepository repository = checkRepository(tenantId); + repository.getLock().readLock().lock(); try { - checkRepository(); - checkBranch(tenantId, branch); - checkVersion(tenantId, branch, versionId, path); - + checkVersion(tenantId, branch, versionId); return repository.listFilesAtCommit(versionId, path).stream() .map(filePath -> { EntityId entityId = fromRelativePath(filePath); - EntityExportData entityData = getEntityDataAtVersion(entityId, versionId); + EntityExportData entityData = getEntityDataAtVersion(tenantId, entityId, versionId); VersionedEntityInfo info = new VersionedEntityInfo(); info.setExternalId(entityId); @@ -205,171 +308,155 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont }) .collect(Collectors.toList()); } finally { - repositoryLock.readLock().unlock(); + repository.getLock().readLock().unlock(); } } @Override - public EntityVersionLoadResult loadEntityVersion(SecurityUser user, EntityId externalId, String branch, String versionId, EntityVersionLoadSettings settings) throws Exception { - return loadAtVersion(user, branch, versionId, getRelativePath(externalId.getEntityType(), externalId.getId().toString()), settings).get(0); - } - - @Override - public List loadEntityTypeVersion(SecurityUser user, EntityType entityType, String branch, String versionId, EntityVersionLoadSettings settings) throws Exception { - return loadAtVersion(user, branch, versionId, getRelativePath(entityType, null), settings); - } - - @Override - public List loadAllAtVersion(SecurityUser user, String branch, String versionId, EntityVersionLoadSettings settings) throws Exception { - return loadAtVersion(user, branch, versionId, null, settings); - } + public List loadEntitiesVersion(SecurityUser user, VersionLoadRequest request) throws Exception { + GitRepository repository = checkRepository(user.getTenantId()); - private List loadAtVersion(SecurityUser user, String branch, String versionId, String path, EntityVersionLoadSettings settings) throws Exception { List> entityDataList = new ArrayList<>(); - repositoryLock.readLock().lock(); + EntityVersion version; + repository.getLock().readLock().lock(); try { - for (VersionedEntityInfo info : listEntitiesAtVersion(user.getTenantId(), branch, versionId, path)) { - EntityExportData entityData = getEntityDataAtVersion(info.getExternalId(), versionId); + version = checkVersion(user.getTenantId(), request.getBranch(), request.getVersionId()); + for (VersionedEntityInfo info : listEntitiesAtVersion(user.getTenantId(), request.getBranch(), request.getVersionId(), path)) { + EntityExportData entityData = getEntityDataAtVersion(user.getTenantId(), info.getExternalId(), versionId); entityDataList.add(entityData); } } finally { - repositoryLock.readLock().unlock(); + repository.getLock().readLock().unlock(); } EntityImportSettings importSettings = EntityImportSettings.builder() .updateRelations(settings.isLoadRelations()) .findExistingByName(settings.isFindExistingEntityByName()) .build(); + // FIXME [viacheslav]: do evrth in transaction List> importResults = exportImportService.importEntities(user, entityDataList, importSettings); - return importResults.stream() - .map(importResult -> EntityVersionLoadResult.builder() - .previousEntityVersion(importResult.getOldEntity()) - .newEntityVersion(importResult.getSavedEntity()) - .entityType(importResult.getEntityType()) - .build()) - .collect(Collectors.toList()); + Map results = new HashMap<>(); + + boolean removeNonexistentLocalEntities = false; + if () + if (request.isRemoveOtherLocalEntitiesOfType()) { + importResults.stream() + .collect(Collectors.groupingBy(EntityImportResult::getEntityType)) // FIXME [viacheslav]: if no entities of entity type - remove all ? + .forEach((entityType, resultsForEntityType) -> { + Set modifiedEntities = resultsForEntityType.stream().map(EntityImportResult::getSavedEntity).map(ExportableEntity::getExternalId).collect(Collectors.toSet()); + AtomicInteger deleted = new AtomicInteger(); + + DaoUtil.processInBatches(pageLink -> { + return exportableEntitiesService.findEntitiesByTenantId(user.getTenantId(), entityType, pageLink); + }, 100, entity -> { + if (entity.getExternalId() == null || !modifiedEntities.contains(entity.getExternalId())) { + try { + exportableEntitiesService.checkPermission(user, entity, entityType, Operation.DELETE); + } catch (ThingsboardException e) { + throw new RuntimeException(e); + } + // need to delete in a specific order? + exportableEntitiesService.deleteByTenantIdAndId(user.getTenantId(), entity.getId()); + deleted.getAndIncrement(); + } + }); + results.put(entityType, VersionLoadResult.builder() + .entityType(entityType) + .created((int) resultsForEntityType.stream().filter(importResult -> importResult.getOldEntity() == null).count()) + .updated((int) resultsForEntityType.stream().filter(importResult -> importResult.getOldEntity() != null).count()) + .deleted(deleted.get()) + .build()); + }); + } + + return new ArrayList<>(results.values()); } @SneakyThrows - private EntityExportData getEntityDataAtVersion(EntityId externalId, String versionId) { - repositoryLock.readLock().lock(); + private EntityExportData getEntityDataAtVersion(TenantId tenantId, EntityId externalId, String versionId) { + GitRepository repository = checkRepository(tenantId); + repository.getLock().readLock().lock(); try { String entityDataJson = repository.getFileContentAtCommit(getRelativePath(externalId.getEntityType(), externalId.getId().toString()), versionId); return JacksonUtil.fromString(entityDataJson, EntityExportData.class); } finally { - repositoryLock.readLock().unlock(); + repository.getLock().readLock().unlock(); } } - - private void fetch() throws GitAPIException { - repositoryLock.writeLock().lock(); - try { - repository.fetch(); - } finally { - repositoryLock.writeLock().unlock(); - } - } - - private ScheduledFuture scheduleFetch(int fetchPeriod) { - return fetchExecutor.scheduleWithFixedDelay(() -> { - if (repository == null) return; - try { - fetch(); - } catch (Exception e) { - log.error("Failed to fetch remote repository", e); - } - }, fetchPeriod, fetchPeriod, TimeUnit.SECONDS); + private void updateEntityVersionInfo(TenantId tenantId, EntityId entityId, EntityVersion version) { + ObjectNode versionInfo = JacksonUtil.newObjectNode(); + versionInfo.set("versionName", new TextNode(version.getName())); + versionInfo.set("versionId", new TextNode(version.getId())); + attributesService.save(tenantId, entityId, DataConstants.SERVER_SCOPE, + List.of(new BaseAttributeKvEntry(System.currentTimeMillis(), new JsonDataEntry("entityVersionInfo", versionInfo.toString())))); } - private void checkVersion(TenantId tenantId, String branch, String versionId, String path) throws Exception { - if (listVersions(tenantId, branch, path).stream().noneMatch(version -> version.getId().equals(versionId))) { - throw new IllegalArgumentException("Version not found"); - } - } - @Override - public List listAllowedBranches(TenantId tenantId) { - return Optional.ofNullable(getSettings()) - .flatMap(settings -> Optional.ofNullable(settings.getTenantsAllowedBranches())) - .flatMap(tenantsAllowedBranches -> Optional.ofNullable(tenantsAllowedBranches.get(tenantId.getId()))) - .orElse(Collections.emptyList()); + public List listBranches(TenantId tenantId) throws Exception { + GitRepository repository = checkRepository(tenantId); + return repository.listBranches(); } - private void checkBranch(TenantId tenantId, String branch) { - if (!listAllowedBranches(tenantId).contains(branch)) { - throw new IllegalArgumentException("Tenant does not have access to the branch"); - } - } + private EntityVersion checkVersion(TenantId tenantId, String branch, String versionId) throws Exception { + return listVersions(tenantId, branch, null).stream() + .filter(version -> version.getId().equals(versionId)) + .findFirst().orElseThrow(() -> new IllegalArgumentException("Version not found")); + } - private void checkRepository() { - if (repository == null) { - throw new IllegalStateException("Repository is not initialized"); - } + private GitRepository checkRepository(TenantId tenantId) { + return Optional.ofNullable(repositories.get(tenantId)) + .orElseThrow(() -> new IllegalStateException("Repository is not initialized")); } - private void initRepository(EntitiesVersionControlSettings settings) throws Exception { - repositoryLock.writeLock().lock(); - try { - if (Files.exists(Path.of(settings.getRepositoryDirectory()))) { - this.repository = GitRepository.open(settings.getRepositoryDirectory(), settings.getUsername(), settings.getPassword()); - } else { - Files.createDirectories(Path.of(settings.getRepositoryDirectory())); - this.repository = GitRepository.clone(settings.getRepositoryUri(), settings.getRepositoryDirectory(), - settings.getUsername(), settings.getPassword()); - } - } finally { - repositoryLock.writeLock().unlock(); + private void initRepository(TenantId tenantId, EntitiesVersionControlSettings settings) throws Exception { + Path repositoryDirectory = Path.of(repositoriesFolder, tenantId.getId().toString()); + GitRepository repository; + if (Files.exists(repositoryDirectory)) { + repository = GitRepository.open(repositoryDirectory.toFile(), settings.getUsername(), settings.getPassword()); + } else { + Files.createDirectories(repositoryDirectory); + repository = GitRepository.clone(settings.getRepositoryUri(), settings.getUsername(), settings.getPassword(), repositoryDirectory.toFile()); } + repositories.put(tenantId, repository); } - private void clearRepository() throws IOException { - repositoryLock.writeLock().lock(); - try { - if (repository != null) { - FileUtils.deleteDirectory(new File(repository.getDirectory())); - repository = null; - } - } finally { - repositoryLock.writeLock().unlock(); + private void clearRepository(TenantId tenantId) throws IOException { + GitRepository repository = repositories.get(tenantId); + if (repository != null) { + FileUtils.deleteDirectory(new File(repository.getDirectory())); + repositories.remove(tenantId); } } @SneakyThrows @Override - public void saveSettings(EntitiesVersionControlSettings settings) { - AdminSettings adminSettings = Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "vc")) - .orElseGet(() -> { - AdminSettings newAdminSettings = new AdminSettings(); - newAdminSettings.setKey(SETTINGS_KEY); - return newAdminSettings; - }); - adminSettings.setJsonValue(JacksonUtil.valueToTree(settings)); - adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, adminSettings); - - repositoryLock.writeLock().lock(); - try { - clearRepository(); - initRepository(settings); - } finally { - repositoryLock.writeLock().unlock(); - } + public void saveSettings(TenantId tenantId, EntitiesVersionControlSettings settings) { + attributesService.save(tenantId, tenantId, DataConstants.SERVER_SCOPE, List.of( + new BaseAttributeKvEntry(System.currentTimeMillis(), new JsonDataEntry(SETTINGS_KEY, JacksonUtil.toString(settings))) + )).get(); - if (settings.getFetchPeriod() != 0) { - fetchTask.cancel(true); - fetchTask = scheduleFetch(settings.getFetchPeriod()); - } + clearRepository(tenantId); + initRepository(tenantId, settings); } + @SneakyThrows @Override - public EntitiesVersionControlSettings getSettings() { - return Optional.ofNullable(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "vc")) - .map(adminSettings -> JacksonUtil.treeToValue(adminSettings.getJsonValue(), EntitiesVersionControlSettings.class)) + public EntitiesVersionControlSettings getSettings(TenantId tenantId) { + return attributesService.find(tenantId, tenantId, DataConstants.SERVER_SCOPE, SETTINGS_KEY).get() + .flatMap(KvEntry::getJsonValue) + .map(json -> { + try { + return JacksonUtil.fromString(json, EntitiesVersionControlSettings.class); + } catch (IllegalArgumentException e) { + return null; + } + }) .orElse(null); } @@ -387,7 +474,7 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont } private EntityId fromRelativePath(String path) { - EntityType entityType = EntityType.valueOf(StringUtils.substringBefore(path, "/")); + EntityType entityType = EntityType.valueOf(StringUtils.substringBefore(path, "/").toUpperCase()); String entityId = StringUtils.substringBetween(path, "/", ".json"); return EntityIdFactory.getByTypeAndUuid(entityType, entityId); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java index 435c661b02..03343ec6f5 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/EntitiesVersionControlService.java @@ -20,19 +20,18 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.sync.vc.data.EntitiesVersionControlSettings; -import org.thingsboard.server.service.sync.vc.data.VersionedEntityInfo; import org.thingsboard.server.service.sync.vc.data.EntityVersion; -import org.thingsboard.server.service.sync.vc.data.EntityVersionLoadResult; -import org.thingsboard.server.service.sync.vc.data.EntityVersionLoadSettings; -import org.thingsboard.server.service.sync.vc.data.EntityVersionSaveSettings; +import org.thingsboard.server.service.sync.vc.data.VersionCreationResult; +import org.thingsboard.server.service.sync.vc.data.VersionLoadResult; +import org.thingsboard.server.service.sync.vc.data.VersionedEntityInfo; +import org.thingsboard.server.service.sync.vc.data.request.load.VersionLoadRequest; +import org.thingsboard.server.service.sync.vc.data.request.create.VersionCreateRequest; import java.util.List; public interface EntitiesVersionControlService { - EntityVersion saveEntityVersion(SecurityUser user, EntityId entityId, String branch, String versionName, EntityVersionSaveSettings settings) throws Exception; - - EntityVersion saveEntitiesVersion(SecurityUser user, List entitiesIds, String branch, String versionName, EntityVersionSaveSettings settings) throws Exception; + VersionCreationResult saveEntitiesVersion(SecurityUser user, VersionCreateRequest request) throws Exception; List listEntityVersions(TenantId tenantId, String branch, EntityId externalId) throws Exception; @@ -42,23 +41,19 @@ public interface EntitiesVersionControlService { List listVersions(TenantId tenantId, String branch) throws Exception; - List listEntitiesAtVersion(TenantId tenantId, EntityType entityType, String branch, String versionId) throws Exception; // will be good to return entity name also + List listEntitiesAtVersion(TenantId tenantId, EntityType entityType, String branch, String versionId) throws Exception; List listAllEntitiesAtVersion(TenantId tenantId, String branch, String versionId) throws Exception; - EntityVersionLoadResult loadEntityVersion(SecurityUser user, EntityId externalId, String branch, String versionId, EntityVersionLoadSettings settings) throws Exception; - - List loadEntityTypeVersion(SecurityUser user, EntityType entityType, String branch, String versionId, EntityVersionLoadSettings settings) throws Exception; - - List loadAllAtVersion(SecurityUser user, String branch, String versionId, EntityVersionLoadSettings settings) throws Exception; + List loadEntitiesVersion(SecurityUser user, VersionLoadRequest request) throws Exception; - List listAllowedBranches(TenantId tenantId); + List listBranches(TenantId tenantId) throws Exception; - void saveSettings(EntitiesVersionControlSettings settings); + void saveSettings(TenantId tenantId, EntitiesVersionControlSettings settings); - EntitiesVersionControlSettings getSettings(); + EntitiesVersionControlSettings getSettings(TenantId tenantId); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntitiesVersionControlSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntitiesVersionControlSettings.java index 06e0a9c7aa..602f5dba4f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntitiesVersionControlSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntitiesVersionControlSettings.java @@ -17,18 +17,10 @@ package org.thingsboard.server.service.sync.vc.data; import lombok.Data; -import java.util.List; -import java.util.Map; -import java.util.UUID; - @Data public class EntitiesVersionControlSettings { - private String repositoryDirectory; private String repositoryUri; private String username; private String password; - - private int fetchPeriod; - - private Map> tenantsAllowedBranches; + private String defaultBranch; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionCreationResult.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionCreationResult.java new file mode 100644 index 0000000000..ac13a7b8bf --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionCreationResult.java @@ -0,0 +1,11 @@ +package org.thingsboard.server.service.sync.vc.data; + +import lombok.Data; + +@Data +public class VersionCreationResult { + private EntityVersion version; + private int added; + private int modified; + private int removed; +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionLoadResult.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionLoadResult.java similarity index 81% rename from application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionLoadResult.java rename to application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionLoadResult.java index 4cf0ee2b0f..cf8b588008 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionLoadResult.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionLoadResult.java @@ -20,14 +20,14 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.ExportableEntity; @Data @AllArgsConstructor @NoArgsConstructor @Builder -public class EntityVersionLoadResult { - private ExportableEntity newEntityVersion; - private ExportableEntity previousEntityVersion; +public class VersionLoadResult { private EntityType entityType; + private int created; + private int updated; + private int deleted; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityFilterExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntitiesByCustomFilterVersionCreateConfig.java similarity index 69% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityFilterExportRequest.java rename to application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntitiesByCustomFilterVersionCreateConfig.java index d71ddc8720..835f684021 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityFilterExportRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntitiesByCustomFilterVersionCreateConfig.java @@ -13,26 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting.data.request; +package org.thingsboard.server.service.sync.vc.data.request.create; import lombok.Data; -import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.query.EntityFilter; import java.util.UUID; -@EqualsAndHashCode(callSuper = true) @Data -public class CustomEntityFilterExportRequest extends ExportRequest { +public class EntitiesByCustomFilterVersionCreateConfig extends VersionCreateConfig { private EntityFilter filter; - private int page; - private int pageSize; private UUID customerId; + private boolean removeOtherRemoteEntitiesOfType; + @Override - public ExportRequestType getType() { - return ExportRequestType.CUSTOM_ENTITY_FILTER; + public VersionCreateConfigType getType() { + return VersionCreateConfigType.CUSTOM_ENTITY_FILTER; } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityQueryExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntitiesByCustomQueryVersionCreateConfig.java similarity index 72% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityQueryExportRequest.java rename to application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntitiesByCustomQueryVersionCreateConfig.java index 946676cf65..6386e9299a 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/CustomEntityQueryExportRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntitiesByCustomQueryVersionCreateConfig.java @@ -13,24 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting.data.request; +package org.thingsboard.server.service.sync.vc.data.request.create; import lombok.Data; -import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.query.EntityDataQuery; import java.util.UUID; -@EqualsAndHashCode(callSuper = true) @Data -public class CustomEntityQueryExportRequest extends ExportRequest { +public class EntitiesByCustomQueryVersionCreateConfig extends VersionCreateConfig { private EntityDataQuery query; private UUID customerId; + private boolean removeOtherRemoteEntitiesOfType; + @Override - public ExportRequestType getType() { - return ExportRequestType.CUSTOM_ENTITY_QUERY; + public VersionCreateConfigType getType() { + return VersionCreateConfigType.CUSTOM_ENTITY_QUERY; } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityListExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityListVersionCreateConfig.java similarity index 73% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityListExportRequest.java rename to application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityListVersionCreateConfig.java index 01309a421b..1f3e1e376f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityListExportRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityListVersionCreateConfig.java @@ -13,23 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting.data.request; +package org.thingsboard.server.service.sync.vc.data.request.create; import lombok.Data; -import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.EntityId; import java.util.List; -@EqualsAndHashCode(callSuper = true) @Data -public class EntityListExportRequest extends ExportRequest { +public class EntityListVersionCreateConfig extends VersionCreateConfig { private List entitiesIds; @Override - public ExportRequestType getType() { - return ExportRequestType.ENTITY_LIST; + public VersionCreateConfigType getType() { + return VersionCreateConfigType.ENTITY_LIST; } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityTypeExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityTypeVersionCreateConfig.java similarity index 74% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityTypeExportRequest.java rename to application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityTypeVersionCreateConfig.java index fd2f659b70..6a8d59b876 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/EntityTypeExportRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityTypeVersionCreateConfig.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting.data.request; +package org.thingsboard.server.service.sync.vc.data.request.create; import lombok.Data; import lombok.EqualsAndHashCode; @@ -21,18 +21,18 @@ import org.thingsboard.server.common.data.EntityType; import java.util.UUID; -@EqualsAndHashCode(callSuper = true) @Data -public class EntityTypeExportRequest extends ExportRequest { +@EqualsAndHashCode(callSuper = true) +public class EntityTypeVersionCreateConfig extends VersionCreateConfig { private EntityType entityType; - private int page; - private int pageSize; private UUID customerId; + private boolean removeOtherRemoteEntitiesOfType; + @Override - public ExportRequestType getType() { - return ExportRequestType.ENTITY_TYPE; + public VersionCreateConfigType getType() { + return VersionCreateConfigType.ENTITY_TYPE; } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/SingleEntityExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SingleEntityVersionCreateConfig.java similarity index 77% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/SingleEntityExportRequest.java rename to application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SingleEntityVersionCreateConfig.java index fe07ba7534..ab23f1175e 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/SingleEntityExportRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SingleEntityVersionCreateConfig.java @@ -13,21 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting.data.request; +package org.thingsboard.server.service.sync.vc.data.request.create; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.id.EntityId; -@EqualsAndHashCode(callSuper = true) @Data -public class SingleEntityExportRequest extends ExportRequest { +@EqualsAndHashCode(callSuper = true) +public class SingleEntityVersionCreateConfig extends VersionCreateConfig { private EntityId entityId; @Override - public ExportRequestType getType() { - return ExportRequestType.SINGLE_ENTITY; + public VersionCreateConfigType getType() { + return VersionCreateConfigType.SINGLE_ENTITY; } } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfig.java similarity index 58% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequest.java rename to application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfig.java index af0f161227..31b7b46f47 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfig.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting.data.request; +package org.thingsboard.server.service.sync.vc.data.request.create; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; @@ -23,17 +23,17 @@ import lombok.Data; @JsonTypeInfo(use = Id.NAME, property = "type") @JsonSubTypes({ - @Type(name = "SINGLE_ENTITY", value = SingleEntityExportRequest.class), - @Type(name = "ENTITY_LIST", value = EntityListExportRequest.class), - @Type(name = "ENTITY_TYPE", value = EntityTypeExportRequest.class), - @Type(name = "CUSTOM_ENTITY_FILTER", value = CustomEntityFilterExportRequest.class), - @Type(name = "CUSTOM_ENTITY_QUERY", value = CustomEntityQueryExportRequest.class) + @Type(name = "SINGLE_ENTITY", value = SingleEntityVersionCreateConfig.class), + @Type(name = "ENTITY_LIST", value = EntityListVersionCreateConfig.class), + @Type(name = "ENTITY_TYPE", value = EntityTypeVersionCreateConfig.class), + @Type(name = "CUSTOM_ENTITY_FILTER", value = EntitiesByCustomFilterVersionCreateConfig.class), + @Type(name = "CUSTOM_ENTITY_QUERY", value = EntitiesByCustomQueryVersionCreateConfig.class) }) @Data -public abstract class ExportRequest { +public abstract class VersionCreateConfig { - private EntityExportSettings exportSettings; + private boolean saveRelations; - public abstract ExportRequestType getType(); + public abstract VersionCreateConfigType getType(); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequestType.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfigType.java similarity index 87% rename from application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequestType.java rename to application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfigType.java index c36ac567e3..b2fb251d6a 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exporting/data/request/ExportRequestType.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfigType.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.exporting.data.request; +package org.thingsboard.server.service.sync.vc.data.request.create; -public enum ExportRequestType { +public enum VersionCreateConfigType { SINGLE_ENTITY, ENTITY_LIST, ENTITY_TYPE, diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionLoadSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequest.java similarity index 73% rename from application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionLoadSettings.java rename to application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequest.java index 842f6ddd4e..47b07af21c 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionLoadSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequest.java @@ -13,12 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.vc.data; +package org.thingsboard.server.service.sync.vc.data.request.create; import lombok.Data; +import java.util.List; + @Data -public class EntityVersionLoadSettings { - private boolean loadRelations; - private boolean findExistingEntityByName; +public class VersionCreateRequest { + + private String versionName; + private String branch; + + private List configs; + } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadConfig.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadConfig.java new file mode 100644 index 0000000000..f210dc9e0c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadConfig.java @@ -0,0 +1,12 @@ +package org.thingsboard.server.service.sync.vc.data.request.load; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class EntityTypeVersionLoadConfig extends EntityVersionLoadConfig { + + private boolean removeNonexistentLocalEntities; + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadRequest.java new file mode 100644 index 0000000000..6a49c45c2c --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadRequest.java @@ -0,0 +1,20 @@ +package org.thingsboard.server.service.sync.vc.data.request.load; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.EntityType; + +import java.util.Map; + +@Data +@EqualsAndHashCode(callSuper = true) +public class EntityTypeVersionLoadRequest extends VersionLoadRequest { + + private Map configs; + + @Override + public VersionLoadRequestType getType() { + return VersionLoadRequestType.ENTITY_TYPE; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityVersionLoadConfig.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityVersionLoadConfig.java new file mode 100644 index 0000000000..f7d99a0981 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityVersionLoadConfig.java @@ -0,0 +1,11 @@ +package org.thingsboard.server.service.sync.vc.data.request.load; + +import lombok.Data; + +@Data +public class EntityVersionLoadConfig { + + private boolean loadRelations; + private boolean findExistingEntityByName; + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/SingleEntityVersionLoadRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/SingleEntityVersionLoadRequest.java new file mode 100644 index 0000000000..f5336b55d1 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/SingleEntityVersionLoadRequest.java @@ -0,0 +1,20 @@ +package org.thingsboard.server.service.sync.vc.data.request.load; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.id.EntityId; + +@Data +@EqualsAndHashCode(callSuper = true) +public class SingleEntityVersionLoadRequest extends VersionLoadRequest { + + private EntityId externalEntityId; + + private EntityVersionLoadConfig config; + + @Override + public VersionLoadRequestType getType() { + return VersionLoadRequestType.SINGLE_ENTITY; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionSaveSettings.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadRequest.java similarity index 74% rename from application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionSaveSettings.java rename to application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadRequest.java index 1fc36463ba..660f5dc18d 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/EntityVersionSaveSettings.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadRequest.java @@ -13,11 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.sync.vc.data; +package org.thingsboard.server.service.sync.vc.data.request.load; import lombok.Data; @Data -public class EntityVersionSaveSettings { - private boolean saveRelations; +public abstract class VersionLoadRequest { + + private String branch; + private String versionId; + + public abstract VersionLoadRequestType getType(); + } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadRequestType.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadRequestType.java new file mode 100644 index 0000000000..178d49668f --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadRequestType.java @@ -0,0 +1,6 @@ +package org.thingsboard.server.service.sync.vc.data.request.load; + +public enum VersionLoadRequestType { + SINGLE_ENTITY, + ENTITY_TYPE +} diff --git a/application/src/main/java/org/thingsboard/server/utils/GitRepository.java b/application/src/main/java/org/thingsboard/server/utils/GitRepository.java index 205bbca50e..14cdbf7b2c 100644 --- a/application/src/main/java/org/thingsboard/server/utils/GitRepository.java +++ b/application/src/main/java/org/thingsboard/server/utils/GitRepository.java @@ -24,6 +24,7 @@ import org.eclipse.jgit.api.GitCommand; import org.eclipse.jgit.api.ListBranchCommand; import org.eclipse.jgit.api.LogCommand; import org.eclipse.jgit.api.RmCommand; +import org.eclipse.jgit.api.Status; import org.eclipse.jgit.api.TransportCommand; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.Constants; @@ -33,18 +34,18 @@ import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.filter.RevFilter; import org.eclipse.jgit.transport.CredentialsProvider; -import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.filter.PathFilter; import java.io.File; import java.io.IOException; -import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; public class GitRepository { @@ -54,6 +55,8 @@ public class GitRepository { @Getter private final String directory; + @Getter + private final ReadWriteLock lock = new ReentrantReadWriteLock(); private GitRepository(Git git, CredentialsProvider credentialsProvider, String directory) { this.git = git; @@ -61,21 +64,20 @@ public class GitRepository { this.directory = directory; } - public static GitRepository clone(String uri, String directory, - String username, String password) throws GitAPIException { + public static GitRepository clone(String uri, String username, String password, File directory) throws GitAPIException { CredentialsProvider credentialsProvider = newCredentialsProvider(username, password); Git git = Git.cloneRepository() .setURI(uri) - .setDirectory(new java.io.File(directory)) + .setDirectory(directory) .setNoCheckout(true) .setCredentialsProvider(credentialsProvider) .call(); - return new GitRepository(git, credentialsProvider, directory); + return new GitRepository(git, credentialsProvider, directory.getAbsolutePath()); } - public static GitRepository open(String directory, String username, String password) throws IOException { - Git git = Git.open(new java.io.File(directory)); - return new GitRepository(git, newCredentialsProvider(username, password), directory); + public static GitRepository open(File directory, String username, String password) throws IOException { + Git git = Git.open(directory); + return new GitRepository(git, newCredentialsProvider(username, password), directory.getAbsolutePath()); } @@ -180,9 +182,17 @@ public class GitRepository { execute(git.clean()); } - - public Commit commit(String message, String filesPattern) throws GitAPIException { + public void add(String filesPattern) throws GitAPIException { // FIXME [viacheslav] + execute(git.add().setUpdate(true).addFilepattern(filesPattern)); execute(git.add().addFilepattern(filesPattern)); + } + + public Status status() throws GitAPIException { + org.eclipse.jgit.api.Status status = execute(git.status()); + return new Status(status.getAdded(), status.getModified(), status.getRemoved()); + } + + public Commit commit(String message) throws GitAPIException { RevCommit revCommit = execute(git.commit() .setMessage(message)); // TODO [viacheslav]: set configurable author for commit return toCommit(revCommit); @@ -256,4 +266,11 @@ public class GitRepository { private final String authorName; } + @Data + public static class Status { + private final Set added; + private final Set modified; + private final Set removed; + } + } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEntitiesExportImportControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEntitiesExportImportControllerTest.java index b5c5a5aab8..988bae7146 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEntitiesExportImportControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEntitiesExportImportControllerTest.java @@ -64,13 +64,13 @@ import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; -import org.thingsboard.server.service.sync.exporting.data.request.ExportRequest; -import org.thingsboard.server.service.sync.exporting.data.request.SingleEntityExportRequest; -import org.thingsboard.server.service.sync.importing.data.EntityImportResult; -import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; -import org.thingsboard.server.service.sync.importing.data.request.ImportRequest; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportSettings; +import org.thingsboard.server.service.sync.vc.data.request.create.VersionCreateConfig; +import org.thingsboard.server.service.sync.vc.data.request.create.SingleEntityVersionCreateConfig; +import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportResult; +import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportSettings; +import org.thingsboard.server.service.sync.exportimport.importing.data.ImportRequest; import java.nio.ByteBuffer; import java.util.Arrays; @@ -339,17 +339,17 @@ public abstract class BaseEntitiesExportImportControllerTest extends AbstractCon protected , I extends EntityId> EntityExportData exportSingleEntity(I entityId) throws Exception { - SingleEntityExportRequest exportRequest = new SingleEntityExportRequest(); + SingleEntityVersionCreateConfig exportRequest = new SingleEntityVersionCreateConfig(); exportRequest.setEntityId(entityId); exportRequest.setExportSettings(new EntityExportSettings()); return (EntityExportData) exportEntities(exportRequest).get(0); } - protected List> exportEntities(ExportRequest exportRequest) throws Exception { + protected List> exportEntities(VersionCreateConfig exportRequest) throws Exception { return getResponse(doPost("/api/entities/export", exportRequest), new TypeReference>>() {}); } - protected List> exportEntities(List exportRequests) throws Exception { + protected List> exportEntities(List exportRequests) throws Exception { return getResponse(doPost("/api/entities/export?multiple", exportRequests), new TypeReference>>() {}); } diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java index bd237e39d0..faa7f3a318 100644 --- a/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/sql/EntitiesExportImportControllerSqlTest.java @@ -39,7 +39,6 @@ import org.thingsboard.server.common.data.id.AssetId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; -import org.thingsboard.server.common.data.query.EntityListFilter; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.rule.RuleChain; @@ -50,18 +49,18 @@ import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.service.action.EntityActionService; import org.thingsboard.server.service.ota.OtaPackageStateService; -import org.thingsboard.server.service.sync.exporting.data.DeviceExportData; -import org.thingsboard.server.service.sync.exporting.data.EntityExportData; -import org.thingsboard.server.service.sync.exporting.data.RuleChainExportData; -import org.thingsboard.server.service.sync.exporting.data.request.CustomEntityFilterExportRequest; -import org.thingsboard.server.service.sync.exporting.data.request.EntityExportSettings; -import org.thingsboard.server.service.sync.exporting.data.request.EntityListExportRequest; -import org.thingsboard.server.service.sync.exporting.data.request.EntityTypeExportRequest; -import org.thingsboard.server.service.sync.exporting.data.request.ExportRequest; -import org.thingsboard.server.service.sync.exporting.data.request.SingleEntityExportRequest; -import org.thingsboard.server.service.sync.importing.data.EntityImportResult; -import org.thingsboard.server.service.sync.importing.data.EntityImportSettings; -import org.thingsboard.server.service.sync.importing.data.request.ImportRequest; +import org.thingsboard.server.service.sync.exportimport.exporting.data.DeviceExportData; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportData; +import org.thingsboard.server.service.sync.exportimport.exporting.data.RuleChainExportData; +import org.thingsboard.server.service.sync.vc.data.request.create.EntitiesByCustomFilterVersionCreateConfig; +import org.thingsboard.server.service.sync.exportimport.exporting.data.EntityExportSettings; +import org.thingsboard.server.service.sync.vc.data.request.create.EntityListVersionCreateConfig; +import org.thingsboard.server.service.sync.vc.data.request.create.EntityTypeVersionCreateConfig; +import org.thingsboard.server.service.sync.vc.data.request.create.VersionCreateConfig; +import org.thingsboard.server.service.sync.vc.data.request.create.SingleEntityVersionCreateConfig; +import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportResult; +import org.thingsboard.server.service.sync.exportimport.importing.data.EntityImportSettings; +import org.thingsboard.server.service.sync.exportimport.importing.data.ImportRequest; import java.util.ArrayList; import java.util.List; @@ -294,11 +293,11 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp dashboard.setConfiguration(dashboardConfiguration); dashboard = dashboardService.saveDashboard(dashboard); - EntityTypeExportRequest assetsExportRequest = new EntityTypeExportRequest(); + EntityTypeVersionCreateConfig assetsExportRequest = new EntityTypeVersionCreateConfig(); assetsExportRequest.setEntityType(EntityType.ASSET); assetsExportRequest.setPageSize(10); assetsExportRequest.setExportSettings(new EntityExportSettings()); - EntityTypeExportRequest dashboardsExportRequest = new EntityTypeExportRequest(); + EntityTypeVersionCreateConfig dashboardsExportRequest = new EntityTypeVersionCreateConfig(); dashboardsExportRequest.setEntityType(EntityType.DASHBOARD); dashboardsExportRequest.setPageSize(10); dashboardsExportRequest.setExportSettings(new EntityExportSettings()); @@ -365,7 +364,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp DeviceProfile deviceProfile = createDeviceProfile(tenantId1, ruleChain.getId(), dashboard.getId(), "Device profile 1"); Device device = createDevice(tenantId1, customer.getId(), deviceProfile.getId(), "Customer 1 - Device 1"); - EntityListExportRequest exportRequest = new EntityListExportRequest(); + EntityListVersionCreateConfig exportRequest = new EntityListVersionCreateConfig(); exportRequest.setExportSettings(new EntityExportSettings()); exportRequest.setEntitiesIds(List.of(customer.getId(), asset.getId(), ruleChain.getId(), deviceProfile.getId(), dashboard.getId())); List> exportDataList = exportEntities(exportRequest); @@ -418,7 +417,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp Device device = createDevice(tenantId1, null, null, "Device 1"); EntityRelation relation = createRelation(asset.getId(), device.getId()); - EntityListExportRequest exportRequest = new EntityListExportRequest(); + EntityListVersionCreateConfig exportRequest = new EntityListVersionCreateConfig(); exportRequest.setEntitiesIds(List.of(asset.getId(), device.getId())); exportRequest.setExportSettings(EntityExportSettings.builder() .exportRelations(true) @@ -464,7 +463,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp Device device = createDevice(tenantId1, null, null, "Device 1"); EntityRelation relation = createRelation(asset.getId(), device.getId()); - EntityListExportRequest exportRequest = new EntityListExportRequest(); + EntityListVersionCreateConfig exportRequest = new EntityListVersionCreateConfig(); exportRequest.setEntitiesIds(List.of(asset.getId(), device.getId())); exportRequest.setExportSettings(EntityExportSettings.builder() .exportRelations(true) @@ -506,7 +505,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp Device device1 = createDevice(tenantId1, null, null, "Device 1"); EntityRelation relation1 = createRelation(asset.getId(), device1.getId()); - SingleEntityExportRequest exportRequest = new SingleEntityExportRequest(); + SingleEntityVersionCreateConfig exportRequest = new SingleEntityVersionCreateConfig(); exportRequest.setEntityId(asset.getId()); exportRequest.setExportSettings(EntityExportSettings.builder() .exportRelations(true) @@ -538,7 +537,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp Device device = createDevice(tenantId1, null, null, "Device 1"); EntityRelation relation1 = createRelation(asset1.getId(), device.getId()); - SingleEntityExportRequest exportRequest = new SingleEntityExportRequest(); + SingleEntityVersionCreateConfig exportRequest = new SingleEntityVersionCreateConfig(); exportRequest.setEntityId(device.getId()); exportRequest.setExportSettings(EntityExportSettings.builder() .exportRelations(true) @@ -568,7 +567,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp logInAsTenantAdmin1(); DeviceProfile defaultDeviceProfile = deviceProfileService.findDefaultDeviceProfile(tenantId1); - EntityListExportRequest exportRequest = new EntityListExportRequest(); + EntityListVersionCreateConfig exportRequest = new EntityListVersionCreateConfig(); exportRequest.setEntitiesIds(List.of(defaultDeviceProfile.getId())); exportRequest.setExportSettings(new EntityExportSettings()); List> exportDataList = exportEntities(exportRequest); @@ -613,7 +612,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp } private void testEntityTypeExportRequest(ExportableEntity entity) throws Exception { - EntityTypeExportRequest exportRequest = new EntityTypeExportRequest(); + EntityTypeVersionCreateConfig exportRequest = new EntityTypeVersionCreateConfig(); exportRequest.setExportSettings(new EntityExportSettings()); exportRequest.setPageSize(10); exportRequest.setEntityType(entity.getId().getEntityType()); @@ -626,11 +625,11 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp } private void testCustomEntityFilterExportRequest(ExportableEntity entity) throws Exception { - CustomEntityFilterExportRequest exportRequest = new CustomEntityFilterExportRequest(); + EntitiesByCustomFilterVersionCreateConfig exportRequest = new EntitiesByCustomFilterVersionCreateConfig(); exportRequest.setExportSettings(new EntityExportSettings()); exportRequest.setPageSize(10); - EntityListFilter filter = new EntityListFilter(); + org.thingsboard.server.common.data.query.EntityListFilter filter = new org.thingsboard.server.common.data.query.EntityListFilter(); filter.setEntityType(entity.getId().getEntityType()); filter.setEntityList(List.of(entity.getId().toString())); exportRequest.setFilter(filter); @@ -652,10 +651,10 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp Asset tenantAsset = createAsset(tenantId1, null, "A", "Tenant asset 1"); Asset customerAsset = createAsset(tenantId1, customer.getId(), "A", "Customer asset 1"); - List exportRequests = new ArrayList<>(); + List exportRequests = new ArrayList<>(); for (EntityType entityType : Set.of(EntityType.DEVICE, EntityType.ASSET)) { - EntityTypeExportRequest exportRequest = new EntityTypeExportRequest(); + EntityTypeVersionCreateConfig exportRequest = new EntityTypeVersionCreateConfig(); exportRequest.setExportSettings(new EntityExportSettings()); exportRequest.setPageSize(10); exportRequest.setEntityType(entityType); @@ -685,7 +684,7 @@ public class EntitiesExportImportControllerSqlTest extends BaseEntitiesExportImp DeviceProfile deviceProfile = createDeviceProfile(tenantId1, ruleChain.getId(), dashboard.getId(), "Device profile 1"); Device device = createDevice(tenantId1, null, deviceProfile.getId(), "Device 1"); - EntityListExportRequest exportRequest = new EntityListExportRequest(); + EntityListVersionCreateConfig exportRequest = new EntityListVersionCreateConfig(); exportRequest.setEntitiesIds(List.of(customer.getId(), asset.getId(), device.getId(), ruleChain.getId(), dashboard.getId(), deviceProfile.getId())); exportRequest.setExportSettings(new EntityExportSettings()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java index 18444bb5f3..9240838543 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java +++ b/dao/src/main/java/org/thingsboard/server/dao/DaoUtil.java @@ -32,6 +32,8 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.function.Consumer; +import java.util.function.Function; public abstract class DaoUtil { @@ -51,7 +53,7 @@ public abstract class DaoUtil { return toPageable(pageLink, Collections.emptyMap()); } - public static Pageable toPageable(PageLink pageLink, Map columnMap) { + public static Pageable toPageable(PageLink pageLink, Map columnMap) { return PageRequest.of(pageLink.getPage(), pageLink.getPageSize(), pageLink.toSort(pageLink.getSortOrder(), columnMap)); } @@ -59,7 +61,7 @@ public abstract class DaoUtil { return toPageable(pageLink, Collections.emptyMap(), sortOrders); } - public static Pageable toPageable(PageLink pageLink, Map columnMap, List sortOrders) { + public static Pageable toPageable(PageLink pageLink, Map columnMap, List sortOrders) { return PageRequest.of(pageLink.getPage(), pageLink.getPageSize(), pageLink.toSort(sortOrders, columnMap)); } @@ -108,4 +110,18 @@ public abstract class DaoUtil { return ids; } + public static void processInBatches(Function> finder, int batchSize, Consumer processor) { + PageLink pageLink = new PageLink(batchSize); + PageData batch; + + boolean hasNextBatch; + do { + batch = finder.apply(pageLink); + batch.getData().forEach(processor); + + hasNextBatch = batch.hasNext(); + pageLink = pageLink.nextPageLink(); + } while (hasNextBatch); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java index cd534f24e6..aaa35e109a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ExportableEntityDao.java @@ -16,6 +16,8 @@ package org.thingsboard.server.dao; import org.thingsboard.server.common.data.ExportableEntity; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import java.util.UUID; @@ -25,4 +27,6 @@ public interface ExportableEntityDao> extends Dao< default T findByTenantIdAndName(UUID tenantId, String name) { throw new UnsupportedOperationException(); } + PageData findByTenantId(UUID tenantId, PageLink pageLink); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java index bcfdaae965..8879809ddb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/asset/JpaAssetDao.java @@ -219,6 +219,11 @@ public class JpaAssetDao extends JpaAbstractSearchTextDao im return findAssetsByTenantIdAndName(tenantId, name).orElse(null); } + @Override + public PageData findByTenantId(UUID tenantId, PageLink pageLink) { + return findAssetsByTenantId(tenantId, pageLink); + } + @Override public EntityType getEntityType() { return EntityType.ASSET; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java index c6ba310793..034431157a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/customer/JpaCustomerDao.java @@ -80,6 +80,11 @@ public class JpaCustomerDao extends JpaAbstractSearchTextDao findByTenantId(UUID tenantId, PageLink pageLink) { + return findCustomersByTenantId(tenantId, pageLink); + } + @Override public EntityType getEntityType() { return EntityType.CUSTOMER; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardRepository.java index 4a870b2a21..847314f1a6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/DashboardRepository.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.dao.sql.dashboard; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.thingsboard.server.dao.ExportableEntityRepository; import org.thingsboard.server.dao.model.sql.DashboardEntity; @@ -31,4 +33,6 @@ public interface DashboardRepository extends JpaRepository findByTenantIdAndTitle(UUID tenantId, String title); + Page findByTenantId(UUID tenantId, Pageable pageable); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java index 6ff9fc6bfb..8fa0cbdc50 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/dashboard/JpaDashboardDao.java @@ -21,6 +21,8 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.dashboard.DashboardDao; import org.thingsboard.server.dao.model.sql.DashboardEntity; @@ -58,6 +60,11 @@ public class JpaDashboardDao extends JpaAbstractSearchTextDao findByTenantId(UUID tenantId, PageLink pageLink) { + return DaoUtil.toPageData(dashboardRepository.findByTenantId(tenantId, DaoUtil.toPageable(pageLink))); + } + @Override public List findByTenantIdAndTitle(UUID tenantId, String title) { return DaoUtil.convertDataList(dashboardRepository.findByTenantIdAndTitle(tenantId, title)); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java index 93cd857660..d4663f60a4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java @@ -313,6 +313,11 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao return findDeviceByTenantIdAndName(tenantId, name).orElse(null); } + @Override + public PageData findByTenantId(UUID tenantId, PageLink pageLink) { + return findDevicesByTenantId(tenantId, pageLink); + } + @Override public EntityType getEntityType() { return EntityType.DEVICE; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java index cae94cfb73..4cab209f49 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java @@ -121,6 +121,11 @@ public class JpaDeviceProfileDao extends JpaAbstractSearchTextDao findByTenantId(UUID tenantId, PageLink pageLink) { + return findDeviceProfiles(TenantId.fromUUID(tenantId), pageLink); + } + @Override public EntityType getEntityType() { return EntityType.DEVICE_PROFILE; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java index 0d5fea6f98..2703c99fba 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/rule/JpaRuleChainDao.java @@ -114,6 +114,11 @@ public class JpaRuleChainDao extends JpaAbstractSearchTextDao findByTenantId(UUID tenantId, PageLink pageLink) { + return findRuleChainsByTenantId(tenantId, pageLink); + } + @Override public EntityType getEntityType() { return EntityType.RULE_CHAIN; From 2302213b3c8f288fa8284973e668285509f5e671 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Mon, 16 May 2022 17:03:23 +0300 Subject: [PATCH 38/39] Refactor version create request structures --- .../DefaultEntitiesVersionControlService.java | 81 ++++++++++--------- ...tiesByCustomFilterVersionCreateConfig.java | 36 --------- ...itiesByCustomQueryVersionCreateConfig.java | 36 --------- .../create/EntityListVersionCreateConfig.java | 33 -------- .../EntityListVersionCreateRequest.java | 21 +++++ .../create/EntityTypeVersionCreateConfig.java | 38 --------- .../EntityTypeVersionCreateRequest.java | 20 +++++ .../MultipleEntitiesVersionCreateConfig.java | 10 +++ .../SingleEntityVersionCreateConfig.java | 33 -------- .../SingleEntityVersionCreateRequest.java | 19 +++++ .../vc/data/request/create/SyncStrategy.java | 6 ++ .../request/create/VersionCreateConfig.java | 16 ---- .../create/VersionCreateConfigType.java | 25 ------ .../request/create/VersionCreateRequest.java | 6 +- .../create/VersionCreateRequestType.java | 7 ++ .../load/EntityTypeVersionLoadConfig.java | 2 +- 16 files changed, 127 insertions(+), 262 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntitiesByCustomFilterVersionCreateConfig.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntitiesByCustomQueryVersionCreateConfig.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityListVersionCreateConfig.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityListVersionCreateRequest.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityTypeVersionCreateConfig.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityTypeVersionCreateRequest.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/MultipleEntitiesVersionCreateConfig.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SingleEntityVersionCreateConfig.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SingleEntityVersionCreateRequest.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SyncStrategy.java delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfigType.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequestType.java diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java index 919960ce62..f6434daecc 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java @@ -65,6 +65,7 @@ import org.thingsboard.server.service.sync.vc.data.EntityVersion; import org.thingsboard.server.service.sync.vc.data.VersionCreationResult; import org.thingsboard.server.service.sync.vc.data.VersionLoadResult; import org.thingsboard.server.service.sync.vc.data.VersionedEntityInfo; +import org.thingsboard.server.service.sync.vc.data.request.create.MultipleEntitiesVersionCreateConfig; import org.thingsboard.server.service.sync.vc.data.request.load.VersionLoadRequest; import org.thingsboard.server.service.sync.vc.data.request.load.VersionLoadSettings; import org.thingsboard.server.service.sync.vc.data.request.create.VersionCreateRequest; @@ -152,23 +153,24 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont if (repository.listBranches().contains(request.getBranch())) { repository.checkout(request.getBranch()); repository.merge(request.getBranch()); - } else { // FIXME [viacheslav]: rollback orphan branch on failure + } else { // TODO [viacheslav]: rollback orphan branch on failure repository.createAndCheckoutOrphanBranch(request.getBranch()); // FIXME [viacheslav]: Checkout returned unexpected result NO_CHANGE for master branch } - for (VersionCreateRequest.Config config : request.getConfigs()) { + for (VersionCreateConfig config : request.getConfigs()) { EntityExportSettings exportSettings = EntityExportSettings.builder() .exportRelations(config.isSaveRelations()) .build(); List> entityDataList = new ArrayList<>(); - for (EntityId entityId : findEntities()) { + for (EntityId entityId : findEntities(user, config, 0, Integer.MAX_VALUE)) { // TODO [viacheslav]: find with pagination EntityExportData> entityData = exportImportService.exportEntity(user, entityId, exportSettings); entityDataList.add(entityData); } - if (config.isRemoveOtherRemoteEntitiesOfType()) { - entityDataList.stream() + if (config instanceof MultipleEntitiesVersionCreateConfig && + ((MultipleEntitiesVersionCreateConfig) config).isRemoveOtherRemoteEntitiesOfType()) { + entityDataList.stream() // FIXME [viacheslav]: in case of an emtpy entity type? none will be deleted on remote? .map(EntityExportData::getEntityType) .distinct() .forEach(entityType -> { @@ -186,7 +188,6 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont entityData.getEntity().getId().toString())).toFile(), entityDataJson, StandardCharsets.UTF_8); } } - // TODO [viacheslav]: find with pagination repository.add("."); @@ -206,18 +207,18 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont } } - private List findEntities(SecurityUser user, VersionCreateConfig entityFilter, int page, int pageSize) { - switch (entityFilter.getType()) { + private List findEntities(SecurityUser user, VersionCreateConfig config, int page, int pageSize) { + switch (config.getType()) { case SINGLE_ENTITY: { - SingleEntityVersionCreateConfig filter = (SingleEntityVersionCreateConfig) entityFilter; + SingleEntityVersionCreateConfig filter = (SingleEntityVersionCreateConfig) config; return List.of(filter.getEntityId()); } case ENTITY_LIST: { - EntityListVersionCreateConfig filter = (EntityListVersionCreateConfig) entityFilter; + EntityListVersionCreateConfig filter = (EntityListVersionCreateConfig) config; return filter.getEntitiesIds(); } case ENTITY_TYPE: { - EntityTypeVersionCreateConfig filter = (EntityTypeVersionCreateConfig) entityFilter; + EntityTypeVersionCreateConfig filter = (EntityTypeVersionCreateConfig) config; EntitiesByCustomFilterVersionCreateConfig newFilter = new EntitiesByCustomFilterVersionCreateConfig(); org.thingsboard.server.common.data.query.EntityTypeFilter entityTypeFilter = new org.thingsboard.server.common.data.query.EntityTypeFilter(); @@ -228,7 +229,7 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont return findEntities(user, newFilter, page, pageSize); } case CUSTOM_ENTITY_FILTER: { - EntitiesByCustomFilterVersionCreateConfig filter = (EntitiesByCustomFilterVersionCreateConfig) entityFilter; + EntitiesByCustomFilterVersionCreateConfig filter = (EntitiesByCustomFilterVersionCreateConfig) config; EntitiesByCustomQueryVersionCreateConfig newFilter = new EntitiesByCustomQueryVersionCreateConfig(); EntityDataPageLink pageLink = new EntityDataPageLink(); @@ -243,7 +244,7 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont return findEntities(user, newFilter, page, pageSize); } case CUSTOM_ENTITY_QUERY: { - EntitiesByCustomQueryVersionCreateConfig filter = (EntitiesByCustomQueryVersionCreateConfig) entityFilter; + EntitiesByCustomQueryVersionCreateConfig filter = (EntitiesByCustomQueryVersionCreateConfig) config; CustomerId customerId = new CustomerId(ObjectUtils.defaultIfNull(filter.getCustomerId(), EntityId.NULL_UUID)); return entityService.findEntityDataByQuery(user.getTenantId(), customerId, filter.getQuery()).getData() .stream().map(EntityData::getEntityId) @@ -341,35 +342,35 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont boolean removeNonexistentLocalEntities = false; if () - if (request.isRemoveOtherLocalEntitiesOfType()) { - importResults.stream() - .collect(Collectors.groupingBy(EntityImportResult::getEntityType)) // FIXME [viacheslav]: if no entities of entity type - remove all ? - .forEach((entityType, resultsForEntityType) -> { - Set modifiedEntities = resultsForEntityType.stream().map(EntityImportResult::getSavedEntity).map(ExportableEntity::getExternalId).collect(Collectors.toSet()); - AtomicInteger deleted = new AtomicInteger(); - - DaoUtil.processInBatches(pageLink -> { - return exportableEntitiesService.findEntitiesByTenantId(user.getTenantId(), entityType, pageLink); - }, 100, entity -> { - if (entity.getExternalId() == null || !modifiedEntities.contains(entity.getExternalId())) { - try { - exportableEntitiesService.checkPermission(user, entity, entityType, Operation.DELETE); - } catch (ThingsboardException e) { - throw new RuntimeException(e); + if (request.isRemoveOtherLocalEntitiesOfType()) { + importResults.stream() + .collect(Collectors.groupingBy(EntityImportResult::getEntityType)) // FIXME [viacheslav]: if no entities of entity type - remove all ? + .forEach((entityType, resultsForEntityType) -> { + Set modifiedEntities = resultsForEntityType.stream().map(EntityImportResult::getSavedEntity).map(ExportableEntity::getExternalId).collect(Collectors.toSet()); + AtomicInteger deleted = new AtomicInteger(); + + DaoUtil.processInBatches(pageLink -> { + return exportableEntitiesService.findEntitiesByTenantId(user.getTenantId(), entityType, pageLink); + }, 100, entity -> { + if (entity.getExternalId() == null || !modifiedEntities.contains(entity.getExternalId())) { + try { + exportableEntitiesService.checkPermission(user, entity, entityType, Operation.DELETE); + } catch (ThingsboardException e) { + throw new RuntimeException(e); + } + // need to delete in a specific order? + exportableEntitiesService.deleteByTenantIdAndId(user.getTenantId(), entity.getId()); + deleted.getAndIncrement(); } - // need to delete in a specific order? - exportableEntitiesService.deleteByTenantIdAndId(user.getTenantId(), entity.getId()); - deleted.getAndIncrement(); - } + }); + results.put(entityType, VersionLoadResult.builder() + .entityType(entityType) + .created((int) resultsForEntityType.stream().filter(importResult -> importResult.getOldEntity() == null).count()) + .updated((int) resultsForEntityType.stream().filter(importResult -> importResult.getOldEntity() != null).count()) + .deleted(deleted.get()) + .build()); }); - results.put(entityType, VersionLoadResult.builder() - .entityType(entityType) - .created((int) resultsForEntityType.stream().filter(importResult -> importResult.getOldEntity() == null).count()) - .updated((int) resultsForEntityType.stream().filter(importResult -> importResult.getOldEntity() != null).count()) - .deleted(deleted.get()) - .build()); - }); - } + } return new ArrayList<>(results.values()); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntitiesByCustomFilterVersionCreateConfig.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntitiesByCustomFilterVersionCreateConfig.java deleted file mode 100644 index 835f684021..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntitiesByCustomFilterVersionCreateConfig.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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.sync.vc.data.request.create; - -import lombok.Data; -import org.thingsboard.server.common.data.query.EntityFilter; - -import java.util.UUID; - -@Data -public class EntitiesByCustomFilterVersionCreateConfig extends VersionCreateConfig { - - private EntityFilter filter; - private UUID customerId; - - private boolean removeOtherRemoteEntitiesOfType; - - @Override - public VersionCreateConfigType getType() { - return VersionCreateConfigType.CUSTOM_ENTITY_FILTER; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntitiesByCustomQueryVersionCreateConfig.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntitiesByCustomQueryVersionCreateConfig.java deleted file mode 100644 index 6386e9299a..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntitiesByCustomQueryVersionCreateConfig.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * 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.sync.vc.data.request.create; - -import lombok.Data; -import org.thingsboard.server.common.data.query.EntityDataQuery; - -import java.util.UUID; - -@Data -public class EntitiesByCustomQueryVersionCreateConfig extends VersionCreateConfig { - - private EntityDataQuery query; - private UUID customerId; - - private boolean removeOtherRemoteEntitiesOfType; - - @Override - public VersionCreateConfigType getType() { - return VersionCreateConfigType.CUSTOM_ENTITY_QUERY; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityListVersionCreateConfig.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityListVersionCreateConfig.java deleted file mode 100644 index 1f3e1e376f..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityListVersionCreateConfig.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * 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.sync.vc.data.request.create; - -import lombok.Data; -import org.thingsboard.server.common.data.id.EntityId; - -import java.util.List; - -@Data -public class EntityListVersionCreateConfig extends VersionCreateConfig { - - private List entitiesIds; - - @Override - public VersionCreateConfigType getType() { - return VersionCreateConfigType.ENTITY_LIST; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityListVersionCreateRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityListVersionCreateRequest.java new file mode 100644 index 0000000000..fadd9b1fd9 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityListVersionCreateRequest.java @@ -0,0 +1,21 @@ +package org.thingsboard.server.service.sync.vc.data.request.create; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.id.EntityId; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +public class EntityListVersionCreateRequest extends VersionCreateRequest { + + private List entitiesIds; + private MultipleEntitiesVersionCreateConfig config; + + @Override + public VersionCreateRequestType getType() { + return VersionCreateRequestType.ENTITY_LIST; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityTypeVersionCreateConfig.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityTypeVersionCreateConfig.java deleted file mode 100644 index 6a8d59b876..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityTypeVersionCreateConfig.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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.sync.vc.data.request.create; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.EntityType; - -import java.util.UUID; - -@Data -@EqualsAndHashCode(callSuper = true) -public class EntityTypeVersionCreateConfig extends VersionCreateConfig { - - private EntityType entityType; - private UUID customerId; - - private boolean removeOtherRemoteEntitiesOfType; - - @Override - public VersionCreateConfigType getType() { - return VersionCreateConfigType.ENTITY_TYPE; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityTypeVersionCreateRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityTypeVersionCreateRequest.java new file mode 100644 index 0000000000..0fd1d60667 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityTypeVersionCreateRequest.java @@ -0,0 +1,20 @@ +package org.thingsboard.server.service.sync.vc.data.request.create; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.EntityType; + +import java.util.Map; + +@Data +@EqualsAndHashCode(callSuper = true) +public class EntityTypeVersionCreateRequest extends VersionCreateRequest { + + private Map entityTypes; + + @Override + public VersionCreateRequestType getType() { + return VersionCreateRequestType.ENTITY_TYPE; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/MultipleEntitiesVersionCreateConfig.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/MultipleEntitiesVersionCreateConfig.java new file mode 100644 index 0000000000..f6d852875d --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/MultipleEntitiesVersionCreateConfig.java @@ -0,0 +1,10 @@ +package org.thingsboard.server.service.sync.vc.data.request.create; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public abstract class MultipleEntitiesVersionCreateConfig extends VersionCreateConfig { + private SyncStrategy syncStrategy; +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SingleEntityVersionCreateConfig.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SingleEntityVersionCreateConfig.java deleted file mode 100644 index ab23f1175e..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SingleEntityVersionCreateConfig.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * 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.sync.vc.data.request.create; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.id.EntityId; - -@Data -@EqualsAndHashCode(callSuper = true) -public class SingleEntityVersionCreateConfig extends VersionCreateConfig { - - private EntityId entityId; - - @Override - public VersionCreateConfigType getType() { - return VersionCreateConfigType.SINGLE_ENTITY; - } - -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SingleEntityVersionCreateRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SingleEntityVersionCreateRequest.java new file mode 100644 index 0000000000..31bdeca658 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SingleEntityVersionCreateRequest.java @@ -0,0 +1,19 @@ +package org.thingsboard.server.service.sync.vc.data.request.create; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.id.EntityId; + +@Data +@EqualsAndHashCode(callSuper = true) +public class SingleEntityVersionCreateRequest extends VersionCreateRequest { + + private EntityId entityId; + private VersionCreateConfig config; + + @Override + public VersionCreateRequestType getType() { + return VersionCreateRequestType.SINGLE_ENTITY; + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SyncStrategy.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SyncStrategy.java new file mode 100644 index 0000000000..6216ad7658 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SyncStrategy.java @@ -0,0 +1,6 @@ +package org.thingsboard.server.service.sync.vc.data.request.create; + +public enum SyncStrategy { + MERGE, + OVERWRITE +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfig.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfig.java index 31b7b46f47..28f3232cb9 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfig.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfig.java @@ -15,25 +15,9 @@ */ package org.thingsboard.server.service.sync.vc.data.request.create; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonSubTypes.Type; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import lombok.Data; -@JsonTypeInfo(use = Id.NAME, property = "type") -@JsonSubTypes({ - @Type(name = "SINGLE_ENTITY", value = SingleEntityVersionCreateConfig.class), - @Type(name = "ENTITY_LIST", value = EntityListVersionCreateConfig.class), - @Type(name = "ENTITY_TYPE", value = EntityTypeVersionCreateConfig.class), - @Type(name = "CUSTOM_ENTITY_FILTER", value = EntitiesByCustomFilterVersionCreateConfig.class), - @Type(name = "CUSTOM_ENTITY_QUERY", value = EntitiesByCustomQueryVersionCreateConfig.class) -}) @Data public abstract class VersionCreateConfig { - private boolean saveRelations; - - public abstract VersionCreateConfigType getType(); - } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfigType.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfigType.java deleted file mode 100644 index b2fb251d6a..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfigType.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * 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.sync.vc.data.request.create; - -public enum VersionCreateConfigType { - SINGLE_ENTITY, - ENTITY_LIST, - ENTITY_TYPE, - - CUSTOM_ENTITY_FILTER, - CUSTOM_ENTITY_QUERY -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequest.java index 47b07af21c..e11e2f85a3 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequest.java @@ -17,14 +17,12 @@ package org.thingsboard.server.service.sync.vc.data.request.create; import lombok.Data; -import java.util.List; - @Data -public class VersionCreateRequest { +public abstract class VersionCreateRequest { private String versionName; private String branch; - private List configs; + public abstract VersionCreateRequestType getType(); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequestType.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequestType.java new file mode 100644 index 0000000000..a5d0447b40 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequestType.java @@ -0,0 +1,7 @@ +package org.thingsboard.server.service.sync.vc.data.request.create; + +public enum VersionCreateRequestType { + SINGLE_ENTITY, + ENTITY_LIST, + ENTITY_TYPE +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadConfig.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadConfig.java index f210dc9e0c..b5ee5ddd50 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadConfig.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadConfig.java @@ -7,6 +7,6 @@ import lombok.EqualsAndHashCode; @EqualsAndHashCode(callSuper = true) public class EntityTypeVersionLoadConfig extends EntityVersionLoadConfig { - private boolean removeNonexistentLocalEntities; + private boolean removeOtherEntities; } From 5a43788b2498700135b4df4f6f1e5d7ed81b4f5c Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Tue, 17 May 2022 15:32:07 +0300 Subject: [PATCH 39/39] VC refactoring; Swagger examples --- .../EntitiesVersionControlController.java | 141 ++++++- .../DefaultEntitiesExportImportService.java | 40 +- .../EntitiesExportImportService.java | 5 + .../DefaultExportableEntitiesService.java | 46 --- .../exporting/ExportableEntitiesService.java | 2 - .../DefaultEntitiesVersionControlService.java | 386 +++++++++--------- .../sync/vc/data/VersionCreationResult.java | 15 + .../sync/vc/data/VersionedEntityInfo.java | 1 - .../EntityListVersionCreateRequest.java | 15 + .../EntityTypeVersionCreateRequest.java | 15 + .../MultipleEntitiesVersionCreateConfig.java | 17 +- .../SingleEntityVersionCreateRequest.java | 15 + .../vc/data/request/create/SyncStrategy.java | 15 + .../request/create/VersionCreateConfig.java | 2 +- .../request/create/VersionCreateRequest.java | 9 + .../create/VersionCreateRequestType.java | 15 + .../load/EntityTypeVersionLoadConfig.java | 17 +- .../load/EntityTypeVersionLoadRequest.java | 17 +- .../request/load/EntityVersionLoadConfig.java | 11 - .../load/SingleEntityVersionLoadRequest.java | 17 +- .../data/request/load/VersionLoadConfig.java | 26 ++ .../data/request/load/VersionLoadRequest.java | 9 + .../request/load/VersionLoadRequestType.java | 15 + .../server/utils/GitRepository.java | 4 +- 24 files changed, 580 insertions(+), 275 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityVersionLoadConfig.java create mode 100644 application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadConfig.java diff --git a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java index 0625dbeb38..e7df5acdc3 100644 --- a/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java +++ b/application/src/main/java/org/thingsboard/server/controller/EntitiesVersionControlController.java @@ -16,6 +16,7 @@ package org.thingsboard.server.controller; import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiOperation; import lombok.Data; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; @@ -44,6 +45,8 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; +import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE; + @RestController @RequestMapping("/api/entities/vc") @PreAuthorize("hasAuthority('TENANT_ADMIN')") @@ -53,6 +56,62 @@ public class EntitiesVersionControlController extends BaseController { private final EntitiesVersionControlService versionControlService; + @ApiOperation(value = "", notes = "" + + "SINGLE_ENTITY:" + NEW_LINE + + "```\n{\n" + + " \"type\": \"SINGLE_ENTITY\",\n" + + "\n" + + " \"versionName\": \"Version 1.0\",\n" + + " \"branch\": \"dev\",\n" + + "\n" + + " \"entityId\": {\n" + + " \"entityType\": \"DEVICE\",\n" + + " \"id\": \"b79448e0-d4f4-11ec-847b-0f432358ab48\"\n" + + " },\n" + + " \"config\": {\n" + + " \"saveRelations\": true\n" + + " }\n" + + "}\n```" + NEW_LINE + + "ENTITY_LIST:" + NEW_LINE + + "```\n{\n" + + " \"type\": \"ENTITY_LIST\",\n" + + "\n" + + " \"versionName\": \"Version 1.0\",\n" + + " \"branch\": \"dev\",\n" + + "\n" + + " \"entitiesIds\": [\n" + + " {\n" + + " \"entityType\": \"DEVICE\",\n" + + " \"id\": \"b79448e0-d4f4-11ec-847b-0f432358ab48\"\n" + + " },\n" + + " {\n" + + " \"entityType\": \"DEVICE_PROFILE\",\n" + + " \"id\": \"b7944123-d4f4-11ec-847b-0f432358ab48\"\n" + + " }\n" + + " ],\n" + + " \"config\": {\n" + + " \"saveRelations\": true,\n" + + " \"syncStrategy\": \"MERGE\"\n" + + " }\n" + + "}\n```" + NEW_LINE + + "ENTITY_TYPE:" + NEW_LINE + + "```\n{\n" + + " \"type\": \"ENTITY_TYPE\",\n" + + "\n" + + " \"versionName\": \"Version 1.0\",\n" + + " \"branch\": \"dev\",\n" + + "\n" + + " \"entityTypes\": {\n" + + " \"DEVICE\": {\n" + + " \"saveRelations\": true,\n" + + " \"syncStrategy\": \"MERGE\"\n" + + " },\n" + + " \"DEVICE_PROFILE\": {\n" + + " \"saveRelations\": true,\n" + + " \"syncStrategy\": \"OVERWRITE\"\n" + + " }\n" + + " }\n" + + "}\n```") @PostMapping("/version") public VersionCreationResult saveEntitiesVersion(@RequestBody VersionCreateRequest request) throws ThingsboardException { SecurityUser user = getCurrentUser(); @@ -64,6 +123,13 @@ public class EntitiesVersionControlController extends BaseController { } + @ApiOperation(value = "", notes = "" + + "```\n[\n" + + " {\n" + + " \"id\": \"c30c8bcaed3f0813649f0dee51a89d04d0a12b28\",\n" + + " \"name\": \"Device profile 1 version 1.0\"\n" + + " }\n" + + "]\n```") @GetMapping("/version/{branch}/{entityType}/{externalEntityUuid}") public List listEntityVersions(@PathVariable String branch, @PathVariable EntityType entityType, @@ -76,6 +142,13 @@ public class EntitiesVersionControlController extends BaseController { } } + @ApiOperation(value = "", notes = "" + + "```\n[\n" + + " {\n" + + " \"id\": \"c30c8bcaed3f0813649f0dee51a89d04d0a12b28\",\n" + + " \"name\": \"Device profiles from dev\"\n" + + " }\n" + + "]\n```") @GetMapping("/version/{branch}/{entityType}") public List listEntityTypeVersions(@PathVariable String branch, @PathVariable EntityType entityType) throws ThingsboardException { @@ -86,6 +159,21 @@ public class EntitiesVersionControlController extends BaseController { } } + @ApiOperation(value = "", notes = "" + + "```\n[\n" + + " {\n" + + " \"id\": \"ba9baaca1742b730e7331f31a6a51da5fc7da7f7\",\n" + + " \"name\": \"Device 1 removed\"\n" + + " },\n" + + " {\n" + + " \"id\": \"b3c28d722d328324c7c15b0b30047b0c40011cf7\",\n" + + " \"name\": \"Device profiles added\"\n" + + " },\n" + + " {\n" + + " \"id\": \"c30c8bcaed3f0813649f0dee51a89d04d0a12b28\",\n" + + " \"name\": \"Devices added\"\n" + + " }\n" + + "]\n```") @GetMapping("/version/{branch}") public List listVersions(@PathVariable String branch) throws ThingsboardException { try { @@ -118,6 +206,38 @@ public class EntitiesVersionControlController extends BaseController { } + @ApiOperation(value = "", notes = "" + + "SINGLE_ENTITY:" + NEW_LINE + + "```\n{\n" + + " \"type\": \"SINGLE_ENTITY\",\n" + + " \n" + + " \"branch\": \"dev\",\n" + + " \"versionId\": \"b3c28d722d328324c7c15b0b30047b0c40011cf7\",\n" + + " \n" + + " \"externalEntityId\": {\n" + + " \"entityType\": \"DEVICE\",\n" + + " \"id\": \"b7944123-d4f4-11ec-847b-0f432358ab48\"\n" + + " },\n" + + " \"config\": {\n" + + " \"loadRelations\": false,\n" + + " \"findExistingEntityByName\": false\n" + + " }\n" + + "}\n```" + NEW_LINE + + "ENTITY_TYPE:" + NEW_LINE + + "```\n{\n" + + " \"type\": \"ENTITY_TYPE\",\n" + + "\n" + + " \"branch\": \"dev\",\n" + + " \"versionId\": \"b3c28d722d328324c7c15b0b30047b0c40011cf7\",\n" + + "\n" + + " \"entityTypes\": {\n" + + " \"DEVICE\": {\n" + + " \"loadRelations\": false,\n" + + " \"findExistingEntityByName\": false,\n" + + " \"removeOtherEntities\": true\n" + + " }\n" + + " }\n" + + "}\n```") @PostMapping("/entity") public List loadEntitiesVersion(@RequestBody VersionLoadRequest request) throws ThingsboardException { SecurityUser user = getCurrentUser(); @@ -139,8 +259,21 @@ public class EntitiesVersionControlController extends BaseController { } - @ApiModelProperty(notes = "" + - "") + @ApiOperation(value = "", notes = "" + + "```\n[\n" + + " {\n" + + " \"name\": \"master\",\n" + + " \"default\": true\n" + + " },\n" + + " {\n" + + " \"name\": \"dev\",\n" + + " \"default\": false\n" + + " },\n" + + " {\n" + + " \"name\": \"dev-2\",\n" + + " \"default\": false\n" + + " }\n" + + "]\n\n```") @GetMapping("/branches") public List listBranches() throws ThingsboardException { try { @@ -161,7 +294,7 @@ public class EntitiesVersionControlController extends BaseController { } - @ApiModelProperty(notes = "" + + @ApiOperation(value = "", notes = "" + "```\n{\n" + " \"repositoryUri\": \"https://github.com/User/repo.git\",\n" + " \"username\": \"User\",\n" + @@ -177,7 +310,7 @@ public class EntitiesVersionControlController extends BaseController { } } - @ApiModelProperty(notes = "" + + @ApiOperation(value = "", notes = "" + "```\n{\n" + " \"repositoryUri\": \"https://github.com/User/repo.git\",\n" + " \"username\": \"User\",\n" + diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exportimport/DefaultEntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/DefaultEntitiesExportImportService.java index 608a256514..108c2c0b1b 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exportimport/DefaultEntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/DefaultEntitiesExportImportService.java @@ -70,15 +70,37 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS } + @Override + public , I extends EntityId> EntityImportResult importEntity(SecurityUser user, EntityExportData exportData, EntityImportSettings importSettings, + boolean saveReferences, boolean sendEvents) throws ThingsboardException { + if (exportData.getEntity() == null || exportData.getEntity().getId() == null) { + throw new DataValidationException("Invalid entity data"); + } + + EntityType entityType = exportData.getEntityType(); + EntityImportService> importService = getImportService(entityType); + + EntityImportResult importResult = importService.importEntity(user, exportData, importSettings); + + if (saveReferences) { + importResult.getSaveReferencesCallback().run(); + } + if (sendEvents) { + importResult.getSendEventsCallback().run(); + } + + return importResult; + } + @Transactional(rollbackFor = Exception.class, timeout = 120) @Override public List> importEntities(SecurityUser user, List> exportDataList, EntityImportSettings importSettings) throws ThingsboardException { - fixOrder(exportDataList); + fixDataOrderForImport(exportDataList); List> importResults = new ArrayList<>(); for (EntityExportData exportData : exportDataList) { - EntityImportResult importResult = importEntity(user, exportData, importSettings); + EntityImportResult importResult = importEntity(user, exportData, importSettings, false, false); importResults.add(importResult); } @@ -103,21 +125,11 @@ public class DefaultEntitiesExportImportService implements EntitiesExportImportS return importResults; } - private void fixOrder(List> exportDataList) { + @Override + public void fixDataOrderForImport(List> exportDataList) { exportDataList.sort(Comparator.comparing(exportData -> SUPPORTED_ENTITY_TYPES.indexOf(exportData.getEntityType()))); } - private , I extends EntityId> EntityImportResult importEntity(SecurityUser user, EntityExportData exportData, EntityImportSettings importSettings) throws ThingsboardException { - if (exportData.getEntity() == null || exportData.getEntity().getId() == null) { - throw new DataValidationException("Invalid entity data"); - } - - EntityType entityType = exportData.getEntityType(); - EntityImportService> importService = getImportService(entityType); - - return importService.importEntity(user, exportData, importSettings); - } - @SuppressWarnings("unchecked") private , D extends EntityExportData> EntityExportService getExportService(EntityType entityType) { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exportimport/EntitiesExportImportService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/EntitiesExportImportService.java index 5c29feb57c..049cf4390a 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exportimport/EntitiesExportImportService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/EntitiesExportImportService.java @@ -30,6 +30,11 @@ public interface EntitiesExportImportService { , I extends EntityId> EntityExportData exportEntity(SecurityUser user, I entityId, EntityExportSettings exportSettings) throws ThingsboardException; + , I extends EntityId> EntityImportResult importEntity(SecurityUser user, EntityExportData exportData, EntityImportSettings importSettings, + boolean saveReferences, boolean sendEvents) throws ThingsboardException; + List> importEntities(SecurityUser user, List> exportDataList, EntityImportSettings importSettings) throws ThingsboardException; + void fixDataOrderForImport(List> exportDataList); + } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/DefaultExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/DefaultExportableEntitiesService.java index 4d772ad935..dafd68ba7b 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/DefaultExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/DefaultExportableEntitiesService.java @@ -17,11 +17,9 @@ package org.thingsboard.server.service.sync.exportimport.exporting; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.ObjectUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.ExportableEntity; import org.thingsboard.server.common.data.HasTenantId; @@ -47,12 +45,6 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.permission.AccessControlService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; -import org.thingsboard.server.service.sync.vc.data.request.create.EntitiesByCustomFilterVersionCreateConfig; -import org.thingsboard.server.service.sync.vc.data.request.create.EntitiesByCustomQueryVersionCreateConfig; -import org.thingsboard.server.service.sync.vc.data.request.create.EntityListVersionCreateConfig; -import org.thingsboard.server.service.sync.vc.data.request.create.EntityTypeVersionCreateConfig; -import org.thingsboard.server.service.sync.vc.data.request.create.VersionCreateConfig; -import org.thingsboard.server.service.sync.vc.data.request.create.SingleEntityVersionCreateConfig; import java.util.Collection; import java.util.Collections; @@ -145,44 +137,6 @@ public class DefaultExportableEntitiesService implements ExportableEntitiesServi } - @Transactional(readOnly = true, timeout = 40) - @Override - public List findEntitiesByFilter(TenantId tenantId, VersionCreateConfig request) { - switch (request.getType()) { - case SINGLE_ENTITY: { - return List.of(((SingleEntityVersionCreateConfig) request).getEntityId()); - } - case ENTITY_LIST: { - return ((EntityListVersionCreateConfig) request).getEntitiesIds(); - } - case ENTITY_TYPE: { - EntityTypeVersionCreateConfig exportRequest = (EntityTypeVersionCreateConfig) request; - org.thingsboard.server.common.data.query.EntityTypeFilter entityTypeFilter = new org.thingsboard.server.common.data.query.EntityTypeFilter(); - entityTypeFilter.setEntityType(exportRequest.getEntityType()); - - CustomerId customerId = new CustomerId(ObjectUtils.defaultIfNull(exportRequest.getCustomerId(), EntityId.NULL_UUID)); - return findEntitiesByFilter(tenantId, customerId, entityTypeFilter, 0, Integer.MAX_VALUE); - } - case CUSTOM_ENTITY_FILTER: { - EntitiesByCustomFilterVersionCreateConfig exportRequest = (EntitiesByCustomFilterVersionCreateConfig) request; - EntityFilter filter = exportRequest.getFilter(); - - CustomerId customerId = new CustomerId(ObjectUtils.defaultIfNull(exportRequest.getCustomerId(), EntityId.NULL_UUID)); - return findEntitiesByFilter(tenantId, customerId, filter, 0, Integer.MAX_VALUE); - } - case CUSTOM_ENTITY_QUERY: { - EntitiesByCustomQueryVersionCreateConfig exportRequest = (EntitiesByCustomQueryVersionCreateConfig) request; - EntityDataQuery query = exportRequest.getQuery(); - - CustomerId customerId = new CustomerId(ObjectUtils.defaultIfNull(exportRequest.getCustomerId(), EntityId.NULL_UUID)); - return findEntitiesByQuery(tenantId, customerId, query); - } - default: { - throw new IllegalArgumentException("Export request is not supported"); - } - } - } - private List findEntitiesByFilter(TenantId tenantId, CustomerId customerId, EntityFilter filter, int page, int pageSize) { EntityDataPageLink pageLink = new EntityDataPageLink(); pageLink.setPage(page); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/ExportableEntitiesService.java b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/ExportableEntitiesService.java index 043ec91e20..66c9079148 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/ExportableEntitiesService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/exportimport/exporting/ExportableEntitiesService.java @@ -39,8 +39,6 @@ public interface ExportableEntitiesService { , I extends EntityId> PageData findEntitiesByTenantId(TenantId tenantId, EntityType entityType, PageLink pageLink); - List findEntitiesByFilter(TenantId tenantId, VersionCreateConfig filter); // FIXME [viacheslav]: - void deleteByTenantIdAndId(TenantId tenantId, I id); diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java index f6434daecc..0bdedb5e4d 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/DefaultEntitiesVersionControlService.java @@ -18,16 +18,14 @@ package org.thingsboard.server.service.sync.vc; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.fasterxml.jackson.databind.node.TextNode; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; -import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.transaction.support.TransactionTemplate; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; @@ -40,12 +38,12 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.JsonDataEntry; import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.data.query.EntityData; import org.thingsboard.server.common.data.query.EntityDataPageLink; import org.thingsboard.server.common.data.query.EntityDataQuery; import org.thingsboard.server.common.data.query.EntityDataSortOrder; import org.thingsboard.server.common.data.query.EntityKey; import org.thingsboard.server.common.data.query.EntityKeyType; +import org.thingsboard.server.common.data.query.EntityTypeFilter; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.entity.EntityService; @@ -65,17 +63,18 @@ import org.thingsboard.server.service.sync.vc.data.EntityVersion; import org.thingsboard.server.service.sync.vc.data.VersionCreationResult; import org.thingsboard.server.service.sync.vc.data.VersionLoadResult; import org.thingsboard.server.service.sync.vc.data.VersionedEntityInfo; -import org.thingsboard.server.service.sync.vc.data.request.create.MultipleEntitiesVersionCreateConfig; -import org.thingsboard.server.service.sync.vc.data.request.load.VersionLoadRequest; -import org.thingsboard.server.service.sync.vc.data.request.load.VersionLoadSettings; -import org.thingsboard.server.service.sync.vc.data.request.create.VersionCreateRequest; -import org.thingsboard.server.service.sync.vc.data.request.create.EntitiesByCustomFilterVersionCreateConfig; -import org.thingsboard.server.service.sync.vc.data.request.create.EntitiesByCustomQueryVersionCreateConfig; -import org.thingsboard.server.service.sync.vc.data.request.create.EntityListVersionCreateConfig; -import org.thingsboard.server.service.sync.vc.data.request.create.EntityTypeVersionCreateConfig; -import org.thingsboard.server.service.sync.vc.data.request.create.SingleEntityVersionCreateConfig; +import org.thingsboard.server.service.sync.vc.data.request.create.EntityListVersionCreateRequest; +import org.thingsboard.server.service.sync.vc.data.request.create.EntityTypeVersionCreateRequest; +import org.thingsboard.server.service.sync.vc.data.request.create.SingleEntityVersionCreateRequest; +import org.thingsboard.server.service.sync.vc.data.request.create.SyncStrategy; import org.thingsboard.server.service.sync.vc.data.request.create.VersionCreateConfig; +import org.thingsboard.server.service.sync.vc.data.request.create.VersionCreateRequest; +import org.thingsboard.server.service.sync.vc.data.request.load.EntityTypeVersionLoadRequest; +import org.thingsboard.server.service.sync.vc.data.request.load.SingleEntityVersionLoadRequest; +import org.thingsboard.server.service.sync.vc.data.request.load.VersionLoadConfig; +import org.thingsboard.server.service.sync.vc.data.request.load.VersionLoadRequest; import org.thingsboard.server.utils.GitRepository; +import org.thingsboard.server.utils.ThrowingRunnable; import java.io.File; import java.io.IOException; @@ -108,10 +107,11 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont private final AttributesService attributesService; private final EntityService entityService; private final TenantDao tenantDao; + private final TransactionTemplate transactionTemplate; // TODO [viacheslav]: concurrency private final Map repositories = new ConcurrentHashMap<>(); - @Value("${java.io.tmpdir}") + @Value("${java.io.tmpdir}/repositories") private String repositoriesFolder; private static final String SETTINGS_KEY = "vc"; @@ -146,33 +146,26 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont @Override public VersionCreationResult saveEntitiesVersion(SecurityUser user, VersionCreateRequest request) throws Exception { GitRepository repository = checkRepository(user.getTenantId()); - repository.getLock().writeLock().lock(); - - try { - repository.fetch(); - if (repository.listBranches().contains(request.getBranch())) { - repository.checkout(request.getBranch()); - repository.merge(request.getBranch()); - } else { // TODO [viacheslav]: rollback orphan branch on failure - repository.createAndCheckoutOrphanBranch(request.getBranch()); // FIXME [viacheslav]: Checkout returned unexpected result NO_CHANGE for master branch - } - - for (VersionCreateConfig config : request.getConfigs()) { - EntityExportSettings exportSettings = EntityExportSettings.builder() - .exportRelations(config.isSaveRelations()) - .build(); - List> entityDataList = new ArrayList<>(); - for (EntityId entityId : findEntities(user, config, 0, Integer.MAX_VALUE)) { // TODO [viacheslav]: find with pagination - EntityExportData> entityData = exportImportService.exportEntity(user, entityId, exportSettings); - entityDataList.add(entityData); - } + repository.fetch(); + if (repository.listBranches().contains(request.getBranch())) { + repository.checkout(request.getBranch()); + repository.merge(request.getBranch()); + } else { // TODO [viacheslav]: rollback orphan branch on failure + repository.createAndCheckoutOrphanBranch(request.getBranch()); // FIXME [viacheslav]: Checkout returned unexpected result NO_CHANGE for master branch + } - if (config instanceof MultipleEntitiesVersionCreateConfig && - ((MultipleEntitiesVersionCreateConfig) config).isRemoveOtherRemoteEntitiesOfType()) { - entityDataList.stream() // FIXME [viacheslav]: in case of an emtpy entity type? none will be deleted on remote? - .map(EntityExportData::getEntityType) - .distinct() + switch (request.getType()) { + case SINGLE_ENTITY: { + SingleEntityVersionCreateRequest versionCreateRequest = (SingleEntityVersionCreateRequest) request; + saveEntityData(user, repository, versionCreateRequest.getEntityId(), versionCreateRequest.getConfig()); + break; + } + case ENTITY_LIST: { + EntityListVersionCreateRequest versionCreateRequest = (EntityListVersionCreateRequest) request; + if (versionCreateRequest.getConfig().getSyncStrategy() == SyncStrategy.OVERWRITE) { + versionCreateRequest.getEntitiesIds().stream() + .map(EntityId::getEntityType).distinct() .forEach(entityType -> { try { FileUtils.deleteDirectory(Path.of(repository.getDirectory(), getRelativePath(entityType, null)).toFile()); @@ -181,76 +174,70 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont } }); } - - for (EntityExportData entityData : entityDataList) { - String entityDataJson = jsonWriter.writeValueAsString(entityData); - FileUtils.write(Path.of(repository.getDirectory(), getRelativePath(entityData.getEntityType(), - entityData.getEntity().getId().toString())).toFile(), entityDataJson, StandardCharsets.UTF_8); + for (EntityId entityId : versionCreateRequest.getEntitiesIds()) { + saveEntityData(user, repository, entityId, versionCreateRequest.getConfig()); } + break; } + case ENTITY_TYPE: { + EntityTypeVersionCreateRequest versionCreateRequest = (EntityTypeVersionCreateRequest) request; + versionCreateRequest.getEntityTypes().forEach((entityType, config) -> { + if (config.getSyncStrategy() == SyncStrategy.OVERWRITE) { + try { + FileUtils.deleteDirectory(Path.of(repository.getDirectory(), getRelativePath(entityType, null)).toFile()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } - repository.add("."); - - VersionCreationResult result = new VersionCreationResult(); - GitRepository.Status status = repository.status(); - result.setAdded(status.getAdded().size()); - result.setModified(status.getModified().size()); - result.setRemoved(status.getRemoved().size()); + EntityTypeFilter entityTypeFilter = new EntityTypeFilter(); + entityTypeFilter.setEntityType(entityType); + EntityDataPageLink entityDataPageLink = new EntityDataPageLink(); + entityDataPageLink.setPage(-1); + entityDataPageLink.setPageSize(-1); + EntityKey sortProperty = new EntityKey(EntityKeyType.ENTITY_FIELD, CREATED_TIME); + entityDataPageLink.setSortOrder(new EntityDataSortOrder(sortProperty, EntityDataSortOrder.Direction.DESC)); + EntityDataQuery query = new EntityDataQuery(entityTypeFilter, entityDataPageLink, List.of(sortProperty), Collections.emptyList(), Collections.emptyList()); + + DaoUtil.processInBatches(pageLink -> { + entityDataPageLink.setPage(pageLink.getPage()); + entityDataPageLink.setPageSize(pageLink.getPageSize()); + return entityService.findEntityDataByQuery(user.getTenantId(), new CustomerId(EntityId.NULL_UUID), query); + }, 100, data -> { + EntityId entityId = data.getEntityId(); + try { + saveEntityData(user, repository, entityId, config); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + }); + break; + } + } - GitRepository.Commit commit = repository.commit(request.getVersionName()); - repository.push(); + repository.add("."); - result.setVersion(toVersion(commit)); - return result; - } finally { - repository.getLock().writeLock().unlock(); - } - } + VersionCreationResult result = new VersionCreationResult(); + GitRepository.Status status = repository.status(); + result.setAdded(status.getAdded().size()); + result.setModified(status.getModified().size()); + result.setRemoved(status.getRemoved().size()); - private List findEntities(SecurityUser user, VersionCreateConfig config, int page, int pageSize) { - switch (config.getType()) { - case SINGLE_ENTITY: { - SingleEntityVersionCreateConfig filter = (SingleEntityVersionCreateConfig) config; - return List.of(filter.getEntityId()); - } - case ENTITY_LIST: { - EntityListVersionCreateConfig filter = (EntityListVersionCreateConfig) config; - return filter.getEntitiesIds(); - } - case ENTITY_TYPE: { - EntityTypeVersionCreateConfig filter = (EntityTypeVersionCreateConfig) config; - EntitiesByCustomFilterVersionCreateConfig newFilter = new EntitiesByCustomFilterVersionCreateConfig(); + GitRepository.Commit commit = repository.commit(request.getVersionName()); + repository.push(); - org.thingsboard.server.common.data.query.EntityTypeFilter entityTypeFilter = new org.thingsboard.server.common.data.query.EntityTypeFilter(); - entityTypeFilter.setEntityType(filter.getEntityType()); + result.setVersion(toVersion(commit)); + return result; + } - newFilter.setFilter(entityTypeFilter); - newFilter.setCustomerId(filter.getCustomerId()); - return findEntities(user, newFilter, page, pageSize); - } - case CUSTOM_ENTITY_FILTER: { - EntitiesByCustomFilterVersionCreateConfig filter = (EntitiesByCustomFilterVersionCreateConfig) config; - EntitiesByCustomQueryVersionCreateConfig newFilter = new EntitiesByCustomQueryVersionCreateConfig(); - - EntityDataPageLink pageLink = new EntityDataPageLink(); - pageLink.setPage(page); - pageLink.setPageSize(pageSize); - EntityKey sortProperty = new EntityKey(EntityKeyType.ENTITY_FIELD, CREATED_TIME); - pageLink.setSortOrder(new EntityDataSortOrder(sortProperty, EntityDataSortOrder.Direction.DESC)); - EntityDataQuery query = new EntityDataQuery(filter.getFilter(), pageLink, List.of(sortProperty), Collections.emptyList(), Collections.emptyList()); - - newFilter.setQuery(query); - newFilter.setCustomerId(filter.getCustomerId()); - return findEntities(user, newFilter, page, pageSize); - } - case CUSTOM_ENTITY_QUERY: { - EntitiesByCustomQueryVersionCreateConfig filter = (EntitiesByCustomQueryVersionCreateConfig) config; - CustomerId customerId = new CustomerId(ObjectUtils.defaultIfNull(filter.getCustomerId(), EntityId.NULL_UUID)); - return entityService.findEntityDataByQuery(user.getTenantId(), customerId, filter.getQuery()).getData() - .stream().map(EntityData::getEntityId) - .collect(Collectors.toList()); - } - } + private void saveEntityData(SecurityUser user, GitRepository repository, EntityId entityId, VersionCreateConfig config) throws Exception { + EntityExportData> entityData = exportImportService.exportEntity(user, entityId, EntityExportSettings.builder() + .exportRelations(config.isSaveRelations()) + .build()); + String entityDataJson = jsonWriter.writeValueAsString(entityData); + FileUtils.write(Path.of(repository.getDirectory(), getRelativePath(entityData.getEntityType(), + entityData.getEntity().getId().toString())).toFile(), entityDataJson, StandardCharsets.UTF_8); } @@ -271,14 +258,9 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont private List listVersions(TenantId tenantId, String branch, String path) throws Exception { GitRepository repository = checkRepository(tenantId); - repository.getLock().readLock().lock(); - try { - return repository.listCommits(branch, path, Integer.MAX_VALUE).stream() - .map(this::toVersion) - .collect(Collectors.toList()); - } finally { - repository.getLock().readLock().unlock(); - } + return repository.listCommits(branch, path, Integer.MAX_VALUE).stream() + .map(this::toVersion) + .collect(Collectors.toList()); } @@ -294,23 +276,15 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont private List listEntitiesAtVersion(TenantId tenantId, String branch, String versionId, String path) throws Exception { GitRepository repository = checkRepository(tenantId); - repository.getLock().readLock().lock(); - try { - checkVersion(tenantId, branch, versionId); - return repository.listFilesAtCommit(versionId, path).stream() - .map(filePath -> { - EntityId entityId = fromRelativePath(filePath); - EntityExportData entityData = getEntityDataAtVersion(tenantId, entityId, versionId); - - VersionedEntityInfo info = new VersionedEntityInfo(); - info.setExternalId(entityId); - info.setEntityName(entityData.getEntity().getName()); - return info; - }) - .collect(Collectors.toList()); - } finally { - repository.getLock().readLock().unlock(); - } + checkVersion(tenantId, branch, versionId); + return repository.listFilesAtCommit(versionId, path).stream() + .map(filePath -> { + EntityId entityId = fromRelativePath(filePath); + VersionedEntityInfo info = new VersionedEntityInfo(); + info.setExternalId(entityId); + return info; + }) + .collect(Collectors.toList()); } @@ -318,81 +292,117 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont public List loadEntitiesVersion(SecurityUser user, VersionLoadRequest request) throws Exception { GitRepository repository = checkRepository(user.getTenantId()); - List> entityDataList = new ArrayList<>(); - EntityVersion version; - repository.getLock().readLock().lock(); - try { - version = checkVersion(user.getTenantId(), request.getBranch(), request.getVersionId()); - for (VersionedEntityInfo info : listEntitiesAtVersion(user.getTenantId(), request.getBranch(), request.getVersionId(), path)) { - EntityExportData entityData = getEntityDataAtVersion(user.getTenantId(), info.getExternalId(), versionId); - entityDataList.add(entityData); - } - } finally { - repository.getLock().readLock().unlock(); - } - - EntityImportSettings importSettings = EntityImportSettings.builder() - .updateRelations(settings.isLoadRelations()) - .findExistingByName(settings.isFindExistingEntityByName()) - .build(); - // FIXME [viacheslav]: do evrth in transaction - List> importResults = exportImportService.importEntities(user, entityDataList, importSettings); - - Map results = new HashMap<>(); + EntityVersion version = checkVersion(user.getTenantId(), request.getBranch(), request.getVersionId()); - boolean removeNonexistentLocalEntities = false; - if () - if (request.isRemoveOtherLocalEntitiesOfType()) { - importResults.stream() - .collect(Collectors.groupingBy(EntityImportResult::getEntityType)) // FIXME [viacheslav]: if no entities of entity type - remove all ? - .forEach((entityType, resultsForEntityType) -> { - Set modifiedEntities = resultsForEntityType.stream().map(EntityImportResult::getSavedEntity).map(ExportableEntity::getExternalId).collect(Collectors.toSet()); - AtomicInteger deleted = new AtomicInteger(); + switch (request.getType()) { + case SINGLE_ENTITY: { + SingleEntityVersionLoadRequest versionLoadRequest = (SingleEntityVersionLoadRequest) request; + EntityImportResult importResult = transactionTemplate.execute(status -> { + try { + EntityImportResult result = loadEntity(user, repository, versionLoadRequest.getExternalEntityId(), version.getId(), versionLoadRequest.getConfig()); + result.getSaveReferencesCallback().run(); + return result; + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + try { + importResult.getSendEventsCallback().run(); + } catch (Exception e) { + log.error("Failed to send events for entity", e); + } + return List.of(VersionLoadResult.builder() + .created(importResult.getOldEntity() == null ? 1 : 0) + .updated(importResult.getOldEntity() != null ? 1 : 0) + .deleted(0) + .build()); + } + case ENTITY_TYPE: { + EntityTypeVersionLoadRequest versionLoadRequest = (EntityTypeVersionLoadRequest) request; + return transactionTemplate.execute(status -> { + Map results = new HashMap<>(); + List saveReferencesCallbacks = new ArrayList<>(); + List sendEventsCallbacks = new ArrayList<>(); + // order entity types.. + // or what + versionLoadRequest.getEntityTypes().forEach((entityType, config) -> { + AtomicInteger created = new AtomicInteger(); + AtomicInteger updated = new AtomicInteger(); + AtomicInteger deleted = new AtomicInteger(); + + Set remoteEntities; + try { + remoteEntities = listEntitiesAtVersion(user.getTenantId(), request.getBranch(), request.getVersionId(), getRelativePath(entityType, null)).stream() + .map(VersionedEntityInfo::getExternalId) + .collect(Collectors.toSet()); + for (EntityId externalEntityId : remoteEntities) { + EntityImportResult importResult = loadEntity(user, repository, externalEntityId, version.getId(), config); + + if (importResult.getOldEntity() == null) created.incrementAndGet(); + else updated.incrementAndGet(); + + saveReferencesCallbacks.add(importResult.getSaveReferencesCallback()); + sendEventsCallbacks.add(importResult.getSendEventsCallback()); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + + if (config.isRemoveOtherEntities()) { DaoUtil.processInBatches(pageLink -> { return exportableEntitiesService.findEntitiesByTenantId(user.getTenantId(), entityType, pageLink); }, 100, entity -> { - if (entity.getExternalId() == null || !modifiedEntities.contains(entity.getExternalId())) { + if (entity.getExternalId() == null || !remoteEntities.contains(entity.getExternalId())) { try { exportableEntitiesService.checkPermission(user, entity, entityType, Operation.DELETE); } catch (ThingsboardException e) { throw new RuntimeException(e); } - // need to delete in a specific order? + // need to delete entity types in a specific order? exportableEntitiesService.deleteByTenantIdAndId(user.getTenantId(), entity.getId()); deleted.getAndIncrement(); } }); - results.put(entityType, VersionLoadResult.builder() - .entityType(entityType) - .created((int) resultsForEntityType.stream().filter(importResult -> importResult.getOldEntity() == null).count()) - .updated((int) resultsForEntityType.stream().filter(importResult -> importResult.getOldEntity() != null).count()) - .deleted(deleted.get()) - .build()); - }); + } + + results.put(entityType, VersionLoadResult.builder() + .created(created.get()) + .updated(updated.get()) + .deleted(deleted.get()) + .build()); + }); + + for (ThrowingRunnable saveReferencesCallback : saveReferencesCallbacks) { + try { + saveReferencesCallback.run(); + } catch (ThingsboardException e) { + throw new RuntimeException(e); + } + } + for (ThrowingRunnable sendEventsCallback : sendEventsCallbacks) { + try { + sendEventsCallback.run(); + } catch (Exception e) { + log.error("Failed to send events for entity", e); + } + } + return new ArrayList<>(results.values()); + }); } - - return new ArrayList<>(results.values()); - } - - @SneakyThrows - private EntityExportData getEntityDataAtVersion(TenantId tenantId, EntityId externalId, String versionId) { - GitRepository repository = checkRepository(tenantId); - repository.getLock().readLock().lock(); - try { - String entityDataJson = repository.getFileContentAtCommit(getRelativePath(externalId.getEntityType(), externalId.getId().toString()), versionId); - return JacksonUtil.fromString(entityDataJson, EntityExportData.class); - } finally { - repository.getLock().readLock().unlock(); + default: + throw new IllegalArgumentException("Unsupported version load request"); } } - private void updateEntityVersionInfo(TenantId tenantId, EntityId entityId, EntityVersion version) { - ObjectNode versionInfo = JacksonUtil.newObjectNode(); - versionInfo.set("versionName", new TextNode(version.getName())); - versionInfo.set("versionId", new TextNode(version.getId())); - attributesService.save(tenantId, entityId, DataConstants.SERVER_SCOPE, - List.of(new BaseAttributeKvEntry(System.currentTimeMillis(), new JsonDataEntry("entityVersionInfo", versionInfo.toString())))); + private EntityImportResult loadEntity(SecurityUser user, GitRepository repository, EntityId externalId, String versionId, VersionLoadConfig config) throws Exception { + String entityDataJson = repository.getFileContentAtCommit(getRelativePath(externalId.getEntityType(), externalId.getId().toString()), versionId); + EntityExportData entityData = JacksonUtil.fromString(entityDataJson, EntityExportData.class); + + return exportImportService.importEntity(user, entityData, EntityImportSettings.builder() + .updateRelations(config.isLoadRelations()) + .findExistingByName(config.isFindExistingEntityByName()) + .build(), false, false); } @@ -417,12 +427,10 @@ public class DefaultEntitiesVersionControlService implements EntitiesVersionCont private void initRepository(TenantId tenantId, EntitiesVersionControlSettings settings) throws Exception { Path repositoryDirectory = Path.of(repositoriesFolder, tenantId.getId().toString()); GitRepository repository; - if (Files.exists(repositoryDirectory)) { - repository = GitRepository.open(repositoryDirectory.toFile(), settings.getUsername(), settings.getPassword()); - } else { - Files.createDirectories(repositoryDirectory); - repository = GitRepository.clone(settings.getRepositoryUri(), settings.getUsername(), settings.getPassword(), repositoryDirectory.toFile()); - } + FileUtils.forceDelete(repositoryDirectory.toFile()); + + Files.createDirectories(repositoryDirectory); + repository = GitRepository.clone(settings.getRepositoryUri(), settings.getUsername(), settings.getPassword(), repositoryDirectory.toFile()); repositories.put(tenantId, repository); } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionCreationResult.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionCreationResult.java index ac13a7b8bf..9194c67272 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionCreationResult.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionCreationResult.java @@ -1,3 +1,18 @@ +/** + * 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.sync.vc.data; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionedEntityInfo.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionedEntityInfo.java index 97bb6ada12..0a65297892 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionedEntityInfo.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/VersionedEntityInfo.java @@ -21,6 +21,5 @@ import org.thingsboard.server.common.data.id.EntityId; @Data public class VersionedEntityInfo { private EntityId externalId; - private String entityName; // etc.. } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityListVersionCreateRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityListVersionCreateRequest.java index fadd9b1fd9..59cf7d0176 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityListVersionCreateRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityListVersionCreateRequest.java @@ -1,3 +1,18 @@ +/** + * 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.sync.vc.data.request.create; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityTypeVersionCreateRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityTypeVersionCreateRequest.java index 0fd1d60667..f53e563e28 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityTypeVersionCreateRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/EntityTypeVersionCreateRequest.java @@ -1,3 +1,18 @@ +/** + * 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.sync.vc.data.request.create; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/MultipleEntitiesVersionCreateConfig.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/MultipleEntitiesVersionCreateConfig.java index f6d852875d..0a38e3c922 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/MultipleEntitiesVersionCreateConfig.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/MultipleEntitiesVersionCreateConfig.java @@ -1,3 +1,18 @@ +/** + * 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.sync.vc.data.request.create; import lombok.Data; @@ -5,6 +20,6 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) -public abstract class MultipleEntitiesVersionCreateConfig extends VersionCreateConfig { +public class MultipleEntitiesVersionCreateConfig extends VersionCreateConfig { private SyncStrategy syncStrategy; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SingleEntityVersionCreateRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SingleEntityVersionCreateRequest.java index 31bdeca658..4d03404199 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SingleEntityVersionCreateRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SingleEntityVersionCreateRequest.java @@ -1,3 +1,18 @@ +/** + * 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.sync.vc.data.request.create; import lombok.Data; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SyncStrategy.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SyncStrategy.java index 6216ad7658..43ec873f22 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SyncStrategy.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/SyncStrategy.java @@ -1,3 +1,18 @@ +/** + * 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.sync.vc.data.request.create; public enum SyncStrategy { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfig.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfig.java index 28f3232cb9..d4f8354df4 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfig.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateConfig.java @@ -18,6 +18,6 @@ package org.thingsboard.server.service.sync.vc.data.request.create; import lombok.Data; @Data -public abstract class VersionCreateConfig { +public class VersionCreateConfig { private boolean saveRelations; } diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequest.java index e11e2f85a3..f4ba122f6f 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequest.java @@ -15,8 +15,17 @@ */ package org.thingsboard.server.service.sync.vc.data.request.create; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @Type(name = "SINGLE_ENTITY", value = SingleEntityVersionCreateRequest.class), + @Type(name = "ENTITY_LIST", value = EntityListVersionCreateRequest.class), + @Type(name = "ENTITY_TYPE", value = EntityTypeVersionCreateRequest.class) +}) @Data public abstract class VersionCreateRequest { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequestType.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequestType.java index a5d0447b40..add3591fa2 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequestType.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/create/VersionCreateRequestType.java @@ -1,3 +1,18 @@ +/** + * 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.sync.vc.data.request.create; public enum VersionCreateRequestType { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadConfig.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadConfig.java index b5ee5ddd50..27597f5e87 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadConfig.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadConfig.java @@ -1,3 +1,18 @@ +/** + * 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.sync.vc.data.request.load; import lombok.Data; @@ -5,7 +20,7 @@ import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) -public class EntityTypeVersionLoadConfig extends EntityVersionLoadConfig { +public class EntityTypeVersionLoadConfig extends VersionLoadConfig { private boolean removeOtherEntities; diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadRequest.java index 6a49c45c2c..0ce902c83e 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityTypeVersionLoadRequest.java @@ -1,3 +1,18 @@ +/** + * 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.sync.vc.data.request.load; import lombok.Data; @@ -10,7 +25,7 @@ import java.util.Map; @EqualsAndHashCode(callSuper = true) public class EntityTypeVersionLoadRequest extends VersionLoadRequest { - private Map configs; + private Map entityTypes; @Override public VersionLoadRequestType getType() { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityVersionLoadConfig.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityVersionLoadConfig.java deleted file mode 100644 index f7d99a0981..0000000000 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/EntityVersionLoadConfig.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.thingsboard.server.service.sync.vc.data.request.load; - -import lombok.Data; - -@Data -public class EntityVersionLoadConfig { - - private boolean loadRelations; - private boolean findExistingEntityByName; - -} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/SingleEntityVersionLoadRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/SingleEntityVersionLoadRequest.java index f5336b55d1..eb4bb1bdc1 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/SingleEntityVersionLoadRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/SingleEntityVersionLoadRequest.java @@ -1,3 +1,18 @@ +/** + * 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.sync.vc.data.request.load; import lombok.Data; @@ -10,7 +25,7 @@ public class SingleEntityVersionLoadRequest extends VersionLoadRequest { private EntityId externalEntityId; - private EntityVersionLoadConfig config; + private VersionLoadConfig config; @Override public VersionLoadRequestType getType() { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadConfig.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadConfig.java new file mode 100644 index 0000000000..6ead76f105 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadConfig.java @@ -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.sync.vc.data.request.load; + +import lombok.Data; + +@Data +public class VersionLoadConfig { + + private boolean loadRelations; + private boolean findExistingEntityByName; + +} diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadRequest.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadRequest.java index 660f5dc18d..5a24bc78e5 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadRequest.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadRequest.java @@ -15,8 +15,17 @@ */ package org.thingsboard.server.service.sync.vc.data.request.load; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.Data; +import static com.fasterxml.jackson.annotation.JsonSubTypes.Type; + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes({ + @Type(name = "SINGLE_ENTITY", value = SingleEntityVersionLoadRequest.class), + @Type(name = "ENTITY_TYPE", value = EntityTypeVersionLoadRequest.class) +}) @Data public abstract class VersionLoadRequest { diff --git a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadRequestType.java b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadRequestType.java index 178d49668f..6a59cba676 100644 --- a/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadRequestType.java +++ b/application/src/main/java/org/thingsboard/server/service/sync/vc/data/request/load/VersionLoadRequestType.java @@ -1,3 +1,18 @@ +/** + * 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.sync.vc.data.request.load; public enum VersionLoadRequestType { diff --git a/application/src/main/java/org/thingsboard/server/utils/GitRepository.java b/application/src/main/java/org/thingsboard/server/utils/GitRepository.java index 14cdbf7b2c..2cb01e6a8b 100644 --- a/application/src/main/java/org/thingsboard/server/utils/GitRepository.java +++ b/application/src/main/java/org/thingsboard/server/utils/GitRepository.java @@ -55,8 +55,6 @@ public class GitRepository { @Getter private final String directory; - @Getter - private final ReadWriteLock lock = new ReentrantReadWriteLock(); private GitRepository(Git git, CredentialsProvider credentialsProvider, String directory) { this.git = git; @@ -88,7 +86,7 @@ public class GitRepository { public void checkout(String branch) throws GitAPIException { execute(git.checkout() - .setName(branch)); + .setName("origin/" + branch)); } public void merge(String branch) throws IOException, GitAPIException {