1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

fix: openapi spec should only include base path once (#1755)

* fix: openapi spec should only include base path once
This commit is contained in:
Christopher Kolstad 2022-06-27 15:39:08 +02:00 committed by GitHub
parent f2257eb45b
commit 54d28471f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 139349 additions and 9127 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -39,10 +39,10 @@ tags:
info: info:
title: Unleash API title: Unleash API
description: |- description: |-
> The Open API specifications are currently considered a **"beta feature"** and will not cover the full Unleash Admin API. > The Open API specifications are currently considered a **"beta feature"** and will not cover the full Unleash Admin API.
> You can follow the progress on making OAS official in [GitHub issue 1391](https://github.com/Unleash/unleash/issues/1391) > You can follow the progress on making OAS official in [GitHub issue 1391](https://github.com/Unleash/unleash/issues/1391)
Unleash is an open source feature flag and toggle system for all your applications and services. Unleash is an open source feature flag and toggle system for all your applications and services.
# Try it out # Try it out
@ -133,9 +133,9 @@ paths:
get: get:
summary: Fetches all feature toggles from the Unleash server. summary: Fetches all feature toggles from the Unleash server.
description: |- description: |-
The response returns all active feature toggles and their current strategy configuration: The response returns all active feature toggles and their current strategy configuration:
- Feature toggles will have *at least* one strategy - Feature toggles will have *at least* one strategy
- Strategies have a `name` and `parameters` map. - Strategies have a `name` and `parameters` map.
operationId: getClientFeatures operationId: getClientFeatures
externalDocs: externalDocs:
description: Activation strategies description: Activation strategies
@ -598,7 +598,7 @@ paths:
'200': '200':
$ref: '#/components/responses/featureAccessResponse' $ref: '#/components/responses/featureAccessResponse'
'401': '401':
$ref: '#/components/responses/notAuthorizedResponse' $ref: '#/components/responses/notAuthorizedResponse'
x-code-samples: x-code-samples:
- lang: 'cURL' - lang: 'cURL'
source: | source: |
@ -843,73 +843,73 @@ components:
type: string type: string
responses: responses:
successResponse: successResponse:
description: "Successful response" description: 'Successful response'
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/200' $ref: '#/components/schemas/200'
notAuthorizedResponse: notAuthorizedResponse:
description: "Not authorized" description: 'Not authorized'
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/401' $ref: '#/components/schemas/401'
clientResponse: clientResponse:
description: "Successful response" description: 'Successful response'
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/200Client' $ref: '#/components/schemas/200Client'
strategyExistsResponse: strategyExistsResponse:
description: "Strategy already exists" description: 'Strategy already exists'
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/409' $ref: '#/components/schemas/409'
strategyResponse: strategyResponse:
description: "Successful response" description: 'Successful response'
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/200strategy' $ref: '#/components/schemas/200strategy'
seenTogglesResponse: seenTogglesResponse:
description: "Successful response" description: 'Successful response'
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/200seen' $ref: '#/components/schemas/200seen'
featureAccessResponse: featureAccessResponse:
description: "Successful response" description: 'Successful response'
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/200feature' $ref: '#/components/schemas/200feature'
appDetailsResponse: appDetailsResponse:
description: "Successful response" description: 'Successful response'
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/200appdetails' $ref: '#/components/schemas/200appdetails'
applicationsResponse: applicationsResponse:
description: "Successful response" description: 'Successful response'
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/applicationArray' $ref: '#/components/schemas/applicationArray'
eventsResponse: eventsResponse:
description: "Successful response" description: 'Successful response'
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/200-events' $ref: '#/components/schemas/200-events'
exportResponse: exportResponse:
description: "Successful response" description: 'Successful response'
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/200export' $ref: '#/components/schemas/200export'
featureTypeResponse: featureTypeResponse:
description: "Successful response" description: 'Successful response'
content: content:
application/json: application/json:
schema: schema:
@ -1205,7 +1205,7 @@ components:
type: string type: string
example: '2016-12-09T14:56:36.730Z' example: '2016-12-09T14:56:36.730Z'
updatedAt: updatedAt:
description: 'The last time the application ''talked'' to Unleash (sent metrics, registered, fetched feature toggles) - in [ISO8601 format](https://www.w3.org/TR/NOTE-datetime)' description: "The last time the application 'talked' to Unleash (sent metrics, registered, fetched feature toggles) - in [ISO8601 format](https://www.w3.org/TR/NOTE-datetime)"
type: string type: string
example: '2020-11-14T09:55:23.653Z' example: '2020-11-14T09:55:23.653Z'
description: description:
@ -1222,7 +1222,7 @@ components:
description: Deprecated. Do not use description: Deprecated. Do not use
type: string type: string
icon: icon:
description: 'The application''s icon. Must be one of the [Material Design icon names](https://material.io/resources/icons/?style=baseline)' description: "The application's icon. Must be one of the [Material Design icon names](https://material.io/resources/icons/?style=baseline)"
type: string type: string
example: comment_bank example: comment_bank
x-tags: x-tags:
@ -1270,7 +1270,7 @@ components:
minLength: 1 minLength: 1
example: '::ffff:127.0.0.1' example: '::ffff:127.0.0.1'
lastSeen: lastSeen:
description: 'The last time the application ''talked'' to Unleash (sent metrics, registered, fetched feature toggles) - in [ISO8601 format](https://www.w3.org/TR/NOTE-datetime)' description: "The last time the application 'talked' to Unleash (sent metrics, registered, fetched feature toggles) - in [ISO8601 format](https://www.w3.org/TR/NOTE-datetime)"
type: string type: string
minLength: 1 minLength: 1
example: '2020-11-14T11:17:24.482Z' example: '2020-11-14T11:17:24.482Z'

View File

@ -39,7 +39,6 @@ export default async function getApp(
app.disable('x-powered-by'); app.disable('x-powered-by');
app.set('port', config.server.port); app.set('port', config.server.port);
app.locals.baseUriPath = baseUriPath; app.locals.baseUriPath = baseUriPath;
if (config.server.serverMetrics && config.eventBus) { if (config.server.serverMetrics && config.eventBus) {
app.use(responseTimeMetrics(config.eventBus)); app.use(responseTimeMetrics(config.eventBus));
} }

View File

@ -72,8 +72,46 @@ test('removeJsonSchemaProps', () => {
`); `);
}); });
test('createOpenApiSchema url', () => { describe('createOpenApiSchema', () => {
expect(createOpenApiSchema('https://example.com').servers[0].url).toEqual( test('createOpenApiSchema url', () => {
'https://example.com', expect(
); createOpenApiSchema({
unleashUrl: 'https://example.com',
baseUriPath: '',
}).servers[0].url,
).toEqual('https://example.com');
});
test('if baseurl is set strips from serverUrl', () => {
expect(
createOpenApiSchema({
unleashUrl: 'https://example.com/demo2',
baseUriPath: '/demo2',
}).servers[0].url,
).toEqual('https://example.com');
});
test('if baseurl does not end with suffix, cowardly refuses to strip', () => {
expect(
createOpenApiSchema({
unleashUrl: 'https://example.com/demo2',
baseUriPath: 'example',
}).servers[0].url,
).toEqual('https://example.com/demo2');
});
test('avoids double trailing slash', () => {
expect(
createOpenApiSchema({
unleashUrl: 'https://example.com/example/',
baseUriPath: 'example',
}).servers[0].url,
).toEqual('https://example.com');
expect(
createOpenApiSchema({
unleashUrl: 'https://example.com/example/',
baseUriPath: '/example',
}).servers[0].url,
).toEqual('https://example.com');
});
}); });

