1
0
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:
sjaanus 2023-01-10 15:59:02 +02:00 committed by GitHub
parent 0c1e997f0b
commit f3f3a59e5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 229 additions and 0 deletions

View 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;

View File

@ -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',

View 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;

View File

@ -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,
};

View File

@ -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;
}

View 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',
},
],
});
});