2021-04-22 10:07:10 +02:00
|
|
|
import * as mime from 'mime';
|
|
|
|
import YAML from 'js-yaml';
|
|
|
|
import multer from 'multer';
|
2021-11-02 15:13:46 +01:00
|
|
|
import { format as formatDate } from 'date-fns';
|
2021-04-22 10:07:10 +02:00
|
|
|
import { Request, Response } from 'express';
|
|
|
|
import Controller from '../controller';
|
2021-05-02 20:58:02 +02:00
|
|
|
import { ADMIN } from '../../types/permissions';
|
2021-09-14 19:58:48 +02:00
|
|
|
import { extractUsername } from '../../util/extract-user';
|
2021-04-22 10:07:10 +02:00
|
|
|
import { IUnleashConfig } from '../../types/option';
|
|
|
|
import { IUnleashServices } from '../../types/services';
|
|
|
|
import { Logger } from '../../logger';
|
|
|
|
import StateService from '../../services/state-service';
|
2021-09-14 19:58:48 +02:00
|
|
|
import { IAuthRequest } from '../unleash-types';
|
2022-06-22 09:09:49 +02:00
|
|
|
import { OpenApiService } from '../../services/openapi-service';
|
2022-07-01 08:06:33 +02:00
|
|
|
import { createRequestSchema } from '../../openapi/util/create-request-schema';
|
|
|
|
import { createResponseSchema } from '../../openapi/util/create-response-schema';
|
2022-07-28 09:19:58 +02:00
|
|
|
import {
|
|
|
|
exportQueryParameters,
|
|
|
|
ExportQueryParameters,
|
|
|
|
} from '../../openapi/spec/export-query-parameters';
|
2022-06-30 14:48:39 +02:00
|
|
|
import { emptyResponse } from '../../openapi/util/standard-responses';
|
2022-07-28 09:19:58 +02:00
|
|
|
import { OpenAPIV3 } from 'openapi-types';
|
2020-04-14 22:29:11 +02:00
|
|
|
|
2019-03-13 19:10:13 +01:00
|
|
|
const upload = multer({ limits: { fileSize: 5242880 } });
|
2021-03-12 11:08:10 +01:00
|
|
|
const paramToBool = (param, def) => {
|
|
|
|
if (param === null || param === undefined) {
|
|
|
|
return def;
|
|
|
|
}
|
|
|
|
const nu = Number.parseInt(param, 10);
|
|
|
|
if (Number.isNaN(nu)) {
|
|
|
|
return param.toLowerCase() === 'true';
|
|
|
|
}
|
|
|
|
return Boolean(nu);
|
|
|
|
};
|
2019-03-14 17:56:02 +01:00
|
|
|
class StateController extends Controller {
|
2021-04-22 10:07:10 +02:00
|
|
|
private logger: Logger;
|
|
|
|
|
|
|
|
private stateService: StateService;
|
|
|
|
|
2022-06-22 09:09:49 +02:00
|
|
|
private openApiService: OpenApiService;
|
|
|
|
|
2021-04-22 10:07:10 +02:00
|
|
|
constructor(
|
|
|
|
config: IUnleashConfig,
|
2022-06-22 09:09:49 +02:00
|
|
|
{
|
|
|
|
stateService,
|
|
|
|
openApiService,
|
|
|
|
}: Pick<IUnleashServices, 'stateService' | 'openApiService'>,
|
2021-04-22 10:07:10 +02:00
|
|
|
) {
|
2019-03-13 19:10:13 +01:00
|
|
|
super(config);
|
2021-07-07 10:46:50 +02:00
|
|
|
this.logger = config.getLogger('/admin-api/state.ts');
|
2021-04-22 10:07:10 +02:00
|
|
|
this.stateService = stateService;
|
2022-06-22 09:09:49 +02:00
|
|
|
this.openApiService = openApiService;
|
2019-03-13 19:10:13 +01:00
|
|
|
this.fileupload('/import', upload.single('file'), this.import, ADMIN);
|
2022-06-22 09:09:49 +02:00
|
|
|
this.route({
|
|
|
|
method: 'post',
|
|
|
|
path: '/import',
|
|
|
|
permission: ADMIN,
|
|
|
|
handler: this.import,
|
|
|
|
middleware: [
|
|
|
|
this.openApiService.validPath({
|
|
|
|
tags: ['admin'],
|
|
|
|
operationId: 'import',
|
|
|
|
responses: {
|
|
|
|
202: emptyResponse,
|
|
|
|
},
|
|
|
|
requestBody: createRequestSchema('stateSchema'),
|
|
|
|
}),
|
|
|
|
],
|
|
|
|
});
|
|
|
|
this.route({
|
|
|
|
method: 'get',
|
|
|
|
path: '/export',
|
|
|
|
permission: ADMIN,
|
|
|
|
handler: this.export,
|
|
|
|
middleware: [
|
|
|
|
this.openApiService.validPath({
|
|
|
|
tags: ['admin'],
|
|
|
|
operationId: 'export',
|
|
|
|
responses: {
|
|
|
|
200: createResponseSchema('stateSchema'),
|
|
|
|
},
|
2022-07-28 09:19:58 +02:00
|
|
|
parameters:
|
|
|
|
exportQueryParameters as unknown as OpenAPIV3.ParameterObject[],
|
2022-06-22 09:09:49 +02:00
|
|
|
}),
|
|
|
|
],
|
|
|
|
});
|
2019-03-13 19:10:13 +01:00
|
|
|
}
|
|
|
|
|
2021-09-14 19:58:48 +02:00
|
|
|
async import(req: IAuthRequest, res: Response): Promise<void> {
|
|
|
|
const userName = extractUsername(req);
|
2020-11-03 14:56:07 +01:00
|
|
|
const { drop, keep } = req.query;
|
2021-04-22 10:07:10 +02:00
|
|
|
// TODO: Should override request type so file is a type on request
|
2021-08-13 10:36:19 +02:00
|
|
|
let data;
|
2022-06-07 11:49:17 +02:00
|
|
|
// @ts-expect-error
|
2021-08-13 10:36:19 +02:00
|
|
|
if (req.file) {
|
2022-06-07 11:49:17 +02:00
|
|
|
// @ts-expect-error
|
2021-08-13 10:36:19 +02:00
|
|
|
if (mime.getType(req.file.originalname) === 'text/yaml') {
|
2022-06-07 11:49:17 +02:00
|
|
|
// @ts-expect-error
|
2021-09-28 20:53:39 +02:00
|
|
|
data = YAML.load(req.file.buffer);
|
2019-03-13 19:10:13 +01:00
|
|
|
} else {
|
2022-06-07 11:49:17 +02:00
|
|
|
// @ts-expect-error
|
2021-08-13 10:36:19 +02:00
|
|
|
data = JSON.parse(req.file.buffer);
|
2019-03-13 19:10:13 +01:00
|
|
|
}
|
2021-08-13 10:36:19 +02:00
|
|
|
} else {
|
|
|
|
data = req.body;
|
2019-03-13 19:10:13 +01:00
|
|
|
}
|
2021-08-13 10:36:19 +02:00
|
|
|
|
|
|
|
await this.stateService.import({
|
|
|
|
data,
|
|
|
|
userName,
|
|
|
|
dropBeforeImport: paramToBool(drop, false),
|
|
|
|
keepExisting: paramToBool(keep, true),
|
|
|
|
});
|
|
|
|
res.sendStatus(202);
|
2019-03-13 19:10:13 +01:00
|
|
|
}
|
|
|
|
|
2022-06-22 09:09:49 +02:00
|
|
|
async export(
|
2022-07-28 09:19:58 +02:00
|
|
|
req: Request<unknown, unknown, unknown, ExportQueryParameters>,
|
2022-06-22 09:09:49 +02:00
|
|
|
res: Response,
|
|
|
|
): Promise<void> {
|
2019-03-13 19:10:13 +01:00
|
|
|
const { format } = req.query;
|
|
|
|
|
2021-03-12 11:08:10 +01:00
|
|
|
const downloadFile = paramToBool(req.query.download, false);
|
|
|
|
const includeStrategies = paramToBool(req.query.strategies, true);
|
|
|
|
const includeFeatureToggles = paramToBool(
|
|
|
|
req.query.featureToggles,
|
|
|
|
true,
|
|
|
|
);
|
|
|
|
const includeProjects = paramToBool(req.query.projects, true);
|
|
|
|
const includeTags = paramToBool(req.query.tags, true);
|
2021-08-12 15:04:37 +02:00
|
|
|
const includeEnvironments = paramToBool(req.query.environments, true);
|
2019-03-13 19:10:13 +01:00
|
|
|
|
2021-08-13 10:36:19 +02:00
|
|
|
const data = await this.stateService.export({
|
|
|
|
includeStrategies,
|
|
|
|
includeFeatureToggles,
|
|
|
|
includeProjects,
|
|
|
|
includeTags,
|
|
|
|
includeEnvironments,
|
|
|
|
});
|
2021-11-02 15:13:46 +01:00
|
|
|
const timestamp = formatDate(Date.now(), 'yyyy-MM-dd_HH-mm-ss');
|
2021-08-13 10:36:19 +02:00
|
|
|
if (format === 'yaml') {
|
|
|
|
if (downloadFile) {
|
|
|
|
res.attachment(`export-${timestamp}.yml`);
|
|
|
|
}
|
2021-09-28 20:53:39 +02:00
|
|
|
res.type('yaml').send(YAML.dump(data, { skipInvalid: true }));
|
2021-08-13 10:36:19 +02:00
|
|
|
} else {
|
|
|
|
if (downloadFile) {
|
|
|
|
res.attachment(`export-${timestamp}.json`);
|
2019-03-13 19:10:13 +01:00
|
|
|
}
|
2021-08-13 10:36:19 +02:00
|
|
|
res.json(data);
|
2019-03-13 19:10:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-04-22 10:07:10 +02:00
|
|
|
export default StateController;
|