diff --git a/application/src/main/data/upgrade/3.3.3/schema_update.sql b/application/src/main/data/upgrade/3.3.3/schema_update.sql new file mode 100644 index 0000000000..dbaecc245f --- /dev/null +++ b/application/src/main/data/upgrade/3.3.3/schema_update.sql @@ -0,0 +1,27 @@ +-- +-- Copyright © 2016-2021 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. +-- + +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'fk_device_profile_ota_package') THEN + ALTER TABLE ota_package + ADD CONSTRAINT fk_device_profile_ota_package + FOREIGN KEY (device_profile_id) REFERENCES device_profile (id) + ON DELETE CASCADE; + END IF; + END; +$$; diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index 02bc87aaf9..046ce59b19 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -216,6 +216,9 @@ public class ThingsboardInstallService { dataUpdateService.updateData("3.3.2"); log.info("Updating system data..."); systemDataLoaderService.updateSystemWidgets(); + case "3.3.3": + log.info("Upgrading ThingsBoard from version 3.3.3 to 3.4.0 ..."); + databaseEntitiesUpgradeService.upgradeDatabase("3.3.3"); break; //TODO update CacheCleanupService on the next version upgrade diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index eef29026ba..8872c95c5a 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -488,6 +488,18 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService log.error("Failed updating schema!!!", e); } break; + case "3.3.3": + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + log.info("Updating schema..."); + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.3.3", SCHEMA_UPDATE_SQL); + loadSql(schemaUpdateFile, conn); + log.info("Updating schema settings..."); + conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3004000;"); + log.info("Schema updated"); + } catch (Exception e) { + log.error("Failed to update schema", e); + } + break; default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java index 638851f05d..b14653b1d0 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java @@ -51,7 +51,4 @@ public interface OtaPackageService { void deleteOtaPackagesByTenantId(TenantId tenantId); long sumDataSizeByTenantId(TenantId tenantId); - - boolean existsByDeviceProfileId(DeviceProfileId deviceProfileId); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java index c80834bfb6..18a8a1ecde 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java @@ -218,9 +218,6 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D if (deviceProfile != null && deviceProfile.isDefault()) { throw new DataValidationException("Deletion of Default Device Profile is prohibited!"); } - if (otaPackageService.existsByDeviceProfileId(deviceProfileId)) { - throw new DataValidationException("The device profile is referenced by OTA update package"); - } this.removeDeviceProfile(tenantId, deviceProfile); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java index c879fe6fdd..205b2c447b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java @@ -223,11 +223,6 @@ public class BaseOtaPackageService implements OtaPackageService { return otaPackageDao.sumDataSizeByTenantId(tenantId); } - @Override - public boolean existsByDeviceProfileId(DeviceProfileId deviceProfileId) { - return otaPackageDao.existsByDeviceProfileId(deviceProfileId); - } - @Override public void deleteOtaPackagesByTenantId(TenantId tenantId) { log.trace("Executing deleteOtaPackagesByTenantId, tenantId [{}]", tenantId); 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 a4ad6dc8c1..ef8740030c 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 @@ -16,14 +16,11 @@ package org.thingsboard.server.dao.ota; import org.thingsboard.server.common.data.OtaPackage; -import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.TenantEntityDao; import org.thingsboard.server.dao.TenantEntityWithDataDao; public interface OtaPackageDao extends Dao, TenantEntityWithDataDao { Long sumDataSizeByTenantId(TenantId tenantId); - - boolean existsByDeviceProfileId(DeviceProfileId deviceProfileId); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java index 969fe0ec54..98309b9e51 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java @@ -20,7 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.OtaPackage; -import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.ota.OtaPackageDao; import org.thingsboard.server.dao.model.sql.OtaPackageEntity; @@ -49,10 +48,4 @@ public class JpaOtaPackageDao extends JpaAbstractSearchTextDao { @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); - - boolean existsByDeviceProfileId(UUID deviceProfileId); - } diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 4fc5fbaebc..6cb8e84c45 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -211,7 +211,6 @@ CREATE TABLE IF NOT EXISTS ota_package ( additional_info varchar, search_text varchar(255), CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) --- CONSTRAINT fk_device_profile_firmware FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS device_profile ( @@ -241,6 +240,11 @@ CREATE TABLE IF NOT EXISTS device_profile ( CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES ota_package(id) ); +ALTER TABLE ota_package + ADD CONSTRAINT fk_device_profile_ota_package + FOREIGN KEY (device_profile_id) REFERENCES device_profile (id) + ON DELETE CASCADE; + -- We will use one-to-many relation in the first release and extend this feature in case of user requests -- CREATE TABLE IF NOT EXISTS device_profile_firmware ( -- device_profile_id uuid NOT NULL, diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java index 80d1ae809f..c2caf3ea80 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java @@ -31,8 +31,8 @@ import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +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.exception.DataValidationException; @@ -45,7 +45,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.stream.Collectors; -import static org.junit.Assert.assertThrows; +import static org.assertj.core.api.Assertions.assertThat; import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; public abstract class BaseDeviceProfileServiceTest extends AbstractServiceTest { @@ -254,16 +254,19 @@ public abstract class BaseDeviceProfileServiceTest extends AbstractServiceTest { } @Test - public void testDeleteDeviceProfileWithExistingOta() { + public void testDeleteDeviceProfileWithExistingOta_cascadeDelete() { DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile"); - DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); + deviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); + OtaPackage otaPackage = constructDefaultOtaPackage(tenantId, deviceProfile.getId()); + otaPackage = otaPackageService.saveOtaPackage(otaPackage); + + assertThat(deviceProfileService.findDeviceProfileById(tenantId, deviceProfile.getId())).isNotNull(); + assertThat(otaPackageService.findOtaPackageById(tenantId, otaPackage.getId())).isNotNull(); - OtaPackage otaPackage = constructDefaultOtaPackage(tenantId, savedDeviceProfile.getId()); - otaPackageService.saveOtaPackage(otaPackage); + deviceProfileService.deleteDeviceProfile(tenantId, deviceProfile.getId()); - assertThrows("The device profile is referenced by OTA update package", DataValidationException.class, () -> { - deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); - }); + assertThat(deviceProfileService.findDeviceProfileById(tenantId, deviceProfile.getId())).isNull(); + assertThat(otaPackageService.findOtaPackageById(tenantId, otaPackage.getId())).isNull(); } @Test diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java index 0df2d9e6b6..b890c71453 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java @@ -487,9 +487,6 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { thrown.expectMessage("The otaPackage referenced by the device profile cannot be deleted!"); otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } finally { - savedDeviceProfile.setFirmwareId(null); - deviceProfileService.saveDeviceProfile(savedDeviceProfile); - otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); } } diff --git a/dao/src/test/resources/sql/system-test-psql.sql b/dao/src/test/resources/sql/system-test-psql.sql index 16dcb8c3ae..177f838bb1 100644 --- a/dao/src/test/resources/sql/system-test-psql.sql +++ b/dao/src/test/resources/sql/system-test-psql.sql @@ -1,2 +1,2 @@ --PostgreSQL specific truncate to fit constraints -TRUNCATE TABLE device_credentials, device, device_profile, rule_node_state, rule_node, rule_chain; \ No newline at end of file +TRUNCATE TABLE device_credentials, device, device_profile, ota_package, rule_node_state, rule_node, rule_chain; \ No newline at end of file diff --git a/dao/src/test/resources/sql/system-test.sql b/dao/src/test/resources/sql/system-test.sql index 68a0e0efa3..d673548fb0 100644 --- a/dao/src/test/resources/sql/system-test.sql +++ b/dao/src/test/resources/sql/system-test.sql @@ -1,6 +1,6 @@ TRUNCATE TABLE device_credentials; TRUNCATE TABLE device; -TRUNCATE TABLE device_profile; +TRUNCATE TABLE device_profile CASCADE; TRUNCATE TABLE rule_node_state; TRUNCATE TABLE rule_node; TRUNCATE TABLE rule_chain; \ No newline at end of file