Delete OTA update packages of device profile with cascade when this profile is deleted

This commit is contained in:
Viacheslav Klimov 2022-02-04 18:06:40 +02:00
parent 35528e687a
commit f51a66b585
14 changed files with 63 additions and 40 deletions

View File

@ -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;
$$;

View File

@ -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

View File

@ -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);
}

View File

@ -51,7 +51,4 @@ public interface OtaPackageService {
void deleteOtaPackagesByTenantId(TenantId tenantId);
long sumDataSizeByTenantId(TenantId tenantId);
boolean existsByDeviceProfileId(DeviceProfileId deviceProfileId);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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<OtaPackage>, TenantEntityWithDataDao {
Long sumDataSizeByTenantId(TenantId tenantId);
boolean existsByDeviceProfileId(DeviceProfileId deviceProfileId);
}

View File

@ -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<OtaPackageEntity,
public Long sumDataSizeByTenantId(TenantId tenantId) {
return otaPackageRepository.sumDataSizeByTenantId(tenantId.getId());
}
@Override
public boolean existsByDeviceProfileId(DeviceProfileId deviceProfileId) {
return otaPackageRepository.existsByDeviceProfileId(deviceProfileId.getId());
}
}

View File

@ -19,13 +19,11 @@ import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.thingsboard.server.dao.model.sql.OtaPackageEntity;
import org.thingsboard.server.dao.model.sql.OtaPackageInfoEntity;
import java.util.UUID;
public interface OtaPackageRepository extends CrudRepository<OtaPackageEntity, UUID> {
@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);
}

View File

@ -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,

View File

@ -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);
OtaPackage otaPackage = constructDefaultOtaPackage(tenantId, savedDeviceProfile.getId());
otaPackageService.saveOtaPackage(otaPackage);
assertThat(deviceProfileService.findDeviceProfileById(tenantId, deviceProfile.getId())).isNotNull();
assertThat(otaPackageService.findOtaPackageById(tenantId, otaPackage.getId())).isNotNull();
assertThrows("The device profile is referenced by OTA update package", DataValidationException.class, () -> {
deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId());
});
deviceProfileService.deleteDeviceProfile(tenantId, deviceProfile.getId());
assertThat(deviceProfileService.findDeviceProfileById(tenantId, deviceProfile.getId())).isNull();
assertThat(otaPackageService.findOtaPackageById(tenantId, otaPackage.getId())).isNull();
}
@Test

View File

@ -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());
}
}

View File

@ -1,2 +1,2 @@
--PostgreSQL specific truncate to fit constraints
TRUNCATE TABLE device_credentials, device, device_profile, rule_node_state, rule_node, rule_chain;
TRUNCATE TABLE device_credentials, device, device_profile, ota_package, rule_node_state, rule_node, rule_chain;

View File

@ -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;