diff --git a/src/lib/features/export-import-toggles/export-import-permissions.e2e.test.ts b/src/lib/features/export-import-toggles/export-import-permissions.e2e.test.ts index cbd114f258..bd868722f1 100644 --- a/src/lib/features/export-import-toggles/export-import-permissions.e2e.test.ts +++ b/src/lib/features/export-import-toggles/export-import-permissions.e2e.test.ts @@ -335,6 +335,11 @@ test('validate import data', async () => { 'The following features will not be imported as they are currently archived. To import them, please unarchive them first:', affectedItems: [archivedFeature], }, + { + message: + 'The following features already exist in this project and will be overwritten:', + affectedItems: ['existing_feature'], + }, ], permissions: [ { diff --git a/src/lib/features/export-import-toggles/export-import-service.ts b/src/lib/features/export-import-toggles/export-import-service.ts index 3c8c8252ab..3e553acefc 100644 --- a/src/lib/features/export-import-toggles/export-import-service.ts +++ b/src/lib/features/export-import-toggles/export-import-service.ts @@ -151,6 +151,7 @@ export default class ExportImportService { unsupportedContextFields, archivedFeatures, otherProjectFeatures, + existingProjectFeatures, missingPermissions, ] = await Promise.all([ this.getUnsupportedStrategies(dto), @@ -158,6 +159,7 @@ export default class ExportImportService { this.getUnsupportedContextFields(dto), this.getArchivedFeatures(dto), this.getOtherProjectFeatures(dto), + this.getExistingProjectFeatures(dto), this.importPermissionsService.getMissingPermissions( dto, user, @@ -176,6 +178,7 @@ export default class ExportImportService { const warnings = ImportValidationMessages.compileWarnings( usedCustomStrategies, archivedFeatures, + existingProjectFeatures, ); const permissions = ImportValidationMessages.compilePermissionErrors( @@ -299,7 +302,7 @@ export default class ExportImportService { this.contextService.createContextField( { name: contextField.name, - description: contextField.description, + description: contextField.description || '', legalValues: contextField.legalValues, stickiness: contextField.stickiness, }, @@ -529,6 +532,15 @@ export default class ExportImportService { ); } + private async getExistingProjectFeatures(dto: ImportTogglesSchema) { + const existingProjectsFeatures = + await this.importTogglesStore.getFeaturesInProject( + dto.data.features.map((feature) => feature.name), + dto.project, + ); + return existingProjectsFeatures; + } + private async getNewTagTypes(dto: ImportTogglesSchema) { const existingTagTypes = (await this.tagTypeService.getAll()).map( (tagType) => tagType.name, diff --git a/src/lib/features/export-import-toggles/import-toggles-store-type.ts b/src/lib/features/export-import-toggles/import-toggles-store-type.ts index 1277e8e61b..d8c1aabd2a 100644 --- a/src/lib/features/export-import-toggles/import-toggles-store-type.ts +++ b/src/lib/features/export-import-toggles/import-toggles-store-type.ts @@ -11,6 +11,11 @@ export interface IImportTogglesStore { project: string, ): Promise<{ name: string; project: string }[]>; + getFeaturesInProject( + featureNames: string[], + project: string, + ): Promise; + deleteTagsForFeatures(tags: string[]): Promise; strategiesExistForFeatures( diff --git a/src/lib/features/export-import-toggles/import-toggles-store.ts b/src/lib/features/export-import-toggles/import-toggles-store.ts index fe74fa0cff..0daf877622 100644 --- a/src/lib/features/export-import-toggles/import-toggles-store.ts +++ b/src/lib/features/export-import-toggles/import-toggles-store.ts @@ -74,6 +74,18 @@ export class ImportTogglesStore implements IImportTogglesStore { return rows.map((row) => ({ name: row.name, project: row.project })); } + async getFeaturesInProject( + featureNames: string[], + project: string, + ): Promise { + const rows = await this.db(T.features) + .select(['name', 'project']) + .where('project', project) + .where('archived_at', null) + .whereIn('name', featureNames); + return rows.map((row) => row.name); + } + async deleteTagsForFeatures(features: string[]): Promise { return this.db(T.featureTag).whereIn('feature_name', features).del(); } diff --git a/src/lib/features/export-import-toggles/import-validation-messages.ts b/src/lib/features/export-import-toggles/import-validation-messages.ts index 9017776792..376df05189 100644 --- a/src/lib/features/export-import-toggles/import-validation-messages.ts +++ b/src/lib/features/export-import-toggles/import-validation-messages.ts @@ -73,6 +73,7 @@ export class ImportValidationMessages { static compileWarnings( usedCustomStrategies: string[], archivedFeatures: string[], + existingFeatures: string[], ): ImportTogglesValidateItemSchema[] { const warnings: ImportTogglesValidateItemSchema[] = []; if (usedCustomStrategies.length > 0) { @@ -89,6 +90,13 @@ export class ImportValidationMessages { affectedItems: archivedFeatures, }); } + if (existingFeatures.length > 0) { + warnings.push({ + message: + 'The following features already exist in this project and will be overwritten:', + affectedItems: existingFeatures, + }); + } return warnings; } }