1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-08-04 13:48:56 +02:00

task: add OpenApi spec to metrics route (#1725)

* task: add OpenApi spec to metrics route
This commit is contained in:
Christopher Kolstad 2022-06-21 09:12:40 +02:00 committed by GitHub
parent 28251af39e
commit 32399291e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 309 additions and 24 deletions

View File

@ -49,12 +49,16 @@ import { validateTagTypeSchema } from './spec/validate-tag-type-schema';
import { variantSchema } from './spec/variant-schema'; import { variantSchema } from './spec/variant-schema';
import { variantsSchema } from './spec/variants-schema'; import { variantsSchema } from './spec/variants-schema';
import { versionSchema } from './spec/version-schema'; import { versionSchema } from './spec/version-schema';
import { applicationSchema } from './spec/application-schema';
import { applicationsSchema } from './spec/applications-schema';
import { tagWithVersionSchema } from './spec/tag-with-version-schema'; import { tagWithVersionSchema } from './spec/tag-with-version-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 = {
apiTokenSchema, apiTokenSchema,
apiTokensSchema, apiTokensSchema,
applicationSchema,
applicationsSchema,
cloneFeatureSchema, cloneFeatureSchema,
constraintSchema, constraintSchema,
contextFieldSchema, contextFieldSchema,

View File

@ -0,0 +1,40 @@
import { FromSchema } from 'json-schema-to-ts';
export const applicationSchema = {
$id: '#/components/schemas/applicationSchema',
type: 'object',
additionalProperties: false,
required: ['appName'],
properties: {
appName: {
type: 'string',
},
sdkVersion: {
type: 'string',
},
strategies: {
type: 'array',
items: {
type: 'string',
},
},
description: {
type: 'string',
},
url: {
type: 'string',
},
color: {
type: 'string',
},
icon: {
type: 'string',
},
announced: {
type: 'boolean',
},
},
components: {},
} as const;
export type ApplicationSchema = FromSchema<typeof applicationSchema>;

View File

@ -0,0 +1,22 @@
import { applicationSchema } from './application-schema';
import { FromSchema } from 'json-schema-to-ts';
export const applicationsSchema = {
$id: '#/components/schemas/applicationsSchema',
type: 'object',
properties: {
applications: {
type: 'array',
items: {
$ref: '#/components/schemas/applicationSchema',
},
},
},
components: {
schemas: {
applicationSchema,
},
},
} as const;
export type ApplicationsSchema = FromSchema<typeof applicationsSchema>;

View File

@ -84,7 +84,7 @@ test('should store application', () => {
.expect(202); .expect(202);
}); });
test('should store application details wihtout strategies', () => { test('should store application details without strategies', () => {
expect.assertions(0); expect.assertions(0);
const appName = '123!23'; const appName = '123!23';

View File

@ -1,10 +1,14 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import Controller from '../controller'; import Controller from '../controller';
import { UPDATE_APPLICATION } from '../../types/permissions'; import { NONE, UPDATE_APPLICATION } from '../../types/permissions';
import { IUnleashConfig } from '../../types/option'; import { IUnleashConfig } from '../../types/option';
import { IUnleashServices } from '../../types/services'; import { IUnleashServices } from '../../types/services';
import { Logger } from '../../logger'; import { Logger } from '../../logger';
import ClientInstanceService from '../../services/client-metrics/instance-service'; import ClientInstanceService from '../../services/client-metrics/instance-service';
import { emptyResponse } from '../../openapi/spec/empty-response';
import { createRequestSchema, createResponseSchema } from '../../openapi';
import { ApplicationSchema } from '../../openapi/spec/application-schema';
import { ApplicationsSchema } from '../../openapi/spec/applications-schema';
class MetricsController extends Controller { class MetricsController extends Controller {
private logger: Logger; private logger: Logger;
@ -15,7 +19,8 @@ class MetricsController extends Controller {
config: IUnleashConfig, config: IUnleashConfig,
{ {
clientInstanceService, clientInstanceService,
}: Pick<IUnleashServices, 'clientInstanceService'>, openApiService,
}: Pick<IUnleashServices, 'clientInstanceService' | 'openApiService'>,
) { ) {
super(config); super(config);
this.logger = config.getLogger('/admin-api/metrics.ts'); this.logger = config.getLogger('/admin-api/metrics.ts');
@ -28,19 +33,68 @@ class MetricsController extends Controller {
this.get('/feature-toggles', this.deprecated); this.get('/feature-toggles', this.deprecated);
this.get('/feature-toggles/:name', this.deprecated); this.get('/feature-toggles/:name', this.deprecated);
// in use this.route({
this.post( method: 'post',
'/applications/:appName', path: '/applications/:appName',
this.createApplication, handler: this.createApplication,
UPDATE_APPLICATION, permission: UPDATE_APPLICATION,
); middleware: [
this.delete( openApiService.validPath({
'/applications/:appName', tags: ['admin'],
this.deleteApplication, operationId: 'createApplication',
UPDATE_APPLICATION, responses: {
); 202: emptyResponse,
this.get('/applications/', this.getApplications); },
this.get('/applications/:appName', this.getApplication); requestBody: createRequestSchema('applicationSchema'),
}),
],
});
this.route({
method: 'delete',
path: '/applications/:appName',
handler: this.deleteApplication,
permission: UPDATE_APPLICATION,
acceptAnyContentType: true,
middleware: [
openApiService.validPath({
tags: ['admin'],
operationId: 'deleteApplication',
responses: {
200: emptyResponse,
},
}),
],
});
this.route({
method: 'get',
path: '/applications',
handler: this.getApplications,
permission: NONE,
middleware: [
openApiService.validPath({
tags: ['admin'],
operationId: 'getApplications',
responses: {
200: createResponseSchema('applicationsSchema'),
},
}),
],
});
this.route({
method: 'get',
path: '/applications/:appName',
handler: this.getApplication,
permission: NONE,
middleware: [
openApiService.validPath({
tags: ['admin'],
operationId: 'getApplication',
responses: {
200: createResponseSchema('applicationSchema'),
},
}),
],
});
} }
async deprecated(req: Request, res: Response): Promise<void> { async deprecated(req: Request, res: Response): Promise<void> {
@ -51,20 +105,32 @@ class MetricsController extends Controller {
}); });
} }
async deleteApplication(req: Request, res: Response): Promise<void> { async deleteApplication(
req: Request<{ appName: string }>,
res: Response,
): Promise<void> {
const { appName } = req.params; const { appName } = req.params;
await this.clientInstanceService.deleteApplication(appName); await this.clientInstanceService.deleteApplication(appName);
res.status(200).end(); res.status(200).end();
} }
async createApplication(req: Request, res: Response): Promise<void> { async createApplication(
const input = { ...req.body, appName: req.params.appName }; req: Request<{ appName: string }, unknown, ApplicationSchema>,
res: Response,
): Promise<void> {
const input = {
...req.body,
appName: req.params.appName,
};
await this.clientInstanceService.createApplication(input); await this.clientInstanceService.createApplication(input);
res.status(202).end(); res.status(202).end();
} }
async getApplications(req: Request, res: Response): Promise<void> { async getApplications(
req: Request,
res: Response<ApplicationsSchema>,
): Promise<void> {
const query = req.query.strategyName const query = req.query.strategyName
? { strategyName: req.query.strategyName as string } ? { strategyName: req.query.strategyName as string }
: {}; : {};
@ -74,7 +140,10 @@ class MetricsController extends Controller {
res.json({ applications }); res.json({ applications });
} }
async getApplication(req: Request, res: Response): Promise<void> { async getApplication(
req: Request,
res: Response<ApplicationSchema>,
): Promise<void> {
const { appName } = req.params; const { appName } = req.params;
const appDetails = await this.clientInstanceService.getApplication( const appDetails = await this.clientInstanceService.getApplication(

View File

@ -22,8 +22,8 @@ export interface IApplication {
url?: string; url?: string;
color?: string; color?: string;
icon?: string; icon?: string;
createdAt: Date; createdAt?: Date;
instances?: IClientInstance[]; instances?: IClientInstance[];
seenToggles: Record<string, any>; seenToggles?: Record<string, any>;
links: Record<string, string>; links?: Record<string, string>;
} }

View File

@ -113,6 +113,53 @@ Object {
], ],
"type": "object", "type": "object",
}, },
"applicationSchema": Object {
"additionalProperties": false,
"properties": Object {
"announced": Object {
"type": "boolean",
},
"appName": Object {
"type": "string",
},
"color": Object {
"type": "string",
},
"description": Object {
"type": "string",
},
"icon": Object {
"type": "string",
},
"sdkVersion": Object {
"type": "string",
},
"strategies": Object {
"items": Object {
"type": "string",
},
"type": "array",
},
"url": Object {
"type": "string",
},
},
"required": Array [
"appName",
],
"type": "object",
},
"applicationsSchema": Object {
"properties": Object {
"applications": Object {
"items": Object {
"$ref": "#/components/schemas/applicationSchema",
},
"type": "array",
},
},
"type": "object",
},
"cloneFeatureSchema": Object { "cloneFeatureSchema": Object {
"properties": Object { "properties": Object {
"name": Object { "name": Object {
@ -2011,6 +2058,109 @@ Object {
], ],
}, },
}, },
"/api/admin/metrics/applications": Object {
"get": Object {
"operationId": "getApplications",
"responses": Object {
"200": Object {
"content": Object {
"application/json": Object {
"schema": Object {
"$ref": "#/components/schemas/applicationsSchema",
},
},
},
"description": "applicationsSchema",
},
},
"tags": Array [
"admin",
],
},
},
"/api/admin/metrics/applications/{appName}": Object {
"delete": Object {
"operationId": "deleteApplication",
"parameters": Array [
Object {
"in": "path",
"name": "appName",
"required": true,
"schema": Object {
"type": "string",
},
},
],
"responses": Object {
"200": Object {
"description": "emptyResponse",
},
},
"tags": Array [
"admin",
],
},
"get": Object {
"operationId": "getApplication",
"parameters": Array [
Object {
"in": "path",
"name": "appName",
"required": true,
"schema": Object {
"type": "string",
},
},
],
"responses": Object {
"200": Object {
"content": Object {
"application/json": Object {
"schema": Object {
"$ref": "#/components/schemas/applicationSchema",
},
},
},
"description": "applicationSchema",
},
},
"tags": Array [
"admin",
],
},
"post": Object {
"operationId": "createApplication",
"parameters": Array [
Object {
"in": "path",
"name": "appName",
"required": true,
"schema": Object {
"type": "string",
},
},
],
"requestBody": Object {
"content": Object {
"application/json": Object {
"schema": Object {
"$ref": "#/components/schemas/applicationSchema",
},
},
},
"description": "applicationSchema",
"required": true,
},
"responses": Object {
"202": Object {
"description": "emptyResponse",
},
},
"tags": Array [
"admin",
],
},
},
"/api/admin/projects": Object { "/api/admin/projects": Object {
"get": Object { "get": Object {
"operationId": "getProjects", "operationId": "getProjects",