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:
parent
f2257eb45b
commit
54d28471f7
7576
coverage/clover.xml
7576
coverage/clover.xml
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
11513
coverage/lcov.info
11513
coverage/lcov.info
File diff suppressed because it is too large
Load Diff
67142
coverage/report.json
67142
coverage/report.json
File diff suppressed because one or more lines are too long
@ -39,10 +39,10 @@ tags:
|
||||
info:
|
||||
title: Unleash API
|
||||
description: |-
|
||||
|
||||
|
||||
> 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)
|
||||
|
||||
|
||||
Unleash is an open source feature flag and toggle system for all your applications and services.
|
||||
|
||||
# Try it out
|
||||
@ -133,9 +133,9 @@ paths:
|
||||
get:
|
||||
summary: Fetches all feature toggles from the Unleash server.
|
||||
description: |-
|
||||
The response returns all active feature toggles and their current strategy configuration:
|
||||
- Feature toggles will have *at least* one strategy
|
||||
- Strategies have a `name` and `parameters` map.
|
||||
The response returns all active feature toggles and their current strategy configuration:
|
||||
- Feature toggles will have *at least* one strategy
|
||||
- Strategies have a `name` and `parameters` map.
|
||||
operationId: getClientFeatures
|
||||
externalDocs:
|
||||
description: Activation strategies
|
||||
@ -598,7 +598,7 @@ paths:
|
||||
'200':
|
||||
$ref: '#/components/responses/featureAccessResponse'
|
||||
'401':
|
||||
$ref: '#/components/responses/notAuthorizedResponse'
|
||||
$ref: '#/components/responses/notAuthorizedResponse'
|
||||
x-code-samples:
|
||||
- lang: 'cURL'
|
||||
source: |
|
||||
@ -843,73 +843,73 @@ components:
|
||||
type: string
|
||||
responses:
|
||||
successResponse:
|
||||
description: "Successful response"
|
||||
description: 'Successful response'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/200'
|
||||
notAuthorizedResponse:
|
||||
description: "Not authorized"
|
||||
description: 'Not authorized'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/401'
|
||||
clientResponse:
|
||||
description: "Successful response"
|
||||
description: 'Successful response'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/200Client'
|
||||
strategyExistsResponse:
|
||||
description: "Strategy already exists"
|
||||
description: 'Strategy already exists'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/409'
|
||||
strategyResponse:
|
||||
description: "Successful response"
|
||||
description: 'Successful response'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/200strategy'
|
||||
seenTogglesResponse:
|
||||
description: "Successful response"
|
||||
description: 'Successful response'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/200seen'
|
||||
featureAccessResponse:
|
||||
description: "Successful response"
|
||||
description: 'Successful response'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/200feature'
|
||||
appDetailsResponse:
|
||||
description: "Successful response"
|
||||
description: 'Successful response'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/200appdetails'
|
||||
applicationsResponse:
|
||||
description: "Successful response"
|
||||
description: 'Successful response'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/applicationArray'
|
||||
eventsResponse:
|
||||
description: "Successful response"
|
||||
description: 'Successful response'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/200-events'
|
||||
exportResponse:
|
||||
description: "Successful response"
|
||||
description: 'Successful response'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/200export'
|
||||
featureTypeResponse:
|
||||
description: "Successful response"
|
||||
description: 'Successful response'
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
@ -1205,7 +1205,7 @@ components:
|
||||
type: string
|
||||
example: '2016-12-09T14:56:36.730Z'
|
||||
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
|
||||
example: '2020-11-14T09:55:23.653Z'
|
||||
description:
|
||||
@ -1222,7 +1222,7 @@ components:
|
||||
description: Deprecated. Do not use
|
||||
type: string
|
||||
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
|
||||
example: comment_bank
|
||||
x-tags:
|
||||
@ -1270,7 +1270,7 @@ components:
|
||||
minLength: 1
|
||||
example: '::ffff:127.0.0.1'
|
||||
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
|
||||
minLength: 1
|
||||
example: '2020-11-14T11:17:24.482Z'
|
||||
|
@ -39,7 +39,6 @@ export default async function getApp(
|
||||
app.disable('x-powered-by');
|
||||
app.set('port', config.server.port);
|
||||
app.locals.baseUriPath = baseUriPath;
|
||||
|
||||
if (config.server.serverMetrics && config.eventBus) {
|
||||
app.use(responseTimeMetrics(config.eventBus));
|
||||
}
|
||||
|
@ -72,8 +72,46 @@ test('removeJsonSchemaProps', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
test('createOpenApiSchema url', () => {
|
||||
expect(createOpenApiSchema('https://example.com').servers[0].url).toEqual(
|
||||
'https://example.com',
|
||||
);
|
||||
describe('createOpenApiSchema', () => {
|
||||
test('createOpenApiSchema url', () => {
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
@ -83,6 +83,8 @@ import { strategySchema } from './spec/strategy-schema';
|
||||
import { strategiesSchema } from './spec/strategies-schema';
|
||||
import { upsertStrategySchema } from './spec/upsert-strategy-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.
|
||||
export const schemas = {
|
||||
@ -226,12 +228,31 @@ export const removeJsonSchemaProps = <T extends JsonSchemaProps>(
|
||||
return omitKeys(schema, '$id', 'components');
|
||||
};
|
||||
|
||||
export const createOpenApiSchema = (
|
||||
serverUrl?: string,
|
||||
): Omit<OpenAPIV3.Document, 'paths'> => {
|
||||
const findRootUrl: (unleashUrl: string, baseUriPath: string) => string = (
|
||||
unleashUrl: string,
|
||||
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 {
|
||||
openapi: '3.0.3',
|
||||
servers: serverUrl ? [{ url: serverUrl }] : [],
|
||||
servers: url ? [{ url }] : [],
|
||||
info: {
|
||||
title: 'Unleash API',
|
||||
version: process.env.npm_package_version!,
|
||||
|
@ -24,7 +24,7 @@ export class OpenApiService {
|
||||
|
||||
this.api = openapi(
|
||||
this.docsPath(),
|
||||
createOpenApiSchema(config.server?.unleashUrl),
|
||||
createOpenApiSchema(config.server),
|
||||
{ coerce: true },
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user