mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: first skeleton of the batch import (#2868)
First skeleton of batch import: * injecting feature toggle service because I want to reuse logic and not just the store
This commit is contained in:
		
							parent
							
								
									3e4e0e4df9
								
							
						
					
					
						commit
						afdcd45042
					
				@ -7,8 +7,10 @@ import { Logger } from '../../logger';
 | 
				
			|||||||
import { OpenApiService } from '../../services/openapi-service';
 | 
					import { OpenApiService } from '../../services/openapi-service';
 | 
				
			||||||
import ExportImportService, {
 | 
					import ExportImportService, {
 | 
				
			||||||
    IExportQuery,
 | 
					    IExportQuery,
 | 
				
			||||||
 | 
					    IImportDTO,
 | 
				
			||||||
} from 'lib/services/export-import-service';
 | 
					} from 'lib/services/export-import-service';
 | 
				
			||||||
import { InvalidOperationError } from '../../error';
 | 
					import { InvalidOperationError } from '../../error';
 | 
				
			||||||
 | 
					import { IAuthRequest } from '../unleash-types';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ExportImportController extends Controller {
 | 
					class ExportImportController extends Controller {
 | 
				
			||||||
    private logger: Logger;
 | 
					    private logger: Logger;
 | 
				
			||||||
@ -45,6 +47,12 @@ class ExportImportController extends Controller {
 | 
				
			|||||||
            //     }),
 | 
					            //     }),
 | 
				
			||||||
            // ],
 | 
					            // ],
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					        this.route({
 | 
				
			||||||
 | 
					            method: 'post',
 | 
				
			||||||
 | 
					            path: '/import',
 | 
				
			||||||
 | 
					            permission: NONE,
 | 
				
			||||||
 | 
					            handler: this.importData,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async export(
 | 
					    async export(
 | 
				
			||||||
@ -65,5 +73,16 @@ class ExportImportController extends Controller {
 | 
				
			|||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async importData(
 | 
				
			||||||
 | 
					        req: IAuthRequest<unknown, unknown, IImportDTO, unknown>,
 | 
				
			||||||
 | 
					        res: Response,
 | 
				
			||||||
 | 
					    ): Promise<void> {
 | 
				
			||||||
 | 
					        const dto = req.body;
 | 
				
			||||||
 | 
					        const user = req.user;
 | 
				
			||||||
 | 
					        await this.exportImportService.import(dto, user);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        res.status(201).end();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
export default ExportImportController;
 | 
					export default ExportImportController;
 | 
				
			||||||
 | 
				
			|||||||
@ -13,14 +13,22 @@ import { IEnvironmentStore } from '../types/stores/environment-store';
 | 
				
			|||||||
import { IFeatureEnvironmentStore } from '../types/stores/feature-environment-store';
 | 
					import { IFeatureEnvironmentStore } from '../types/stores/feature-environment-store';
 | 
				
			||||||
import { IUnleashStores } from '../types/stores';
 | 
					import { IUnleashStores } from '../types/stores';
 | 
				
			||||||
import { ISegmentStore } from '../types/stores/segment-store';
 | 
					import { ISegmentStore } from '../types/stores/segment-store';
 | 
				
			||||||
import { IFlagResolver } from 'lib/types';
 | 
					import { IFlagResolver, IUnleashServices } from 'lib/types';
 | 
				
			||||||
import { IContextFieldDto } from '../types/stores/context-field-store';
 | 
					import { IContextFieldDto } from '../types/stores/context-field-store';
 | 
				
			||||||
 | 
					import FeatureToggleService from './feature-toggle-service';
 | 
				
			||||||
 | 
					import User from 'lib/types/user';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IExportQuery {
 | 
					export interface IExportQuery {
 | 
				
			||||||
    features: string[];
 | 
					    features: string[];
 | 
				
			||||||
    environment: string;
 | 
					    environment: string;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface IImportDTO {
 | 
				
			||||||
 | 
					    data: IExportData;
 | 
				
			||||||
 | 
					    project?: string;
 | 
				
			||||||
 | 
					    environment?: string;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IExportData {
 | 
					export interface IExportData {
 | 
				
			||||||
    features: FeatureToggle[];
 | 
					    features: FeatureToggle[];
 | 
				
			||||||
    tags?: ITag[];
 | 
					    tags?: ITag[];
 | 
				
			||||||
@ -54,12 +62,17 @@ export default class ExportImportService {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private flagResolver: IFlagResolver;
 | 
					    private flagResolver: IFlagResolver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private featureToggleService: FeatureToggleService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(
 | 
					    constructor(
 | 
				
			||||||
        stores: IUnleashStores,
 | 
					        stores: IUnleashStores,
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            getLogger,
 | 
					            getLogger,
 | 
				
			||||||
            flagResolver,
 | 
					            flagResolver,
 | 
				
			||||||
        }: Pick<IUnleashConfig, 'getLogger' | 'flagResolver'>,
 | 
					        }: Pick<IUnleashConfig, 'getLogger' | 'flagResolver'>,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            featureToggleService,
 | 
				
			||||||
 | 
					        }: Pick<IUnleashServices, 'featureToggleService'>,
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        this.eventStore = stores.eventStore;
 | 
					        this.eventStore = stores.eventStore;
 | 
				
			||||||
        this.toggleStore = stores.featureToggleStore;
 | 
					        this.toggleStore = stores.featureToggleStore;
 | 
				
			||||||
@ -73,6 +86,7 @@ export default class ExportImportService {
 | 
				
			|||||||
        this.environmentStore = stores.environmentStore;
 | 
					        this.environmentStore = stores.environmentStore;
 | 
				
			||||||
        this.segmentStore = stores.segmentStore;
 | 
					        this.segmentStore = stores.segmentStore;
 | 
				
			||||||
        this.flagResolver = flagResolver;
 | 
					        this.flagResolver = flagResolver;
 | 
				
			||||||
 | 
					        this.featureToggleService = featureToggleService;
 | 
				
			||||||
        this.logger = getLogger('services/state-service.js');
 | 
					        this.logger = getLogger('services/state-service.js');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -82,6 +96,18 @@ export default class ExportImportService {
 | 
				
			|||||||
        ).filter((toggle) => query.features.includes(toggle.name));
 | 
					        ).filter((toggle) => query.features.includes(toggle.name));
 | 
				
			||||||
        return { features: features };
 | 
					        return { features: features };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async import(dto: IImportDTO, user: User): Promise<void> {
 | 
				
			||||||
 | 
					        await Promise.all(
 | 
				
			||||||
 | 
					            dto.data.features.map((feature) =>
 | 
				
			||||||
 | 
					                this.featureToggleService.createFeatureToggle(
 | 
				
			||||||
 | 
					                    dto.project || feature.project,
 | 
				
			||||||
 | 
					                    feature,
 | 
				
			||||||
 | 
					                    user.name,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = ExportImportService;
 | 
					module.exports = ExportImportService;
 | 
				
			||||||
 | 
				
			|||||||
@ -61,7 +61,6 @@ export const createServices = (
 | 
				
			|||||||
    const featureTypeService = new FeatureTypeService(stores, config);
 | 
					    const featureTypeService = new FeatureTypeService(stores, config);
 | 
				
			||||||
    const resetTokenService = new ResetTokenService(stores, config);
 | 
					    const resetTokenService = new ResetTokenService(stores, config);
 | 
				
			||||||
    const stateService = new StateService(stores, config);
 | 
					    const stateService = new StateService(stores, config);
 | 
				
			||||||
    const exportImportService = new ExportImportService(stores, config);
 | 
					 | 
				
			||||||
    const strategyService = new StrategyService(stores, config);
 | 
					    const strategyService = new StrategyService(stores, config);
 | 
				
			||||||
    const tagService = new TagService(stores, config);
 | 
					    const tagService = new TagService(stores, config);
 | 
				
			||||||
    const tagTypeService = new TagTypeService(stores, config);
 | 
					    const tagTypeService = new TagTypeService(stores, config);
 | 
				
			||||||
@ -85,6 +84,9 @@ export const createServices = (
 | 
				
			|||||||
        segmentService,
 | 
					        segmentService,
 | 
				
			||||||
        accessService,
 | 
					        accessService,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					    const exportImportService = new ExportImportService(stores, config, {
 | 
				
			||||||
 | 
					        featureToggleService: featureToggleServiceV2,
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
    const environmentService = new EnvironmentService(stores, config);
 | 
					    const environmentService = new EnvironmentService(stores, config);
 | 
				
			||||||
    const featureTagService = new FeatureTagService(stores, config);
 | 
					    const featureTagService = new FeatureTagService(stores, config);
 | 
				
			||||||
    const favoritesService = new FavoritesService(stores, config);
 | 
					    const favoritesService = new FavoritesService(stores, config);
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ import {
 | 
				
			|||||||
import dbInit, { ITestDb } from '../../helpers/database-init';
 | 
					import dbInit, { ITestDb } from '../../helpers/database-init';
 | 
				
			||||||
import getLogger from '../../../fixtures/no-logger';
 | 
					import getLogger from '../../../fixtures/no-logger';
 | 
				
			||||||
import { IEventStore } from 'lib/types/stores/event-store';
 | 
					import { IEventStore } from 'lib/types/stores/event-store';
 | 
				
			||||||
import { FeatureToggleDTO, IStrategyConfig } from 'lib/types';
 | 
					import { FeatureToggle, FeatureToggleDTO, IStrategyConfig } from 'lib/types';
 | 
				
			||||||
import { DEFAULT_ENV } from '../../../../lib/util';
 | 
					import { DEFAULT_ENV } from '../../../../lib/util';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let app: IUnleashTest;
 | 
					let app: IUnleashTest;
 | 
				
			||||||
@ -79,3 +79,25 @@ test('exports features', async () => {
 | 
				
			|||||||
        ],
 | 
					        ],
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('import features', async () => {
 | 
				
			||||||
 | 
					    const feature: FeatureToggle = { project: 'ignore', name: 'first_feature' };
 | 
				
			||||||
 | 
					    await app.request
 | 
				
			||||||
 | 
					        .post('/api/admin/features-batch/import')
 | 
				
			||||||
 | 
					        .send({
 | 
				
			||||||
 | 
					            data: { features: [feature] },
 | 
				
			||||||
 | 
					            project: 'default',
 | 
				
			||||||
 | 
					            environment: 'custom_environment',
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .set('Content-Type', 'application/json')
 | 
				
			||||||
 | 
					        .expect(201);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { body } = await app.request
 | 
				
			||||||
 | 
					        .get('/api/admin/features/first_feature')
 | 
				
			||||||
 | 
					        .expect(200);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expect(body).toMatchObject({
 | 
				
			||||||
 | 
					        name: 'first_feature',
 | 
				
			||||||
 | 
					        project: 'default',
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user