mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-04 00:18:01 +01:00
Import export (#2865)
This commit is contained in:
parent
0c1e997f0b
commit
f3f3a59e5e
59
src/lib/routes/admin-api/export-import.ts
Normal file
59
src/lib/routes/admin-api/export-import.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import { Request, Response } from 'express';
|
||||
import Controller from '../controller';
|
||||
import { NONE } from '../../types/permissions';
|
||||
import { IUnleashConfig } from '../../types/option';
|
||||
import { IUnleashServices } from '../../types/services';
|
||||
import { Logger } from '../../logger';
|
||||
import { OpenApiService } from '../../services/openapi-service';
|
||||
import ExportImportService, {
|
||||
IExportQuery,
|
||||
} from 'lib/services/export-import-service';
|
||||
|
||||
class ExportImportController extends Controller {
|
||||
private logger: Logger;
|
||||
|
||||
private exportImportService: ExportImportService;
|
||||
|
||||
private openApiService: OpenApiService;
|
||||
|
||||
constructor(
|
||||
config: IUnleashConfig,
|
||||
{
|
||||
exportImportService,
|
||||
openApiService,
|
||||
}: Pick<IUnleashServices, 'exportImportService' | 'openApiService'>,
|
||||
) {
|
||||
super(config);
|
||||
this.logger = config.getLogger('/admin-api/export-import.ts');
|
||||
this.exportImportService = exportImportService;
|
||||
this.openApiService = openApiService;
|
||||
this.route({
|
||||
method: 'post',
|
||||
path: '/export',
|
||||
permission: NONE,
|
||||
handler: this.export,
|
||||
// middleware: [
|
||||
// this.openApiService.validPath({
|
||||
// tags: ['Import/Export'],
|
||||
// operationId: 'export',
|
||||
// responses: {
|
||||
// 200: createResponseSchema('stateSchema'),
|
||||
// },
|
||||
// parameters:
|
||||
// exportQueryParameters as unknown as OpenAPIV3.ParameterObject[],
|
||||
// }),
|
||||
// ],
|
||||
});
|
||||
}
|
||||
|
||||
async export(
|
||||
req: Request<unknown, unknown, IExportQuery, unknown>,
|
||||
res: Response,
|
||||
): Promise<void> {
|
||||
const query = req.body;
|
||||
const data = await this.exportImportService.export(query);
|
||||
|
||||
res.json(data);
|
||||
}
|
||||
}
|
||||
export default ExportImportController;
|
@ -28,6 +28,7 @@ import { PublicSignupController } from './public-signup';
|
||||
import InstanceAdminController from './instance-admin';
|
||||
import FavoritesController from './favorites';
|
||||
import MaintenanceController from './maintenance';
|
||||
import ExportImportController from './export-import';
|
||||
|
||||
class AdminApi extends Controller {
|
||||
constructor(config: IUnleashConfig, services: IUnleashServices) {
|
||||
@ -77,6 +78,10 @@ class AdminApi extends Controller {
|
||||
new ContextController(config, services).router,
|
||||
);
|
||||
this.app.use('/state', new StateController(config, services).router);
|
||||
this.app.use(
|
||||
'/features-batch',
|
||||
new ExportImportController(config, services).router,
|
||||
);
|
||||
this.app.use('/tags', new TagController(config, services).router);
|
||||
this.app.use(
|
||||
'/tag-types',
|
||||
|
87
src/lib/services/export-import-service.ts
Normal file
87
src/lib/services/export-import-service.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import { IUnleashConfig } from '../types/option';
|
||||
import { FeatureToggle, ITag } from '../types/model';
|
||||
import { Logger } from '../logger';
|
||||
import { IFeatureTagStore } from '../types/stores/feature-tag-store';
|
||||
import { IProjectStore } from '../types/stores/project-store';
|
||||
import { ITagTypeStore } from '../types/stores/tag-type-store';
|
||||
import { ITagStore } from '../types/stores/tag-store';
|
||||
import { IEventStore } from '../types/stores/event-store';
|
||||
import { IStrategyStore } from '../types/stores/strategy-store';
|
||||
import { IFeatureToggleStore } from '../types/stores/feature-toggle-store';
|
||||
import { IFeatureStrategiesStore } from '../types/stores/feature-strategies-store';
|
||||
import { IEnvironmentStore } from '../types/stores/environment-store';
|
||||
import { IFeatureEnvironmentStore } from '../types/stores/feature-environment-store';
|
||||
import { IUnleashStores } from '../types/stores';
|
||||
import { ISegmentStore } from '../types/stores/segment-store';
|
||||
import { IFlagResolver } from 'lib/types';
|
||||
import { IContextFieldDto } from '../types/stores/context-field-store';
|
||||
|
||||
export interface IExportQuery {
|
||||
features: string[];
|
||||
environment: string;
|
||||
}
|
||||
|
||||
export interface IExportData {
|
||||
features: FeatureToggle[];
|
||||
tags?: ITag[];
|
||||
contextFields?: IContextFieldDto[];
|
||||
}
|
||||
|
||||
export default class ExportImportService {
|
||||
private logger: Logger;
|
||||
|
||||
private toggleStore: IFeatureToggleStore;
|
||||
|
||||
private featureStrategiesStore: IFeatureStrategiesStore;
|
||||
|
||||
private strategyStore: IStrategyStore;
|
||||
|
||||
private eventStore: IEventStore;
|
||||
|
||||
private tagStore: ITagStore;
|
||||
|
||||
private tagTypeStore: ITagTypeStore;
|
||||
|
||||
private projectStore: IProjectStore;
|
||||
|
||||
private featureEnvironmentStore: IFeatureEnvironmentStore;
|
||||
|
||||
private featureTagStore: IFeatureTagStore;
|
||||
|
||||
private environmentStore: IEnvironmentStore;
|
||||
|
||||
private segmentStore: ISegmentStore;
|
||||
|
||||
private flagResolver: IFlagResolver;
|
||||
|
||||
constructor(
|
||||
stores: IUnleashStores,
|
||||
{
|
||||
getLogger,
|
||||
flagResolver,
|
||||
}: Pick<IUnleashConfig, 'getLogger' | 'flagResolver'>,
|
||||
) {
|
||||
this.eventStore = stores.eventStore;
|
||||
this.toggleStore = stores.featureToggleStore;
|
||||
this.strategyStore = stores.strategyStore;
|
||||
this.tagStore = stores.tagStore;
|
||||
this.featureStrategiesStore = stores.featureStrategiesStore;
|
||||
this.featureEnvironmentStore = stores.featureEnvironmentStore;
|
||||
this.tagTypeStore = stores.tagTypeStore;
|
||||
this.projectStore = stores.projectStore;
|
||||
this.featureTagStore = stores.featureTagStore;
|
||||
this.environmentStore = stores.environmentStore;
|
||||
this.segmentStore = stores.segmentStore;
|
||||
this.flagResolver = flagResolver;
|
||||
this.logger = getLogger('services/state-service.js');
|
||||
}
|
||||
|
||||
async export(query: IExportQuery): Promise<IExportData> {
|
||||
const features = (
|
||||
await this.toggleStore.getAll({ archived: false })
|
||||
).filter((toggle) => query.features.includes(toggle.name));
|
||||
return { features: features };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ExportImportService;
|
@ -39,6 +39,7 @@ import { LastSeenService } from './client-metrics/last-seen-service';
|
||||
import { InstanceStatsService } from './instance-stats-service';
|
||||
import { FavoritesService } from './favorites-service';
|
||||
import MaintenanceService from './maintenance-service';
|
||||
import ExportImportService from './export-import-service';
|
||||
|
||||
export const createServices = (
|
||||
stores: IUnleashStores,
|
||||
@ -60,6 +61,7 @@ export const createServices = (
|
||||
const featureTypeService = new FeatureTypeService(stores, config);
|
||||
const resetTokenService = new ResetTokenService(stores, config);
|
||||
const stateService = new StateService(stores, config);
|
||||
const exportImportService = new ExportImportService(stores, config);
|
||||
const strategyService = new StrategyService(stores, config);
|
||||
const tagService = new TagService(stores, config);
|
||||
const tagTypeService = new TagTypeService(stores, config);
|
||||
@ -176,6 +178,7 @@ export const createServices = (
|
||||
instanceStatsService,
|
||||
favoritesService,
|
||||
maintenanceService,
|
||||
exportImportService,
|
||||
};
|
||||
};
|
||||
|
||||
@ -218,4 +221,5 @@ export {
|
||||
LastSeenService,
|
||||
InstanceStatsService,
|
||||
FavoritesService,
|
||||
ExportImportService,
|
||||
};
|
||||
|
@ -37,6 +37,7 @@ import { LastSeenService } from '../services/client-metrics/last-seen-service';
|
||||
import { InstanceStatsService } from '../services/instance-stats-service';
|
||||
import { FavoritesService } from '../services';
|
||||
import MaintenanceService from '../services/maintenance-service';
|
||||
import ExportImportService from 'lib/services/export-import-service';
|
||||
|
||||
export interface IUnleashServices {
|
||||
accessService: AccessService;
|
||||
@ -79,4 +80,5 @@ export interface IUnleashServices {
|
||||
instanceStatsService: InstanceStatsService;
|
||||
favoritesService: FavoritesService;
|
||||
maintenanceService: MaintenanceService;
|
||||
exportImportService: ExportImportService;
|
||||
}
|
||||
|
72
src/test/e2e/api/admin/export-import.e2e.test.ts
Normal file
72
src/test/e2e/api/admin/export-import.e2e.test.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { IUnleashTest, setupApp } from '../../helpers/test-helper';
|
||||
import dbInit, { ITestDb } from '../../helpers/database-init';
|
||||
import getLogger from '../../../fixtures/no-logger';
|
||||
import { IEventStore } from 'lib/types/stores/event-store';
|
||||
import { FeatureToggleDTO, IStrategyConfig } from 'lib/types';
|
||||
import { DEFAULT_ENV } from '../../../../lib/util';
|
||||
|
||||
let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
let eventStore: IEventStore;
|
||||
|
||||
const createToggle = async (
|
||||
toggle: FeatureToggleDTO,
|
||||
strategy?: Omit<IStrategyConfig, 'id'>,
|
||||
projectId: string = 'default',
|
||||
username: string = 'test',
|
||||
) => {
|
||||
await app.services.featureToggleServiceV2.createFeatureToggle(
|
||||
projectId,
|
||||
toggle,
|
||||
username,
|
||||
);
|
||||
if (strategy) {
|
||||
await app.services.featureToggleServiceV2.createStrategy(
|
||||
strategy,
|
||||
{ projectId, featureName: toggle.name, environment: DEFAULT_ENV },
|
||||
username,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('export_import_api_serial', getLogger);
|
||||
app = await setupApp(db.stores);
|
||||
eventStore = db.stores.eventStore;
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await eventStore.deleteAll();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app.destroy();
|
||||
await db.destroy();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
db.stores.featureToggleStore.deleteAll();
|
||||
});
|
||||
|
||||
test('exports features', async () => {
|
||||
await createToggle({
|
||||
name: 'first_feature',
|
||||
description: 'the #1 feature',
|
||||
});
|
||||
const { body } = await app.request
|
||||
.post('/api/admin/features-batch/export')
|
||||
.send({
|
||||
features: ['first_feature'],
|
||||
environment: 'default',
|
||||
})
|
||||
.set('Content-Type', 'application/json')
|
||||
.expect(200);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
features: [
|
||||
{
|
||||
name: 'first_feature',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user