1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

feat: Frontend api openapi spec (#4133)

This commit is contained in:
Mateusz Kwasniewski 2023-07-03 15:48:09 +02:00 committed by GitHub
parent d3b3652984
commit b50b06c257
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 358 additions and 11 deletions

View File

@ -9,12 +9,19 @@ export const proxyFeatureSchema = {
properties: {
name: {
type: 'string',
example: 'disable-comments',
description: 'Unique feature name.',
},
enabled: {
type: 'boolean',
example: true,
description: 'Always set to `true`.',
},
impressionData: {
type: 'boolean',
example: false,
description:
'`true` if the impression data collection is enabled for the feature, otherwise `false`.',
},
variant: {
type: 'object',
@ -23,20 +30,31 @@ export const proxyFeatureSchema = {
properties: {
name: {
type: 'string',
description:
'The variants name. Is unique for this feature toggle',
example: 'blue_group',
},
enabled: {
type: 'boolean',
example: true,
description: 'Whether the variant is enabled or not.',
},
payload: {
type: 'object',
additionalProperties: false,
required: ['type', 'value'],
description: 'Extra data configured for this variant',
example: { type: 'json', value: '{color: red}' },
properties: {
type: {
type: 'string',
description: 'The format of the payload.',
enum: Object.values(PayloadType),
},
value: { type: 'string' },
value: {
type: 'string',
description: 'The payload value stringified.',
},
},
},
},

View File

@ -10,7 +10,6 @@ const ClientApi = require('./client-api');
const Controller = require('./controller');
import { HealthCheckController } from './health-check';
import ProxyController from './proxy-api';
import { conditionalMiddleware } from '../middleware';
import EdgeController from './edge-api';
import { PublicInviteController } from './public-invite';
import { Db } from '../db/db';
@ -47,11 +46,7 @@ class IndexRouter extends Controller {
this.use(
'/api/frontend',
conditionalMiddleware(
() => config.flagResolver.isEnabled('embedProxy'),
new ProxyController(config, services, config.flagResolver)
.router,
),
new ProxyController(config, services, config.flagResolver).router,
);
this.use('/edge', new EdgeController(config, services).router);

View File

@ -13,6 +13,7 @@ import {
createRequestSchema,
createResponseSchema,
emptyResponse,
getStandardResponses,
ProxyClientSchema,
proxyFeaturesSchema,
ProxyFeaturesSchema,
@ -21,6 +22,7 @@ import { Context } from 'unleash-client';
import { enrichContextWithIp } from '../../proxy';
import { corsOriginMiddleware } from '../../middleware';
import NotImplementedError from '../../error/not-implemented-error';
import NotFoundError from '../../error/notfound-error';
interface ApiUserRequest<
PARAM = any,
@ -68,7 +70,12 @@ export default class ProxyController extends Controller {
operationId: 'getFrontendFeatures',
responses: {
200: createResponseSchema('proxyFeaturesSchema'),
...getStandardResponses(401, 404),
},
summary:
'Retrieve enabled feature toggles for the provided context.',
description:
'This endpoint returns the list of feature toggles that the proxy evaluates to enabled for the given context. Context values are provided as query parameters. If the Frontend API is disabled 404 is returned.',
}),
],
});
@ -95,9 +102,14 @@ export default class ProxyController extends Controller {
middleware: [
this.services.openApiService.validPath({
tags: ['Frontend API'],
summary: 'Register client usage metrics',
description: `Registers usage metrics. Stores information about how many times each toggle was evaluated to enabled and disabled within a time frame. If provided, this operation will also store data on how many times each feature toggle's variants were displayed to the end user. If the Frontend API is disabled 404 is returned.`,
operationId: 'registerFrontendMetrics',
requestBody: createRequestSchema('clientMetricsSchema'),
responses: { 200: emptyResponse },
responses: {
200: emptyResponse,
...getStandardResponses(400, 401, 404),
},
}),
],
});
@ -105,14 +117,20 @@ export default class ProxyController extends Controller {
this.route({
method: 'post',
path: '/client/register',
handler: ProxyController.registerProxyClient,
handler: this.registerProxyClient,
permission: NONE,
middleware: [
this.services.openApiService.validPath({
tags: ['Frontend API'],
summary: 'Register a client SDK',
description:
'This is for future use. Currently Frontend client registration is not supported. Returning 200 for clients that expect this status code. If the Frontend API is disabled 404 is returned.',
operationId: 'registerFrontendClient',
requestBody: createRequestSchema('proxyClientSchema'),
responses: { 200: emptyResponse },
responses: {
200: emptyResponse,
...getStandardResponses(400, 401, 404),
},
}),
],
});
@ -146,6 +164,9 @@ export default class ProxyController extends Controller {
req: ApiUserRequest,
res: Response<ProxyFeaturesSchema>,
) {
if (!this.config.flagResolver.isEnabled('embedProxy')) {
throw new NotFoundError();
}
const toggles = await this.services.proxyService.getProxyFeatures(
req.user,
ProxyController.createContext(req),
@ -165,6 +186,9 @@ export default class ProxyController extends Controller {
req: ApiUserRequest<unknown, unknown, ClientMetricsSchema>,
res: Response,
) {
if (!this.config.flagResolver.isEnabled('embedProxy')) {
throw new NotFoundError();
}
await this.services.proxyService.registerProxyMetrics(
req.user,
req.body,
@ -173,10 +197,13 @@ export default class ProxyController extends Controller {
res.sendStatus(200);
}
private static async registerProxyClient(
private async registerProxyClient(
req: ApiUserRequest<unknown, unknown, ProxyClientSchema>,
res: Response<string>,
) {
if (!this.config.flagResolver.isEnabled('embedProxy')) {
throw new NotFoundError();
}
// Client registration is not yet supported by @unleash/proxy,
// but proxy clients may still expect a 200 from this endpoint.
res.sendStatus(200);

View File

@ -4509,27 +4509,43 @@ Stats are divided into current and previous **windows**.
"additionalProperties": false,
"properties": {
"enabled": {
"description": "Always set to \`true\`.",
"example": true,
"type": "boolean",
},
"impressionData": {
"description": "\`true\` if the impression data collection is enabled for the feature, otherwise \`false\`.",
"example": false,
"type": "boolean",
},
"name": {
"description": "Unique feature name.",
"example": "disable-comments",
"type": "string",
},
"variant": {
"additionalProperties": false,
"properties": {
"enabled": {
"description": "Whether the variant is enabled or not.",
"example": true,
"type": "boolean",
},
"name": {
"description": "The variants name. Is unique for this feature toggle",
"example": "blue_group",
"type": "string",
},
"payload": {
"additionalProperties": false,
"description": "Extra data configured for this variant",
"example": {
"type": "json",
"value": "{color: red}",
},
"properties": {
"type": {
"description": "The format of the payload.",
"enum": [
"string",
"json",
@ -4538,6 +4554,7 @@ Stats are divided into current and previous **windows**.
"type": "string",
},
"value": {
"description": "The payload value stringified.",
"type": "string",
},
},
@ -14484,6 +14501,296 @@ true,false,"[{""range"":""allTime"",""count"":15},{""range"":""30d"",""count"":9
],
},
},
"/api/frontend": {
"get": {
"description": "This endpoint returns the list of feature toggles that the proxy evaluates to enabled for the given context. Context values are provided as query parameters. If the Frontend API is disabled 404 is returned.",
"operationId": "getFrontendFeatures",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/proxyFeaturesSchema",
},
},
},
"description": "proxyFeaturesSchema",
},
"401": {
"content": {
"application/json": {
"schema": {
"properties": {
"id": {
"description": "The ID of the error instance",
"example": "9c40958a-daac-400e-98fb-3bb438567008",
"type": "string",
},
"message": {
"description": "A description of what went wrong.",
"example": "You must log in to use Unleash. Your request had no authorization header, so we could not authorize you. Try logging in at /auth/simple/login.",
"type": "string",
},
"name": {
"description": "The name of the error kind",
"example": "AuthenticationRequired",
"type": "string",
},
},
"type": "object",
},
},
},
"description": "Authorization information is missing or invalid. Provide a valid API token as the \`authorization\` header, e.g. \`authorization:*.*.my-admin-token\`.",
},
"404": {
"content": {
"application/json": {
"schema": {
"properties": {
"id": {
"description": "The ID of the error instance",
"example": "9c40958a-daac-400e-98fb-3bb438567008",
"type": "string",
},
"message": {
"description": "A description of what went wrong.",
"example": "Could not find the addon with ID "12345".",
"type": "string",
},
"name": {
"description": "The name of the error kind",
"example": "NotFoundError",
"type": "string",
},
},
"type": "object",
},
},
},
"description": "The requested resource was not found.",
},
},
"summary": "Retrieve enabled feature toggles for the provided context.",
"tags": [
"Frontend API",
],
},
},
"/api/frontend/client/metrics": {
"post": {
"description": "Registers usage metrics. Stores information about how many times each toggle was evaluated to enabled and disabled within a time frame. If provided, this operation will also store data on how many times each feature toggle's variants were displayed to the end user. If the Frontend API is disabled 404 is returned.",
"operationId": "registerFrontendMetrics",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/clientMetricsSchema",
},
},
},
"description": "clientMetricsSchema",
"required": true,
},
"responses": {
"200": {
"description": "This response has no body.",
},
"400": {
"content": {
"application/json": {
"schema": {
"properties": {
"id": {
"description": "The ID of the error instance",
"example": "9c40958a-daac-400e-98fb-3bb438567008",
"type": "string",
},
"message": {
"description": "A description of what went wrong.",
"example": "The request payload you provided doesn't conform to the schema. The .parameters property should be object. You sent [].",
"type": "string",
},
"name": {
"description": "The name of the error kind",
"example": "ValidationError",
"type": "string",
},
},
"type": "object",
},
},
},
"description": "The request data does not match what we expect.",
},
"401": {
"content": {
"application/json": {
"schema": {
"properties": {
"id": {
"description": "The ID of the error instance",
"example": "9c40958a-daac-400e-98fb-3bb438567008",
"type": "string",
},
"message": {
"description": "A description of what went wrong.",
"example": "You must log in to use Unleash. Your request had no authorization header, so we could not authorize you. Try logging in at /auth/simple/login.",
"type": "string",
},
"name": {
"description": "The name of the error kind",
"example": "AuthenticationRequired",
"type": "string",
},
},
"type": "object",
},
},
},
"description": "Authorization information is missing or invalid. Provide a valid API token as the \`authorization\` header, e.g. \`authorization:*.*.my-admin-token\`.",
},
"404": {
"content": {
"application/json": {
"schema": {
"properties": {
"id": {
"description": "The ID of the error instance",
"example": "9c40958a-daac-400e-98fb-3bb438567008",
"type": "string",
},
"message": {
"description": "A description of what went wrong.",
"example": "Could not find the addon with ID "12345".",
"type": "string",
},
"name": {
"description": "The name of the error kind",
"example": "NotFoundError",
"type": "string",
},
},
"type": "object",
},
},
},
"description": "The requested resource was not found.",
},
},
"summary": "Register client usage metrics",
"tags": [
"Frontend API",
],
},
},
"/api/frontend/client/register": {
"post": {
"description": "This is for future use. Currently Frontend client registration is not supported. Returning 200 for clients that expect this status code. If the Frontend API is disabled 404 is returned.",
"operationId": "registerFrontendClient",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/proxyClientSchema",
},
},
},
"description": "proxyClientSchema",
"required": true,
},
"responses": {
"200": {
"description": "This response has no body.",
},
"400": {
"content": {
"application/json": {
"schema": {
"properties": {
"id": {
"description": "The ID of the error instance",
"example": "9c40958a-daac-400e-98fb-3bb438567008",
"type": "string",
},
"message": {
"description": "A description of what went wrong.",
"example": "The request payload you provided doesn't conform to the schema. The .parameters property should be object. You sent [].",
"type": "string",
},
"name": {
"description": "The name of the error kind",
"example": "ValidationError",
"type": "string",
},
},
"type": "object",
},
},
},
"description": "The request data does not match what we expect.",
},
"401": {
"content": {
"application/json": {
"schema": {
"properties": {
"id": {
"description": "The ID of the error instance",
"example": "9c40958a-daac-400e-98fb-3bb438567008",
"type": "string",
},
"message": {
"description": "A description of what went wrong.",
"example": "You must log in to use Unleash. Your request had no authorization header, so we could not authorize you. Try logging in at /auth/simple/login.",
"type": "string",
},
"name": {
"description": "The name of the error kind",
"example": "AuthenticationRequired",
"type": "string",
},
},
"type": "object",
},
},
},
"description": "Authorization information is missing or invalid. Provide a valid API token as the \`authorization\` header, e.g. \`authorization:*.*.my-admin-token\`.",
},
"404": {
"content": {
"application/json": {
"schema": {
"properties": {
"id": {
"description": "The ID of the error instance",
"example": "9c40958a-daac-400e-98fb-3bb438567008",
"type": "string",
},
"message": {
"description": "A description of what went wrong.",
"example": "Could not find the addon with ID "12345".",
"type": "string",
},
"name": {
"description": "The name of the error kind",
"example": "NotFoundError",
"type": "string",
},
},
"type": "object",
},
},
},
"description": "The requested resource was not found.",
},
},
"summary": "Register a client SDK",
"tags": [
"Frontend API",
],
},
},
"/auth/reset/password": {
"post": {
"operationId": "changePassword",