View File

@ -83,6 +83,8 @@ import { strategySchema } from './spec/strategy-schema';
import { strategiesSchema } from './spec/strategies-schema'; import { strategiesSchema } from './spec/strategies-schema';
import { upsertStrategySchema } from './spec/upsert-strategy-schema'; import { upsertStrategySchema } from './spec/upsert-strategy-schema';
import { clientApplicationSchema } from './spec/client-application-schema'; import { clientApplicationSchema } from './spec/client-application-schema';
import { IServerOption } from '../types';
import { URL } from 'url';
// All schemas in `openapi/spec` should be listed here. // All schemas in `openapi/spec` should be listed here.
export const schemas = { export const schemas = {
@ -226,12 +228,31 @@ export const removeJsonSchemaProps = <T extends JsonSchemaProps>(
return omitKeys(schema, '$id', 'components'); return omitKeys(schema, '$id', 'components');
}; };
export const createOpenApiSchema = ( const findRootUrl: (unleashUrl: string, baseUriPath: string) => string = (
serverUrl?: string, unleashUrl: string,
): Omit<OpenAPIV3.Document, 'paths'> => { baseUriPath?: string,
) => {
if (!baseUriPath) {
return unleashUrl;
}
const baseUrl = new URL(unleashUrl);
if (baseUrl.pathname.indexOf(baseUriPath) >= 0) {
return `${baseUrl.protocol}//${baseUrl.host}`;
}
return baseUrl.toString();
};
export const createOpenApiSchema = ({
unleashUrl,
baseUriPath,
}: Pick<IServerOption, 'unleashUrl' | 'baseUriPath'>): Omit<
OpenAPIV3.Document,
'paths'
> => {
const url = findRootUrl(unleashUrl, baseUriPath);
return { return {
openapi: '3.0.3', openapi: '3.0.3',
servers: serverUrl ? [{ url: serverUrl }] : [], servers: url ? [{ url }] : [],
info: { info: {
title: 'Unleash API', title: 'Unleash API',
version: process.env.npm_package_version!, version: process.env.npm_package_version!,

View File

@ -24,7 +24,7 @@ export class OpenApiService {
this.api = openapi( this.api = openapi(
this.docsPath(), this.docsPath(),
createOpenApiSchema(config.server?.unleashUrl), createOpenApiSchema(config.server),
{ coerce: true }, { coerce: true },
); );
} }