1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-02-09 00:18:00 +01:00

Task/open api state (#1738)

* task: add open api to import/export
This commit is contained in:
Christopher Kolstad 2022-06-22 09:09:49 +02:00 committed by GitHub
parent 5bae11a3fb
commit 18c720f4e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 495 additions and 4 deletions

View File

@ -52,6 +52,11 @@ import { versionSchema } from './spec/version-schema';
import { applicationSchema } from './spec/application-schema'; import { applicationSchema } from './spec/application-schema';
import { applicationsSchema } from './spec/applications-schema'; import { applicationsSchema } from './spec/applications-schema';
import { tagWithVersionSchema } from './spec/tag-with-version-schema'; import { tagWithVersionSchema } from './spec/tag-with-version-schema';
import { featureStrategySegmentSchema } from './spec/feature-strategy-segment-schema';
import { segmentSchema } from './spec/segment-schema';
import { stateSchema } from './spec/state-schema';
import { featureTagSchema } from './spec/feature-tag-schema';
import { exportParametersSchema } from './spec/export-parameters-schema';
// All schemas in `openapi/spec` should be listed here. // All schemas in `openapi/spec` should be listed here.
export const schemas = { export const schemas = {
@ -68,9 +73,12 @@ export const schemas = {
createStrategySchema, createStrategySchema,
environmentSchema, environmentSchema,
environmentsSchema, environmentsSchema,
exportParametersSchema,
featureEnvironmentSchema, featureEnvironmentSchema,
featureSchema, featureSchema,
featureStrategySchema, featureStrategySchema,
featureStrategySegmentSchema,
featureTagSchema,
featureTypeSchema, featureTypeSchema,
featureTypesSchema, featureTypesSchema,
featureVariantsSchema, featureVariantsSchema,
@ -88,8 +96,10 @@ export const schemas = {
projectEnvironmentSchema, projectEnvironmentSchema,
projectSchema, projectSchema,
projectsSchema, projectsSchema,
segmentSchema,
sortOrderSchema, sortOrderSchema,
splashSchema, splashSchema,
stateSchema,
strategySchema, strategySchema,
tagSchema, tagSchema,
tagWithVersionSchema, tagWithVersionSchema,

View File

@ -0,0 +1,32 @@
import { FromSchema } from 'json-schema-to-ts';
export const exportParametersSchema = {
$id: '#/components/schemas/exportParametersSchema',
type: 'object',
properties: {
format: {
type: 'string',
},
download: {
type: 'boolean',
},
strategies: {
type: 'boolean',
},
featureToggles: {
type: 'boolean',
},
projects: {
type: 'boolean',
},
tags: {
type: 'boolean',
},
environments: {
type: 'boolean',
},
},
components: {},
} as const;
export type ExportParametersSchema = FromSchema<typeof exportParametersSchema>;

View File

@ -0,0 +1,21 @@
import { FromSchema } from 'json-schema-to-ts';
export const featureStrategySegmentSchema = {
$id: '#/components/schemas/featureStrategySegmentSchema',
type: 'object',
additionalProperties: false,
required: ['segmentId', 'featureStrategyId'],
properties: {
segmentId: {
type: 'integer',
},
featureStrategyId: {
type: 'string',
},
},
components: {},
} as const;
export type FeatureStrategySegmentSchema = FromSchema<
typeof featureStrategySegmentSchema
>;

View File

@ -0,0 +1,28 @@
import { FromSchema } from 'json-schema-to-ts';
export const featureTagSchema = {
$id: '#/components/schemas/featureTagSchema',
type: 'object',
additionalProperties: false,
required: ['featureName', 'tagValue'],
properties: {
featureName: {
type: 'string',
},
tagType: {
type: 'string',
},
tagValue: {
type: 'string',
},
type: {
type: 'string',
},
value: {
type: 'string',
},
},
components: {},
} as const;
export type FeatureTagSchema = FromSchema<typeof featureTagSchema>;

View File

@ -0,0 +1,30 @@
import { FromSchema } from 'json-schema-to-ts';
import { constraintSchema } from './constraint-schema';
export const segmentSchema = {
$id: '#/components/schemas/segmentSchema',
type: 'object',
additionalProperties: false,
required: ['name', 'constraints'],
properties: {
name: {
type: 'string',
},
description: {
type: 'string',
},
constraints: {
type: 'array',
items: {
$ref: '#/components/schemas/constraintSchema',
},
},
},
components: {
schemas: {
constraintSchema,
},
},
} as const;
export type SegmentSchema = FromSchema<typeof segmentSchema>;

View File

@ -0,0 +1,107 @@
import { FromSchema } from 'json-schema-to-ts';
import { featureSchema } from './feature-schema';
import { strategySchema } from './strategy-schema';
import { tagSchema } from './tag-schema';
import { tagTypeSchema } from './tag-type-schema';
import { featureTagSchema } from './feature-tag-schema';
import { projectSchema } from './project-schema';
import { featureStrategySchema } from './feature-strategy-schema';
import { featureEnvironmentSchema } from './feature-environment-schema';
import { environmentSchema } from './environment-schema';
import { segmentSchema } from './segment-schema';
import { featureStrategySegmentSchema } from './feature-strategy-segment-schema';
export const stateSchema = {
$id: '#/components/schemas/stateSchema',
type: 'object',
additionalProperties: true,
required: ['version'],
properties: {
version: {
type: 'integer',
},
features: {
type: 'array',
items: {
$ref: '#/components/schemas/featureSchema',
},
},
strategies: {
type: 'array',
items: {
$ref: '#/components/schemas/strategySchema',
},
},
tags: {
type: 'array',
items: {
$ref: '#/components/schemas/tagSchema',
},
},
tagTypes: {
type: 'array',
items: {
$ref: '#/components/schemas/tagTypeSchema',
},
},
featureTags: {
type: 'array',
items: {
$ref: '#/components/schemas/featureTagSchema',
},
},
projects: {
type: 'array',
items: {
$ref: '#/components/schemas/projectSchema',
},
},
featureStrategies: {
type: 'array',
items: {
$ref: '#/components/schemas/featureStrategySchema',
},
},
featureEnvironments: {
type: 'array',
items: {
$ref: '#/components/schemas/featureEnvironmentSchema',
},
},
environments: {
type: 'array',
items: {
$ref: '#/components/schemas/environmentSchema',
},
},
segments: {
type: 'array',
items: {
$ref: '#/components/schemas/segmentSchema',
},
},
featureStrategySegments: {
type: 'array',
items: {
$ref: '#/components/schemas/featureStrategySegmentSchema',
},
},
},
components: {
schemas: {
featureSchema,
strategySchema,
tagSchema,
tagTypeSchema,
featureTagSchema,
projectSchema,
featureStrategySchema,
featureEnvironmentSchema,
environmentSchema,
segmentSchema,
featureStrategySegmentSchema,
},
},
} as const;
export type StateSchema = FromSchema<typeof stateSchema>;

View File

@ -11,6 +11,10 @@ import { IUnleashServices } from '../../types/services';
import { Logger } from '../../logger'; import { Logger } from '../../logger';
import StateService from '../../services/state-service'; import StateService from '../../services/state-service';
import { IAuthRequest } from '../unleash-types'; import { IAuthRequest } from '../unleash-types';
import { OpenApiService } from '../../services/openapi-service';
import { createRequestSchema, createResponseSchema } from '../../openapi';
import { emptyResponse } from '../../openapi/spec/empty-response';
import { ExportParametersSchema } from '../../openapi/spec/export-parameters-schema';
const upload = multer({ limits: { fileSize: 5242880 } }); const upload = multer({ limits: { fileSize: 5242880 } });
const paramToBool = (param, def) => { const paramToBool = (param, def) => {
@ -28,16 +32,56 @@ class StateController extends Controller {
private stateService: StateService; private stateService: StateService;
private openApiService: OpenApiService;
constructor( constructor(
config: IUnleashConfig, config: IUnleashConfig,
{ stateService }: Pick<IUnleashServices, 'stateService'>, {
stateService,
openApiService,
}: Pick<IUnleashServices, 'stateService' | 'openApiService'>,
) { ) {
super(config); super(config);
this.logger = config.getLogger('/admin-api/state.ts'); this.logger = config.getLogger('/admin-api/state.ts');
this.stateService = stateService; this.stateService = stateService;
this.openApiService = openApiService;
this.fileupload('/import', upload.single('file'), this.import, ADMIN); this.fileupload('/import', upload.single('file'), this.import, ADMIN);
this.post('/import', this.import, ADMIN); this.route({
this.get('/export', this.export, ADMIN); 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'),
},
parameters: [
{
$ref: '#/components/schema/exportParametersSchema',
},
],
}),
],
});
} }
async import(req: IAuthRequest, res: Response): Promise<void> { async import(req: IAuthRequest, res: Response): Promise<void> {
@ -68,7 +112,10 @@ class StateController extends Controller {
res.sendStatus(202); res.sendStatus(202);
} }
async export(req: Request, res: Response): Promise<void> { async export(
req: Request<unknown, unknown, unknown, ExportParametersSchema>,
res: Response,
): Promise<void> {
const { format } = req.query; const { format } = req.query;
const downloadFile = paramToBool(req.query.download, false); const downloadFile = paramToBool(req.query.download, false);

View File

@ -384,6 +384,32 @@ Object {
], ],
"type": "object", "type": "object",
}, },
"exportParametersSchema": Object {
"properties": Object {
"download": Object {
"type": "boolean",
},
"environments": Object {
"type": "boolean",
},
"featureToggles": Object {
"type": "boolean",
},
"format": Object {
"type": "string",
},
"projects": Object {
"type": "boolean",
},
"strategies": Object {
"type": "boolean",
},
"tags": Object {
"type": "boolean",
},
},
"type": "object",
},
"featureEnvironmentSchema": Object { "featureEnvironmentSchema": Object {
"additionalProperties": false, "additionalProperties": false,
"properties": Object { "properties": Object {
@ -522,6 +548,47 @@ Object {
], ],
"type": "object", "type": "object",
}, },
"featureStrategySegmentSchema": Object {
"additionalProperties": false,
"properties": Object {
"featureStrategyId": Object {
"type": "string",
},
"segmentId": Object {
"type": "integer",
},
},
"required": Array [
"segmentId",
"featureStrategyId",
],
"type": "object",
},
"featureTagSchema": Object {
"additionalProperties": false,
"properties": Object {
"featureName": Object {
"type": "string",
},
"tagType": Object {
"type": "string",
},
"tagValue": Object {
"type": "string",
},
"type": Object {
"type": "string",
},
"value": Object {
"type": "string",
},
},
"required": Array [
"featureName",
"tagValue",
],
"type": "object",
},
"featureTypeSchema": Object { "featureTypeSchema": Object {
"additionalProperties": false, "additionalProperties": false,
"properties": Object { "properties": Object {
@ -889,6 +956,28 @@ Object {
], ],
"type": "object", "type": "object",
}, },
"segmentSchema": Object {
"additionalProperties": false,
"properties": Object {
"constraints": Object {
"items": Object {
"$ref": "#/components/schemas/constraintSchema",
},
"type": "array",
},
"description": Object {
"type": "string",
},
"name": Object {
"type": "string",
},
},
"required": Array [
"name",
"constraints",
],
"type": "object",
},
"sortOrderSchema": Object { "sortOrderSchema": Object {
"additionalProperties": Object { "additionalProperties": Object {
"type": "number", "type": "number",
@ -915,6 +1004,84 @@ Object {
], ],
"type": "object", "type": "object",
}, },
"stateSchema": Object {
"additionalProperties": true,
"properties": Object {
"environments": Object {
"items": Object {
"$ref": "#/components/schemas/environmentSchema",
},
"type": "array",
},
"featureEnvironments": Object {
"items": Object {
"$ref": "#/components/schemas/featureEnvironmentSchema",
},
"type": "array",
},
"featureStrategies": Object {
"items": Object {
"$ref": "#/components/schemas/featureStrategySchema",
},
"type": "array",
},
"featureStrategySegments": Object {
"items": Object {
"$ref": "#/components/schemas/featureStrategySegmentSchema",
},
"type": "array",
},
"featureTags": Object {
"items": Object {
"$ref": "#/components/schemas/featureTagSchema",
},
"type": "array",
},
"features": Object {
"items": Object {
"$ref": "#/components/schemas/featureSchema",
},
"type": "array",
},
"projects": Object {
"items": Object {
"$ref": "#/components/schemas/projectSchema",
},
"type": "array",
},
"segments": Object {
"items": Object {
"$ref": "#/components/schemas/segmentSchema",
},
"type": "array",
},
"strategies": Object {
"items": Object {
"$ref": "#/components/schemas/strategySchema",
},
"type": "array",
},
"tagTypes": Object {
"items": Object {
"$ref": "#/components/schemas/tagTypeSchema",
},
"type": "array",
},
"tags": Object {
"items": Object {
"$ref": "#/components/schemas/tagSchema",
},
"type": "array",
},
"version": Object {
"type": "integer",
},
},
"required": Array [
"version",
],
"type": "object",
},
"strategySchema": Object { "strategySchema": Object {
"additionalProperties": false, "additionalProperties": false,
"properties": Object { "properties": Object {
@ -3208,6 +3375,55 @@ Object {
], ],
}, },
}, },
"/api/admin/state/export": Object {
"get": Object {
"operationId": "export",
"parameters": Array [
Object {
"$ref": "#/components/schema/exportParametersSchema",
},
],
"responses": Object {
"200": Object {
"content": Object {
"application/json": Object {
"schema": Object {
"$ref": "#/components/schemas/stateSchema",
},
},
},
"description": "stateSchema",
},
},
"tags": Array [
"admin",
],
},
},
"/api/admin/state/import": Object {
"post": Object {
"operationId": "import",
"requestBody": Object {
"content": Object {
"application/json": Object {
"schema": Object {
"$ref": "#/components/schemas/stateSchema",
},
},
},
"description": "stateSchema",
"required": true,
},
"responses": Object {
"202": Object {
"description": "emptyResponse",
},
},
"tags": Array [
"admin",
],
},
},
"/api/admin/tag-types": Object { "/api/admin/tag-types": Object {
"get": Object { "get": Object {
"operationId": "getTagTypes", "operationId": "getTagTypes",