mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-09 00:18:00 +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 InstanceAdminController from './instance-admin';
|
||||||
import FavoritesController from './favorites';
|
import FavoritesController from './favorites';
|
||||||
import MaintenanceController from './maintenance';
|
import MaintenanceController from './maintenance';
|
||||||
|
import ExportImportController from './export-import';
|
||||||
|
|
||||||
class AdminApi extends Controller {
|
class AdminApi extends Controller {
|
||||||
constructor(config: IUnleashConfig, services: IUnleashServices) {
|
constructor(config: IUnleashConfig, services: IUnleashServices) {
|
||||||
@ -77,6 +78,10 @@ class AdminApi extends Controller {
|
|||||||
new ContextController(config, services).router,
|
new ContextController(config, services).router,
|
||||||
);
|
);
|
||||||
this.app.use('/state', new StateController(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('/tags', new TagController(config, services).router);
|
||||||
this.app.use(
|
this.app.use(
|
||||||
'/tag-types',
|
'/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 { InstanceStatsService } from './instance-stats-service';
|
||||||
import { FavoritesService } from './favorites-service';
|
import { FavoritesService } from './favorites-service';
|
||||||
import MaintenanceService from './maintenance-service';
|
import MaintenanceService from './maintenance-service';
|
||||||
|
import ExportImportService from './export-import-service';
|
||||||
|
|
||||||
export const createServices = (
|
export const createServices = (
|
||||||
stores: IUnleashStores,
|
stores: IUnleashStores,
|
||||||
@ -60,6 +61,7 @@ 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);
|
||||||
@ -176,6 +178,7 @@ export const createServices = (
|
|||||||
instanceStatsService,
|
instanceStatsService,
|
||||||
favoritesService,
|
favoritesService,
|
||||||
maintenanceService,
|
maintenanceService,
|
||||||
|
exportImportService,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -218,4 +221,5 @@ export {
|
|||||||
LastSeenService,
|
LastSeenService,
|
||||||
InstanceStatsService,
|
InstanceStatsService,
|
||||||
FavoritesService,
|
FavoritesService,
|
||||||
|
ExportImportService,
|
||||||
};
|
};
|
||||||
|
@ -37,6 +37,7 @@ import { LastSeenService } from '../services/client-metrics/last-seen-service';
|
|||||||
import { InstanceStatsService } from '../services/instance-stats-service';
|
import { InstanceStatsService } from '../services/instance-stats-service';
|
||||||
import { FavoritesService } from '../services';
|
import { FavoritesService } from '../services';
|
||||||
import MaintenanceService from '../services/maintenance-service';
|
import MaintenanceService from '../services/maintenance-service';
|
||||||
|
import ExportImportService from 'lib/services/export-import-service';
|
||||||
|
|
||||||
export interface IUnleashServices {
|
export interface IUnleashServices {
|
||||||
accessService: AccessService;
|
accessService: AccessService;
|
||||||
@ -79,4 +80,5 @@ export interface IUnleashServices {
|
|||||||
instanceStatsService: InstanceStatsService;
|
instanceStatsService: InstanceStatsService;
|
||||||
favoritesService: FavoritesService;
|
favoritesService: FavoritesService;
|
||||||
maintenanceService: MaintenanceService;
|
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