mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: import dependencies (#5044)
This commit is contained in:
		
							parent
							
								
									e9e110f702
								
							
						
					
					
						commit
						5619db33ed
					
				@ -50,6 +50,10 @@ import {
 | 
			
		||||
    createFakeSegmentService,
 | 
			
		||||
    createSegmentService,
 | 
			
		||||
} from '../segment/createSegmentService';
 | 
			
		||||
import {
 | 
			
		||||
    createDependentFeaturesService,
 | 
			
		||||
    createFakeDependentFeaturesService,
 | 
			
		||||
} from '../dependent-features/createDependentFeaturesService';
 | 
			
		||||
 | 
			
		||||
export const createFakeExportImportTogglesService = (
 | 
			
		||||
    config: IUnleashConfig,
 | 
			
		||||
@ -112,6 +116,8 @@ export const createFakeExportImportTogglesService = (
 | 
			
		||||
 | 
			
		||||
    const segmentService = createFakeSegmentService(config);
 | 
			
		||||
 | 
			
		||||
    const dependentFeaturesService = createFakeDependentFeaturesService(config);
 | 
			
		||||
 | 
			
		||||
    const exportImportService = new ExportImportService(
 | 
			
		||||
        {
 | 
			
		||||
            importTogglesStore,
 | 
			
		||||
@ -133,6 +139,7 @@ export const createFakeExportImportTogglesService = (
 | 
			
		||||
            strategyService,
 | 
			
		||||
            tagTypeService,
 | 
			
		||||
            segmentService,
 | 
			
		||||
            dependentFeaturesService,
 | 
			
		||||
        },
 | 
			
		||||
        dependentFeaturesReadModel,
 | 
			
		||||
    );
 | 
			
		||||
@ -229,6 +236,11 @@ export const deferredExportImportTogglesService = (
 | 
			
		||||
 | 
			
		||||
        const segmentService = createSegmentService(db, config);
 | 
			
		||||
 | 
			
		||||
        const dependentFeaturesService = createDependentFeaturesService(
 | 
			
		||||
            db,
 | 
			
		||||
            config,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        const exportImportService = new ExportImportService(
 | 
			
		||||
            {
 | 
			
		||||
                importTogglesStore,
 | 
			
		||||
@ -250,6 +262,7 @@ export const deferredExportImportTogglesService = (
 | 
			
		||||
                strategyService,
 | 
			
		||||
                tagTypeService,
 | 
			
		||||
                segmentService,
 | 
			
		||||
                dependentFeaturesService,
 | 
			
		||||
            },
 | 
			
		||||
            dependentFeaturesReadModel,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -34,6 +34,7 @@ import { extractUsernameFromUser } from '../../util';
 | 
			
		||||
import {
 | 
			
		||||
    AccessService,
 | 
			
		||||
    ContextService,
 | 
			
		||||
    DependentFeaturesService,
 | 
			
		||||
    EventService,
 | 
			
		||||
    FeatureTagService,
 | 
			
		||||
    FeatureToggleService,
 | 
			
		||||
@ -52,7 +53,6 @@ import { FeatureNameCheckResultWithFeaturePattern } from '../feature-toggle/feat
 | 
			
		||||
import { IDependentFeaturesReadModel } from '../dependent-features/dependent-features-read-model-type';
 | 
			
		||||
import groupBy from 'lodash.groupby';
 | 
			
		||||
import { ISegmentService } from '../../segments/segment-service-interface';
 | 
			
		||||
import { FeatureDependenciesSchema } from '../../openapi/spec/feature-dependencies-schema';
 | 
			
		||||
 | 
			
		||||
export type IImportService = {
 | 
			
		||||
    validate(
 | 
			
		||||
@ -113,6 +113,8 @@ export default class ExportImportService
 | 
			
		||||
 | 
			
		||||
    private dependentFeaturesReadModel: IDependentFeaturesReadModel;
 | 
			
		||||
 | 
			
		||||
    private dependentFeaturesService: DependentFeaturesService;
 | 
			
		||||
 | 
			
		||||
    constructor(
 | 
			
		||||
        stores: Pick<
 | 
			
		||||
            IUnleashStores,
 | 
			
		||||
@ -138,6 +140,7 @@ export default class ExportImportService
 | 
			
		||||
            tagTypeService,
 | 
			
		||||
            featureTagService,
 | 
			
		||||
            segmentService,
 | 
			
		||||
            dependentFeaturesService,
 | 
			
		||||
        }: Pick<
 | 
			
		||||
            IUnleashServices,
 | 
			
		||||
            | 'featureToggleService'
 | 
			
		||||
@ -148,6 +151,7 @@ export default class ExportImportService
 | 
			
		||||
            | 'tagTypeService'
 | 
			
		||||
            | 'featureTagService'
 | 
			
		||||
            | 'segmentService'
 | 
			
		||||
            | 'dependentFeaturesService'
 | 
			
		||||
        >,
 | 
			
		||||
        dependentFeaturesReadModel: IDependentFeaturesReadModel,
 | 
			
		||||
    ) {
 | 
			
		||||
@ -168,6 +172,7 @@ export default class ExportImportService
 | 
			
		||||
        this.eventService = eventService;
 | 
			
		||||
        this.tagTypeService = tagTypeService;
 | 
			
		||||
        this.featureTagService = featureTagService;
 | 
			
		||||
        this.dependentFeaturesService = dependentFeaturesService;
 | 
			
		||||
        this.importPermissionsService = new ImportPermissionsService(
 | 
			
		||||
            this.importTogglesStore,
 | 
			
		||||
            this.accessService,
 | 
			
		||||
@ -258,7 +263,7 @@ export default class ExportImportService
 | 
			
		||||
        ]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async importToggleLevelInfo(
 | 
			
		||||
    async importFeatureData(
 | 
			
		||||
        dto: ImportTogglesSchema,
 | 
			
		||||
        user: User,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
@ -274,9 +279,9 @@ export default class ExportImportService
 | 
			
		||||
 | 
			
		||||
        await this.importVerify(cleanedDto, user);
 | 
			
		||||
 | 
			
		||||
        await this.importToggleLevelInfo(cleanedDto, user);
 | 
			
		||||
        await this.importFeatureData(cleanedDto, user);
 | 
			
		||||
 | 
			
		||||
        await this.importDefault(cleanedDto, user);
 | 
			
		||||
        await this.importEnvironmentData(cleanedDto, user);
 | 
			
		||||
        await this.eventService.storeEvent({
 | 
			
		||||
            project: cleanedDto.project,
 | 
			
		||||
            environment: cleanedDto.environment,
 | 
			
		||||
@ -285,10 +290,34 @@ export default class ExportImportService
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async importDefault(dto: ImportTogglesSchema, user: User): Promise<void> {
 | 
			
		||||
    async importEnvironmentData(
 | 
			
		||||
        dto: ImportTogglesSchema,
 | 
			
		||||
        user: User,
 | 
			
		||||
    ): Promise<void> {
 | 
			
		||||
        await this.deleteStrategies(dto);
 | 
			
		||||
        await this.importStrategies(dto, user);
 | 
			
		||||
        await this.importToggleStatuses(dto, user);
 | 
			
		||||
        await this.importDependencies(dto, user);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async importDependencies(dto: ImportTogglesSchema, user: User) {
 | 
			
		||||
        await Promise.all(
 | 
			
		||||
            (dto.data.dependencies || []).flatMap((dependency) => {
 | 
			
		||||
                const projectId = dto.data.features.find(
 | 
			
		||||
                    (feature) => feature.name === dependency.feature,
 | 
			
		||||
                )!.project!;
 | 
			
		||||
                return dependency.dependencies.map((parentDependency) =>
 | 
			
		||||
                    this.dependentFeaturesService.upsertFeatureDependency(
 | 
			
		||||
                        {
 | 
			
		||||
                            child: dependency.feature,
 | 
			
		||||
                            projectId,
 | 
			
		||||
                        },
 | 
			
		||||
                        parentDependency,
 | 
			
		||||
                        user,
 | 
			
		||||
                    ),
 | 
			
		||||
                );
 | 
			
		||||
            }),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async importToggleStatuses(dto: ImportTogglesSchema, user: User) {
 | 
			
		||||
 | 
			
		||||
@ -161,6 +161,7 @@ beforeAll(async () => {
 | 
			
		||||
                    featuresExportImport: true,
 | 
			
		||||
                    featureNamingPattern: true,
 | 
			
		||||
                    dependentFeatures: true,
 | 
			
		||||
                    transactionalDecorator: true,
 | 
			
		||||
                },
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
@ -750,6 +751,10 @@ test('import features to existing project and environment', async () => {
 | 
			
		||||
        ...defaultImportPayload,
 | 
			
		||||
        data: {
 | 
			
		||||
            ...defaultImportPayload.data,
 | 
			
		||||
            features: [
 | 
			
		||||
                ...defaultImportPayload.data.features,
 | 
			
		||||
                anotherExportedFeature,
 | 
			
		||||
            ],
 | 
			
		||||
            featureStrategies: [
 | 
			
		||||
                {
 | 
			
		||||
                    ...exportedStrategy,
 | 
			
		||||
@ -762,6 +767,16 @@ test('import features to existing project and environment', async () => {
 | 
			
		||||
                    name: segment.name,
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
            dependencies: [
 | 
			
		||||
                {
 | 
			
		||||
                    feature: exportedFeature.name,
 | 
			
		||||
                    dependencies: [
 | 
			
		||||
                        {
 | 
			
		||||
                            feature: anotherExportedFeature.name,
 | 
			
		||||
                        },
 | 
			
		||||
                    ],
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -779,6 +794,11 @@ test('import features to existing project and environment', async () => {
 | 
			
		||||
                ],
 | 
			
		||||
            },
 | 
			
		||||
        ],
 | 
			
		||||
        dependencies: [
 | 
			
		||||
            {
 | 
			
		||||
                feature: anotherExportedFeature.name,
 | 
			
		||||
            },
 | 
			
		||||
        ],
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const { body: importedFeatureEnvironment } = await getFeatureEnvironment(
 | 
			
		||||
 | 
			
		||||
@ -1731,18 +1731,20 @@ class FeatureToggleService {
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            if (hasDisabledStrategies && shouldActivateDisabledStrategies) {
 | 
			
		||||
                strategies.map(async (strategy) => {
 | 
			
		||||
                    return this.updateStrategy(
 | 
			
		||||
                        strategy.id,
 | 
			
		||||
                        { disabled: false },
 | 
			
		||||
                        {
 | 
			
		||||
                            environment,
 | 
			
		||||
                            projectId: project,
 | 
			
		||||
                            featureName,
 | 
			
		||||
                        },
 | 
			
		||||
                        createdBy,
 | 
			
		||||
                    );
 | 
			
		||||
                });
 | 
			
		||||
                await Promise.all(
 | 
			
		||||
                    strategies.map((strategy) =>
 | 
			
		||||
                        this.updateStrategy(
 | 
			
		||||
                            strategy.id,
 | 
			
		||||
                            { disabled: false },
 | 
			
		||||
                            {
 | 
			
		||||
                                environment,
 | 
			
		||||
                                projectId: project,
 | 
			
		||||
                                featureName,
 | 
			
		||||
                            },
 | 
			
		||||
                            createdBy,
 | 
			
		||||
                        ),
 | 
			
		||||
                    ),
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const hasOnlyDisabledStrategies = strategies.every(
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user