mirror of
https://github.com/Kong/insomnia
synced 2024-11-07 22:30:15 +00:00
fix: adding a fix to the openapi2kong circular deps issue (#4872)
* commit progress * save changes * add components and $schema * add components and $schema * adding a doc * add typings * fix unit tests * fix unit tests * add fix and verified with kong cli * fix unit tests * Improve types and parameter/body resolution Co-authored-by: Mark Kim <marckong@users.noreply.github.com> * fix circular "components" issue Co-authored-by: gatzjames <jamesgatzos@gmail.com> Co-authored-by: Mark Kim <marckong@users.noreply.github.com>
This commit is contained in:
parent
8300652981
commit
4f5ef73f7e
1685
packages/openapi-2-kong/package-lock.json
generated
1685
packages/openapi-2-kong/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,5 @@
|
||||
import { describe, expect, it } from '@jest/globals';
|
||||
import { OpenAPIV3 } from 'openapi-types';
|
||||
|
||||
import {
|
||||
distinctByProperty,
|
||||
@ -120,12 +121,12 @@ describe('common', () => {
|
||||
type: 'http',
|
||||
scheme: 'basic',
|
||||
name: 'name',
|
||||
},
|
||||
} as OpenAPIV3.HttpSecurityScheme,
|
||||
anotherAuth: {
|
||||
type: 'http',
|
||||
scheme: 'basic',
|
||||
name: 'another-name',
|
||||
},
|
||||
} as OpenAPIV3.HttpSecurityScheme,
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -155,6 +156,7 @@ describe('common', () => {
|
||||
const spec = getSpec({
|
||||
info: {
|
||||
version: '1.0.0',
|
||||
title: '',
|
||||
},
|
||||
});
|
||||
const result = getName(spec);
|
||||
|
@ -41,7 +41,7 @@
|
||||
"name": "request-validator",
|
||||
"config": {
|
||||
"version": "draft4",
|
||||
"body_schema": "{\"type\":\"object\",\"title\":\"request-body-nullable\",\"description\":\"This object is sent in post application request.\",\"required\":[\"redirectUri\"],\"properties\":{\"redirectUri\":{\"type\":[\"string\",\"null\"],\"nullable\":true}}}",
|
||||
"body_schema": "{\"type\":\"object\",\"title\":\"request-body-nullable\",\"description\":\"This object is sent in post application request.\",\"required\":[\"redirectUri\"],\"properties\":{\"redirectUri\":{\"type\":[\"string\",\"null\"],\"nullable\":true}},\"components\":{\"schemas\":{\"request-body-nullable\":{\"type\":\"object\",\"title\":\"request-body-nullable\",\"description\":\"This object is sent in post application request.\",\"required\":[\"redirectUri\"],\"properties\":{\"redirectUri\":{\"type\":[\"string\",\"null\"],\"nullable\":true}}}}},\"$schema\":\"http://json-schema.org/schema\"}",
|
||||
"allowed_content_types": [
|
||||
"application/json"
|
||||
],
|
||||
|
@ -29,7 +29,7 @@
|
||||
"application/json",
|
||||
"application/xml"
|
||||
],
|
||||
"body_schema": "{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\"},\"name\":{\"type\":\"string\"}}}",
|
||||
"body_schema": "{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\"},\"name\":{\"type\":\"string\"}},\"components\":{\"schemas\":{\"jsonSchema\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\"},\"name\":{\"type\":\"string\"}}},\"xmlSchema\":{\"type\":\"object\",\"properties\":{\"prop\":{\"type\":\"integer\"}}}}},\"$schema\":\"http://json-schema.org/schema\"}",
|
||||
"version": "draft4"
|
||||
},
|
||||
"tags": ["OAS3_import", "OAS3file_request-validator-plugin.yaml"],
|
||||
@ -47,7 +47,7 @@
|
||||
{
|
||||
"config": {
|
||||
"allowed_content_types": ["application/json"],
|
||||
"body_schema": "{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\"},\"name\":{\"type\":\"string\"}}}",
|
||||
"body_schema": "{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\"},\"name\":{\"type\":\"string\"}},\"components\":{\"schemas\":{\"jsonSchema\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\"},\"name\":{\"type\":\"string\"}}},\"xmlSchema\":{\"type\":\"object\",\"properties\":{\"prop\":{\"type\":\"integer\"}}}}},\"$schema\":\"http://json-schema.org/schema\"}",
|
||||
"version": "draft4"
|
||||
},
|
||||
"tags": ["OAS3_import", "OAS3file_request-validator-plugin.yaml"],
|
||||
|
@ -5,14 +5,14 @@ import { DeclarativeConfigResult } from '../types/outputs';
|
||||
import { generateServices } from './services';
|
||||
import { generateUpstreams } from './upstreams';
|
||||
|
||||
export function generateDeclarativeConfigFromSpec(
|
||||
export async function generateDeclarativeConfigFromSpec(
|
||||
api: OpenApi3Spec,
|
||||
tags: string[],
|
||||
) {
|
||||
try {
|
||||
const document: DeclarativeConfig = {
|
||||
_format_version: '1.1',
|
||||
services: generateServices(api, tags),
|
||||
services: await generateServices(api, tags),
|
||||
};
|
||||
|
||||
if (hasUpstreams(api)) {
|
||||
@ -26,9 +26,16 @@ export function generateDeclarativeConfigFromSpec(
|
||||
warnings: [],
|
||||
};
|
||||
|
||||
// This removes any circular references or weirdness that might result from the JS objects used.
|
||||
// see: https://github.com/Kong/studio/issues/93
|
||||
const result: DeclarativeConfigResult = JSON.parse(JSON.stringify(declarativeConfigResult));
|
||||
/**
|
||||
* There was an [issue](https://github.com/Kong/studio/issues/93) that required us to stringify and parse the declarative config object containing circular dependencies.
|
||||
* However, that fix didn't seem to clear the issue of the circular dependencies completely.
|
||||
*
|
||||
* It is attempted to resolve the circular issue by bundling the openapi spec using SwaggerParser.bundle() method, which resolves all the schemas into $ref instead of dereferencing them.
|
||||
* Then, we would just dump the components part of it with $schema property, so any JSON parsing logic can refer to the components object.
|
||||
*
|
||||
* Therefore, JSON.parse(JSON.stringify(result)) doesn't seem to be needed any more.
|
||||
*/
|
||||
const result: DeclarativeConfigResult = declarativeConfigResult;
|
||||
return result;
|
||||
} catch (err) {
|
||||
throw new Error('Failed to generate spec: ' + err.message);
|
||||
|
@ -58,7 +58,7 @@ export type UserXKongPlugin = XKongPlugin<Plugin> | XKongPlugin<DummyPlugin>;
|
||||
export const getSpec = (overrides: Partial<OpenApi3Spec> = {}): OpenApi3Spec =>
|
||||
JSON.parse(
|
||||
JSON.stringify({
|
||||
openapi: '3.0',
|
||||
openapi: '3.0.0',
|
||||
info: {
|
||||
version: '1.0',
|
||||
title: 'My API',
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { describe, expect, it } from '@jest/globals';
|
||||
import { OpenAPIV3 } from 'openapi-types';
|
||||
|
||||
import { ParameterSchema, RequestTerminationPlugin, RequestValidatorPlugin, xKongPluginKeyAuth, xKongPluginRequestTermination, xKongPluginRequestValidator } from '../types/kong';
|
||||
import { OA3Operation, OA3Parameter } from '../types/openapi3';
|
||||
@ -25,7 +26,7 @@ describe('plugins', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const result = generateGlobalPlugins(api, tags);
|
||||
const result = await generateGlobalPlugins(api, tags);
|
||||
|
||||
expect(result.plugins).toEqual([
|
||||
{
|
||||
@ -63,7 +64,7 @@ describe('plugins', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('does not add extra things to the plugin', () => {
|
||||
it('does not add extra things to the plugin', async () => {
|
||||
const spec = getSpec({
|
||||
[xKongPluginRequestTermination]: {
|
||||
name: 'request-termination',
|
||||
@ -77,7 +78,7 @@ describe('plugins', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const result = generateGlobalPlugins(spec, tags);
|
||||
const result = await generateGlobalPlugins(spec, tags);
|
||||
|
||||
expect(result.plugins as RequestTerminationPlugin[]).toEqual([
|
||||
{
|
||||
@ -95,6 +96,24 @@ describe('plugins', () => {
|
||||
});
|
||||
|
||||
describe('generateRequestValidatorPlugin()', () => {
|
||||
const api = getSpec({
|
||||
[xKongPluginRequestValidator]: {
|
||||
name: 'request-validator',
|
||||
enabled: false,
|
||||
config: {
|
||||
body_schema: ALLOW_ALL_SCHEMA,
|
||||
verbose_response: true,
|
||||
},
|
||||
},
|
||||
...pluginDummy,
|
||||
[xKongPluginKeyAuth]: {
|
||||
name: 'key-auth',
|
||||
config: {
|
||||
key_names: ['x-api-key'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const parameterSchema: ParameterSchema = {
|
||||
explode: false,
|
||||
in: 'path',
|
||||
@ -104,7 +123,7 @@ describe('plugins', () => {
|
||||
style: 'form',
|
||||
};
|
||||
|
||||
it('should retain config properties', () => {
|
||||
it('should retain config properties', async () => {
|
||||
const plugin: RequestValidatorPlugin = {
|
||||
name: 'request-validator',
|
||||
enabled: true,
|
||||
@ -115,7 +134,7 @@ describe('plugins', () => {
|
||||
allowed_content_types: ['application/json'],
|
||||
},
|
||||
};
|
||||
const generated = generateRequestValidatorPlugin({ plugin, tags });
|
||||
const generated = await generateRequestValidatorPlugin({ plugin, tags, api });
|
||||
expect(generated).toStrictEqual({
|
||||
name: 'request-validator',
|
||||
enabled: plugin.enabled,
|
||||
@ -128,7 +147,7 @@ describe('plugins', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should not add config properties if they are not defined', () => {
|
||||
it('should not add config properties if they are not defined', async () => {
|
||||
const plugin: RequestValidatorPlugin = {
|
||||
name: 'request-validator',
|
||||
enabled: true,
|
||||
@ -140,7 +159,7 @@ describe('plugins', () => {
|
||||
// allowed_content_types: ['application/json'],
|
||||
},
|
||||
};
|
||||
const generated = generateRequestValidatorPlugin({ plugin, tags });
|
||||
const generated = await generateRequestValidatorPlugin({ plugin, tags, api });
|
||||
expect(generated).toStrictEqual({
|
||||
name: 'request-validator',
|
||||
enabled: plugin.enabled,
|
||||
@ -154,7 +173,7 @@ describe('plugins', () => {
|
||||
});
|
||||
|
||||
describe('parameter_schema', () => {
|
||||
it('should not add parameter_schema if no parameters present', () => {
|
||||
it('should not add parameter_schema if no parameters present', async () => {
|
||||
const plugin: RequestValidatorPlugin = {
|
||||
name: 'request-validator',
|
||||
config: {
|
||||
@ -162,18 +181,19 @@ describe('plugins', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const generated1 = generateRequestValidatorPlugin({ plugin, tags });
|
||||
const generated1 = await generateRequestValidatorPlugin({ plugin, tags, api });
|
||||
expect(generated1.config).toStrictEqual({
|
||||
version: 'draft4',
|
||||
body_schema: ALLOW_ALL_SCHEMA,
|
||||
});
|
||||
|
||||
const generated2 = generateRequestValidatorPlugin({
|
||||
const generated2 = await generateRequestValidatorPlugin({
|
||||
plugin,
|
||||
operation: {
|
||||
parameters: [],
|
||||
},
|
||||
tags,
|
||||
api,
|
||||
});
|
||||
expect(generated2.config).toStrictEqual({
|
||||
version: 'draft4',
|
||||
@ -181,7 +201,7 @@ describe('plugins', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should convert operation parameters to parameter_schema', () => {
|
||||
it('should convert operation parameters to parameter_schema', async () => {
|
||||
const param: OA3Parameter = {
|
||||
in: 'query',
|
||||
explode: true,
|
||||
@ -199,7 +219,7 @@ describe('plugins', () => {
|
||||
const operation: OA3Operation = {
|
||||
parameters: [param],
|
||||
};
|
||||
const generated = generateRequestValidatorPlugin({ tags, operation });
|
||||
const generated = await generateRequestValidatorPlugin({ tags, operation, api });
|
||||
expect(generated.config).toStrictEqual({
|
||||
version: 'draft4',
|
||||
parameter_schema: [
|
||||
@ -215,7 +235,7 @@ describe('plugins', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should return default if operation parameter schema not defined on any parameters', () => {
|
||||
it('should return default if operation parameter schema not defined on any parameters', async () => {
|
||||
const operation: OA3Operation = {
|
||||
parameters: [
|
||||
{
|
||||
@ -224,7 +244,7 @@ describe('plugins', () => {
|
||||
},
|
||||
],
|
||||
};
|
||||
const generated = generateRequestValidatorPlugin({ tags, operation });
|
||||
const generated = await generateRequestValidatorPlugin({ tags, operation, api });
|
||||
expect(generated.config).toStrictEqual({
|
||||
version: 'draft4',
|
||||
parameter_schema: [
|
||||
@ -240,7 +260,7 @@ describe('plugins', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore parameters without schema', () => {
|
||||
it('should ignore parameters without schema', async () => {
|
||||
const paramWithSchema: OA3Parameter = {
|
||||
in: 'query',
|
||||
explode: true,
|
||||
@ -265,7 +285,7 @@ describe('plugins', () => {
|
||||
paramWithoutSchema,
|
||||
],
|
||||
};
|
||||
const generated = generateRequestValidatorPlugin({ tags, operation });
|
||||
const generated = await generateRequestValidatorPlugin({ tags, operation, api });
|
||||
expect(generated.config).toStrictEqual({
|
||||
version: 'draft4',
|
||||
parameter_schema: [
|
||||
@ -291,45 +311,44 @@ describe('plugins', () => {
|
||||
});
|
||||
|
||||
describe('body_schema and allowed_content_types', () => {
|
||||
it('should not add body_schema or allowed_content_types if no body present', () => {
|
||||
it('should not add body_schema or allowed_content_types if no body present', async () => {
|
||||
const plugin: RequestValidatorPlugin = {
|
||||
name: 'request-validator',
|
||||
config: {
|
||||
body_schema: ALLOW_ALL_SCHEMA,
|
||||
},
|
||||
};
|
||||
const generated = generateRequestValidatorPlugin({ plugin, tags });
|
||||
const generated = await generateRequestValidatorPlugin({ plugin, tags, api });
|
||||
expect(generated.config).toStrictEqual({
|
||||
version: 'draft4',
|
||||
body_schema: ALLOW_ALL_SCHEMA,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return default if no operation request body content defined', () => {
|
||||
it('should return default if no operation request body content defined', async () => {
|
||||
const defaultReqVal: RequestValidatorPlugin['config'] = {
|
||||
version: 'draft4',
|
||||
body_schema: ALLOW_ALL_SCHEMA,
|
||||
};
|
||||
const op1: OA3Operation = {
|
||||
requestBody: {},
|
||||
};
|
||||
const op2: OA3Operation = {
|
||||
const op1: OpenAPIV3.OperationObject = {};
|
||||
const op2: OpenAPIV3.OperationObject = {
|
||||
requestBody: {
|
||||
$ref: 'non-existent',
|
||||
$ref: '#/components/non-existent',
|
||||
},
|
||||
};
|
||||
expect(generateRequestValidatorPlugin({ operation: op1, tags }).config).toStrictEqual(
|
||||
const apiWithNoComponents = { ...api, components: {} };
|
||||
expect((await generateRequestValidatorPlugin({ operation: op1, tags, api: apiWithNoComponents })).config).toStrictEqual(
|
||||
defaultReqVal,
|
||||
);
|
||||
expect(generateRequestValidatorPlugin({ operation: op2, tags }).config).toStrictEqual(
|
||||
expect((await generateRequestValidatorPlugin({ operation: op2, tags, api: apiWithNoComponents })).config).toStrictEqual(
|
||||
defaultReqVal,
|
||||
);
|
||||
expect(generateRequestValidatorPlugin({ tags }).config).toStrictEqual(
|
||||
expect((await generateRequestValidatorPlugin({ tags, api: apiWithNoComponents })).config).toStrictEqual(
|
||||
defaultReqVal,
|
||||
);
|
||||
});
|
||||
|
||||
it('should add non-json media types to allowed content types and not add body schema', () => {
|
||||
it('should add non-json media types to allowed content types and not add body schema', async () => {
|
||||
const operation: OA3Operation = {
|
||||
requestBody: {
|
||||
content: {
|
||||
@ -338,7 +357,7 @@ describe('plugins', () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
const generated = generateRequestValidatorPlugin({ tags, operation });
|
||||
const generated = await generateRequestValidatorPlugin({ tags, operation, api });
|
||||
expect(generated.config).toStrictEqual({
|
||||
version: 'draft4',
|
||||
body_schema: ALLOW_ALL_SCHEMA,
|
||||
@ -346,7 +365,7 @@ describe('plugins', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should add body_schema and allowed content types', () => {
|
||||
it('should add body_schema and allowed content types', async () => {
|
||||
const schemaXml = {
|
||||
type: 'Object',
|
||||
properties: {
|
||||
@ -365,19 +384,19 @@ describe('plugins', () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
const operation: OA3Operation = {
|
||||
const operation: OpenAPIV3.OperationObject = {
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/xml': {
|
||||
schema: schemaXml,
|
||||
},
|
||||
} as OpenAPIV3.SchemaObject,
|
||||
'application/json': {
|
||||
schema: schemaJson,
|
||||
},
|
||||
} as OpenAPIV3.SchemaObject,
|
||||
},
|
||||
},
|
||||
};
|
||||
const generated = generateRequestValidatorPlugin({ tags, operation });
|
||||
const generated = await generateRequestValidatorPlugin({ tags, operation, api });
|
||||
expect(generated.config).toStrictEqual({
|
||||
version: 'draft4',
|
||||
body_schema: JSON.stringify(schemaJson),
|
||||
@ -385,8 +404,8 @@ describe('plugins', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should default body_schema if no schema is defined or generated', () => {
|
||||
const generated = generateRequestValidatorPlugin({ tags, operation: {} });
|
||||
it('should default body_schema if no schema is defined or generated', async () => {
|
||||
const generated = await generateRequestValidatorPlugin({ tags, operation: {}, api });
|
||||
expect(generated.config).toStrictEqual({
|
||||
version: 'draft4',
|
||||
body_schema: ALLOW_ALL_SCHEMA,
|
||||
@ -394,8 +413,8 @@ describe('plugins', () => {
|
||||
});
|
||||
});
|
||||
describe('body_schema generateBodyOptions FTI-3278', () => {
|
||||
it('should keep to properties[].type unchanged nullable is not set', () => {
|
||||
const generated = generateBodyOptions({
|
||||
it('should keep to properties[].type unchanged nullable is not set', async () => {
|
||||
const generated = await generateBodyOptions(api, {
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': {
|
||||
@ -412,8 +431,8 @@ describe('plugins', () => {
|
||||
});
|
||||
expect(generated.bodySchema).toStrictEqual('{\"properties\":{\"redirectUri\":{\"type\":\"string\"}}}');
|
||||
});
|
||||
it('should keep to properties[].type unchanged nullable is false', () => {
|
||||
const generated = generateBodyOptions({
|
||||
it('should keep to properties[].type unchanged nullable is false', async () => {
|
||||
const generated = await generateBodyOptions(api, {
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': {
|
||||
@ -431,8 +450,8 @@ describe('plugins', () => {
|
||||
});
|
||||
expect(generated.bodySchema).toStrictEqual('{\"properties\":{\"redirectUri\":{\"type\":\"string\",\"nullable\":false}}}');
|
||||
});
|
||||
it('should append null to properties[].type if nullable is true', () => {
|
||||
const generated = generateBodyOptions({
|
||||
it('should append null to properties[].type if nullable is true', async () => {
|
||||
const generated = await generateBodyOptions(api, {
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': {
|
||||
|
@ -1,9 +1,11 @@
|
||||
import SwaggerParser from '@apidevtools/swagger-parser';
|
||||
import { OpenAPIV3 } from 'openapi-types';
|
||||
import { Entry } from 'type-fest';
|
||||
|
||||
import { distinctByProperty, getPluginNameFromKey, isPluginKey } from '../common';
|
||||
import { DCPlugin } from '../types/declarative-config';
|
||||
import { isBodySchema, isParameterSchema, ParameterSchema, RequestValidatorPlugin, XKongPluginRequestValidator, xKongPluginRequestValidator } from '../types/kong';
|
||||
import { OA3Operation, OA3Parameter, OA3RequestBody, OpenApi3Spec } from '../types/openapi3';
|
||||
import type { OA3Operation, OpenApi3Spec } from '../types/openapi3';
|
||||
|
||||
export const isRequestValidatorPluginKey = (property: string): property is typeof xKongPluginRequestValidator => (
|
||||
property.match(/-request-validator$/) != null
|
||||
@ -36,7 +38,7 @@ const generatePlugin = (tags: string[]) => ([key, value]: Entry<PluginItem>): DC
|
||||
* See: https://github.com/Kong/kong-plugin-enterprise-request-validator/pull/34/files#diff-1a1d2d5ce801cc1cfb2aa91ae15686d81ef900af1dbef00f004677bc727bfd3cR284
|
||||
*/
|
||||
export const ALLOW_ALL_SCHEMA = '{}';
|
||||
|
||||
const $schema = 'http://json-schema.org/schema#';
|
||||
const DEFAULT_PARAM_STYLE = {
|
||||
header: 'simple',
|
||||
cookie: 'form',
|
||||
@ -44,19 +46,79 @@ const DEFAULT_PARAM_STYLE = {
|
||||
path: 'simple',
|
||||
};
|
||||
|
||||
const generateParameterSchema = (operation?: OA3Operation) => {
|
||||
if (!operation?.parameters?.length) {
|
||||
return undefined;
|
||||
interface ResolvedParameter {
|
||||
resolvedParam: OpenAPIV3.ParameterObject;
|
||||
components: OpenAPIV3.ComponentsObject | undefined;
|
||||
}
|
||||
|
||||
const resolveParameter = ($refs: SwaggerParser.$Refs, parameter: OpenAPIV3.ParameterObject | OpenAPIV3.ReferenceObject): ResolvedParameter => {
|
||||
if ('$ref' in parameter) {
|
||||
const components = getOperationRef<OpenAPIV3.ComponentsObject>($refs, '#/components');
|
||||
const dereferenced = getOperationRef<OpenAPIV3.ParameterObject>($refs, parameter.$ref);
|
||||
const { $ref, ...param } = parameter;
|
||||
|
||||
let schema: OpenAPIV3.ParameterObject['schema'] = dereferenced?.schema;
|
||||
if (schema && '$ref' in schema) {
|
||||
schema = getOperationRef<OpenAPIV3.ParameterObject['schema']>($refs, schema.$ref);
|
||||
}
|
||||
|
||||
const resolvedParam: OpenAPIV3.ParameterObject = {
|
||||
...param,
|
||||
...dereferenced,
|
||||
name: dereferenced?.name || '',
|
||||
in: dereferenced?.in || '',
|
||||
schema,
|
||||
};
|
||||
|
||||
return {
|
||||
resolvedParam,
|
||||
components,
|
||||
};
|
||||
}
|
||||
|
||||
if (parameter.schema && '$ref' in parameter.schema) {
|
||||
const components = getOperationRef<OpenAPIV3.ComponentsObject>($refs, '#/components');
|
||||
const schema = getOperationRef<OpenAPIV3.ParameterObject['schema']>($refs, parameter.schema.$ref);
|
||||
|
||||
return {
|
||||
resolvedParam: {
|
||||
...parameter,
|
||||
schema,
|
||||
},
|
||||
components,
|
||||
};
|
||||
}
|
||||
|
||||
return { resolvedParam: parameter, components: undefined };
|
||||
};
|
||||
|
||||
type KongSchema = (OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject) & {
|
||||
components?: OpenAPIV3.ComponentsObject;
|
||||
$schema?: string;
|
||||
};
|
||||
|
||||
const generateParameterSchema = async (api: OpenApi3Spec, operation?: OA3Operation) => {
|
||||
if (!operation?.parameters?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const refs: SwaggerParser.$Refs = await SwaggerParser.resolve(api);
|
||||
const parameterSchemas: ParameterSchema[] = [];
|
||||
for (const parameter of operation.parameters as OA3Parameter[]) {
|
||||
for (const parameter of operation.parameters) {
|
||||
// The following is valid config to allow all content to pass, in the case where schema is not defined
|
||||
let schema = '';
|
||||
|
||||
if (parameter.schema) {
|
||||
schema = JSON.stringify(parameter.schema);
|
||||
} else if (parameter.content) {
|
||||
const { resolvedParam, components } = resolveParameter(refs, parameter);
|
||||
|
||||
if (resolvedParam.schema) {
|
||||
const kongSchema: KongSchema = { ...resolvedParam.schema };
|
||||
// The $schema property should only exist if components exist with a $ref path
|
||||
if (components) {
|
||||
kongSchema.components = components;
|
||||
kongSchema.$schema = $schema;
|
||||
}
|
||||
schema = JSON.stringify(kongSchema);
|
||||
} else if ('content' in parameter) {
|
||||
// only parameters defined with a schema (not content) are supported
|
||||
schema = ALLOW_ALL_SCHEMA;
|
||||
} else {
|
||||
@ -64,18 +126,19 @@ const generateParameterSchema = (operation?: OA3Operation) => {
|
||||
schema = ALLOW_ALL_SCHEMA;
|
||||
}
|
||||
|
||||
const paramStyle = parameter.style ?? DEFAULT_PARAM_STYLE[parameter.in];
|
||||
// @ts-expect-error fix this
|
||||
const paramStyle = (parameter as OpenAPIV3.ParameterObject).style ?? DEFAULT_PARAM_STYLE[resolvedParam.in];
|
||||
|
||||
if (typeof paramStyle === 'undefined') {
|
||||
const name = parameter.name;
|
||||
const name = resolvedParam.name;
|
||||
throw new Error(`invalid 'in' property (parameter '${name}')`);
|
||||
}
|
||||
|
||||
const parameterSchema: ParameterSchema = {
|
||||
in: parameter.in,
|
||||
explode: !!parameter.explode,
|
||||
required: !!parameter.required,
|
||||
name: parameter.name,
|
||||
in: resolvedParam.in,
|
||||
explode: !!resolvedParam.explode,
|
||||
required: !!resolvedParam.required,
|
||||
name: resolvedParam.name,
|
||||
schema,
|
||||
style: paramStyle,
|
||||
};
|
||||
@ -85,25 +148,73 @@ const generateParameterSchema = (operation?: OA3Operation) => {
|
||||
return parameterSchemas;
|
||||
};
|
||||
|
||||
export function generateBodyOptions(operation?: OA3Operation) {
|
||||
function resolveRequestBodyContent($refs: SwaggerParser.$Refs, operation?: OA3Operation): OpenAPIV3.RequestBodyObject | undefined {
|
||||
if (!operation || !operation?.requestBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ('$ref' in operation.requestBody) {
|
||||
return getOperationRef($refs, operation.requestBody.$ref);
|
||||
}
|
||||
|
||||
return operation.requestBody;
|
||||
}
|
||||
|
||||
function getOperationRef<RefType = OpenAPIV3.RequestBodyObject>($refs: SwaggerParser.$Refs, refPath: OpenAPIV3.ReferenceObject['$ref']): RefType | undefined {
|
||||
if ($refs.exists(refPath)) {
|
||||
return $refs.get(refPath);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function resolveItemSchema($refs: SwaggerParser.$Refs, item: OpenAPIV3.MediaTypeObject): OpenAPIV3.SchemaObject {
|
||||
if (item.schema && '$ref' in item.schema) {
|
||||
const resolved: OpenAPIV3.NonArraySchemaObject & { $schema: string; components: Record<string, unknown> } = { ...$refs.get(item.schema.$ref) };
|
||||
resolved.components = $refs.get('#/components');
|
||||
resolved.$schema = 'http://json-schema.org/schema';
|
||||
return resolved;
|
||||
}
|
||||
|
||||
if (!item.schema) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return item.schema;
|
||||
}
|
||||
|
||||
function serializeSchema(schema: OpenAPIV3.SchemaObject): string {
|
||||
for (const key in schema.properties) {
|
||||
// Append 'null' to property type if nullable true, see FTI-3278
|
||||
// TODO: this does not conform to the OpenAPI 3 spec typings. We may need to investifate further why this was needed
|
||||
|
||||
// @ts-expect-error this needs a casting perhaps. schema can be either ArraySchemaObject or NonArraySchemaObject. Only the later has 'properties'
|
||||
if (schema.properties[key].nullable === true) {
|
||||
// @ts-expect-error this needs some further investigation. 'type' is merely an string enum, not an array according to the OpenAPI 3 typings.
|
||||
schema.properties[key].type = [schema.properties[key].type, 'null'];
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify(schema);
|
||||
}
|
||||
|
||||
export async function generateBodyOptions(api: OpenApi3Spec, operation?: OA3Operation) {
|
||||
const $refs: SwaggerParser.$Refs = await SwaggerParser.resolve(api);
|
||||
let bodySchema;
|
||||
let allowedContentTypes;
|
||||
const bodyContent = (operation?.requestBody as OA3RequestBody)?.content;
|
||||
|
||||
const requestBody = resolveRequestBodyContent($refs, operation);
|
||||
const bodyContent = requestBody?.content;
|
||||
|
||||
if (bodyContent) {
|
||||
const jsonContentType = 'application/json';
|
||||
allowedContentTypes = Object.keys(bodyContent);
|
||||
|
||||
if (allowedContentTypes.includes(jsonContentType)) {
|
||||
const item = bodyContent[jsonContentType];
|
||||
const schema = item.schema;
|
||||
for (const key in schema.properties) {
|
||||
// Append 'null' to property type if nullable true, see FTI-3278
|
||||
if (schema.properties[key].nullable === true) {
|
||||
schema.properties[key].type = [schema.properties[key].type, 'null'];
|
||||
}
|
||||
}
|
||||
bodySchema = JSON.stringify(item.schema);
|
||||
const item: OpenAPIV3.MediaTypeObject = bodyContent[jsonContentType];
|
||||
|
||||
const schema = resolveItemSchema($refs, item);
|
||||
bodySchema = serializeSchema(schema);
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,13 +224,15 @@ export function generateBodyOptions(operation?: OA3Operation) {
|
||||
};
|
||||
}
|
||||
|
||||
export function generateRequestValidatorPlugin({
|
||||
plugin = { name: 'request-validator' },
|
||||
export async function generateRequestValidatorPlugin({
|
||||
tags,
|
||||
api,
|
||||
plugin = { name: 'request-validator' },
|
||||
operation,
|
||||
}: {
|
||||
plugin?: Partial<RequestValidatorPlugin>;
|
||||
tags: string[];
|
||||
api: OpenApi3Spec;
|
||||
plugin?: Partial<RequestValidatorPlugin>;
|
||||
operation?: OA3Operation;
|
||||
}) {
|
||||
const config: Partial<RequestValidatorPlugin['config']> = {
|
||||
@ -127,9 +240,9 @@ export function generateRequestValidatorPlugin({
|
||||
};
|
||||
|
||||
// // Use original or generated parameter_schema
|
||||
const parameterSchema = isParameterSchema(plugin.config) ? plugin.config.parameter_schema : generateParameterSchema(operation);
|
||||
const parameterSchema = isParameterSchema(plugin.config) ? plugin.config.parameter_schema : await generateParameterSchema(api, operation);
|
||||
|
||||
const generated = generateBodyOptions(operation);
|
||||
const generated = await generateBodyOptions(api, operation);
|
||||
|
||||
// Use original or generated body_schema
|
||||
let bodySchema = isBodySchema(plugin.config) ? plugin.config.body_schema : generated.bodySchema;
|
||||
@ -174,12 +287,12 @@ export function generateRequestValidatorPlugin({
|
||||
return requestValidatorPlugin;
|
||||
}
|
||||
|
||||
export function generateGlobalPlugins(api: OpenApi3Spec, tags: string[]) {
|
||||
export async function generateGlobalPlugins(api: OpenApi3Spec, tags: string[]) {
|
||||
const globalPlugins = generatePlugins(api, tags);
|
||||
const plugin = getRequestValidatorPluginDirective(api);
|
||||
|
||||
if (plugin) {
|
||||
globalPlugins.push(generateRequestValidatorPlugin({ plugin, tags }));
|
||||
globalPlugins.push(await generateRequestValidatorPlugin({ plugin, tags, api }));
|
||||
}
|
||||
|
||||
return {
|
||||
@ -189,14 +302,14 @@ export function generateGlobalPlugins(api: OpenApi3Spec, tags: string[]) {
|
||||
};
|
||||
}
|
||||
|
||||
export const generateOperationPlugins = ({ operation, pathPlugins, parentValidatorPlugin, tags }: {
|
||||
export const generateOperationPlugins = async ({ operation, pathPlugins, parentValidatorPlugin, tags, api }: {
|
||||
operation: OA3Operation;
|
||||
pathPlugins: DCPlugin[];
|
||||
parentValidatorPlugin?: RequestValidatorPlugin | null;
|
||||
tags: string[];
|
||||
api: OpenApi3Spec;
|
||||
}) => {
|
||||
const operationPlugins = generatePlugins(operation, tags);
|
||||
|
||||
// Check if validator plugin exists on the operation, even if the value of the plugin is undefined
|
||||
const operationValidatorPlugin = getRequestValidatorPluginDirective(operation);
|
||||
|
||||
@ -204,7 +317,7 @@ export const generateOperationPlugins = ({ operation, pathPlugins, parentValidat
|
||||
const plugin = operationValidatorPlugin || parentValidatorPlugin;
|
||||
|
||||
if (plugin) {
|
||||
operationPlugins.push(generateRequestValidatorPlugin({ plugin, tags, operation }));
|
||||
operationPlugins.push(await generateRequestValidatorPlugin({ plugin, tags, operation, api }));
|
||||
}
|
||||
|
||||
// Operation plugins take precedence over path plugins
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { describe, expect, it } from '@jest/globals';
|
||||
import { OpenAPIV3 } from 'openapi-types';
|
||||
|
||||
import { OA3SecurityScheme, OA3SecuritySchemeOpenIdConnect } from '../types/openapi3';
|
||||
import { OA3SecurityScheme } from '../types/openapi3';
|
||||
import { tags } from './jest/test-helpers';
|
||||
import { generateSecurityPlugin } from './security-plugins';
|
||||
|
||||
@ -35,7 +36,7 @@ describe('security-plugins', () => {
|
||||
enabled: true,
|
||||
protocols: ['http', 'https'],
|
||||
},
|
||||
} as OA3SecuritySchemeOpenIdConnect;
|
||||
} as OpenAPIV3.OpenIdSecurityScheme;
|
||||
|
||||
const scopesRequired = ['required_scope', 'ziltoid_omniscient_power'];
|
||||
const result = generateSecurityPlugin(scheme, scopesRequired, tags);
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { OpenAPIV3 } from 'openapi-types';
|
||||
|
||||
import { getSecurity } from '../common';
|
||||
import { DCPlugin } from '../types/declarative-config';
|
||||
import { BasicAuthPlugin, KeyAuthPlugin, OpenIDConnectPlugin } from '../types/kong';
|
||||
import { OA3Operation, OA3SecurityScheme, OA3SecuritySchemeApiKey, OA3SecuritySchemeHttp, OA3SecuritySchemeOpenIdConnect, OpenApi3Spec } from '../types/openapi3';
|
||||
import { OA3Operation, OA3SecurityScheme, OpenApi3Spec } from '../types/openapi3';
|
||||
|
||||
export function generateSecurityPlugins(
|
||||
op: OA3Operation | null,
|
||||
@ -28,7 +30,7 @@ export function generateSecurityPlugins(
|
||||
return plugins;
|
||||
}
|
||||
|
||||
export const generateApiKeySecurityPlugin = (scheme: OA3SecuritySchemeApiKey) => {
|
||||
export const generateApiKeySecurityPlugin = (scheme: OpenAPIV3.ApiKeySecurityScheme) => {
|
||||
if (!['query', 'header', 'cookie'].includes(scheme.in)) {
|
||||
throw new Error(`a ${scheme.type} object expects valid "in" property. Got ${scheme.in}`);
|
||||
}
|
||||
@ -45,7 +47,7 @@ export const generateApiKeySecurityPlugin = (scheme: OA3SecuritySchemeApiKey) =>
|
||||
return keyAuthPlugin;
|
||||
};
|
||||
|
||||
export const generateBasicAuthPlugin = (scheme: OA3SecuritySchemeHttp) => {
|
||||
export const generateBasicAuthPlugin = (scheme: OpenAPIV3.HttpSecurityScheme) => {
|
||||
if ((scheme.scheme || '').toLowerCase() !== 'basic') {
|
||||
throw new Error(`Only "basic" http scheme supported. got ${scheme.scheme}`);
|
||||
}
|
||||
@ -55,7 +57,7 @@ export const generateBasicAuthPlugin = (scheme: OA3SecuritySchemeHttp) => {
|
||||
return basicAuthPlugin;
|
||||
};
|
||||
|
||||
export const generateOpenIdConnectSecurityPlugin = (scheme: OA3SecuritySchemeOpenIdConnect, args: string[]) => {
|
||||
export const generateOpenIdConnectSecurityPlugin = (scheme: OpenAPIV3.OpenIdSecurityScheme, args: string[]) => {
|
||||
if (!scheme.openIdConnectUrl) {
|
||||
throw new Error(`invalid "openIdConnectUrl" property. Got ${scheme.openIdConnectUrl}`);
|
||||
}
|
||||
@ -86,15 +88,15 @@ export function generateSecurityPlugin(
|
||||
// Generate base plugin
|
||||
switch (scheme?.type.toLowerCase()) {
|
||||
case 'apikey':
|
||||
plugin = generateApiKeySecurityPlugin(scheme as OA3SecuritySchemeApiKey);
|
||||
plugin = generateApiKeySecurityPlugin(scheme as OpenAPIV3.ApiKeySecurityScheme);
|
||||
break;
|
||||
|
||||
case 'http':
|
||||
plugin = generateBasicAuthPlugin(scheme as OA3SecuritySchemeHttp);
|
||||
plugin = generateBasicAuthPlugin(scheme as OpenAPIV3.HttpSecurityScheme);
|
||||
break;
|
||||
|
||||
case 'openidconnect':
|
||||
plugin = generateOpenIdConnectSecurityPlugin(scheme as OA3SecuritySchemeOpenIdConnect, args);
|
||||
plugin = generateOpenIdConnectSecurityPlugin(scheme as OpenAPIV3.OpenIdSecurityScheme, args);
|
||||
break;
|
||||
|
||||
case 'oauth2':
|
||||
|
@ -58,7 +58,7 @@ describe('services', () => {
|
||||
|
||||
const fn = () => generateServices(spec, tags);
|
||||
|
||||
expect(fn).toThrowError('no servers defined in spec');
|
||||
expect(fn).rejects.toThrowError('no servers defined in spec');
|
||||
});
|
||||
|
||||
it('throws for a root level x-kong-route-default', () => {
|
||||
@ -69,16 +69,16 @@ describe('services', () => {
|
||||
|
||||
const fn = () => generateServices(spec, tags);
|
||||
|
||||
expect(fn).toThrowError('expected root-level \'x-kong-route-defaults\' to be an object');
|
||||
expect(fn).rejects.toThrowError('expected root-level \'x-kong-route-defaults\' to be an object');
|
||||
});
|
||||
|
||||
it('ignores null for a root level x-kong-route-default', () => {
|
||||
it('ignores null for a root level x-kong-route-default', async () => {
|
||||
const spec = getSpec({
|
||||
// @ts-expect-error intentionally invalid
|
||||
[xKongRouteDefaults]: null,
|
||||
});
|
||||
const specResult = getSpecResult();
|
||||
expect(generateServices(spec, tags)).toEqual([specResult]);
|
||||
expect(await generateServices(spec, tags)).toEqual([specResult]);
|
||||
});
|
||||
|
||||
it('throws for a paths level x-kong-route-default', () => {
|
||||
@ -88,15 +88,15 @@ describe('services', () => {
|
||||
|
||||
const fn = () => generateServices(spec, tags);
|
||||
|
||||
expect(fn).toThrowError('expected \'x-kong-route-defaults\' to be an object (at path \'/cats\')');
|
||||
expect(fn).rejects.toThrowError('expected \'x-kong-route-defaults\' to be an object (at path \'/cats\')');
|
||||
});
|
||||
|
||||
it('ignores null for a paths level x-kong-route-default', () => {
|
||||
it('ignores null for a paths level x-kong-route-default', async () => {
|
||||
const spec = getSpec();
|
||||
// @ts-expect-error intentionally invalid
|
||||
spec.paths['/cats'][xKongRouteDefaults] = null;
|
||||
const specResult = getSpecResult();
|
||||
expect(generateServices(spec, tags)).toEqual([specResult]);
|
||||
expect(await generateServices(spec, tags)).toEqual([specResult]);
|
||||
});
|
||||
|
||||
it('throws for an operation level x-kong-route-default', () => {
|
||||
@ -106,27 +106,27 @@ describe('services', () => {
|
||||
|
||||
const fn = () => generateServices(spec, tags);
|
||||
|
||||
expect(fn).toThrowError(
|
||||
expect(fn).rejects.toThrowError(
|
||||
'expected \'x-kong-route-defaults\' to be an object (at operation \'post\' of path \'/cats\')',
|
||||
);
|
||||
});
|
||||
|
||||
it('ignores null for an operation level x-kong-route-default', () => {
|
||||
it('ignores null for an operation level x-kong-route-default', async () => {
|
||||
const spec = getSpec();
|
||||
// @ts-expect-error intentionally invalid
|
||||
spec.paths['/cats'].post[xKongRouteDefaults] = null;
|
||||
const specResult = getSpecResult();
|
||||
expect(generateServices(spec, tags)).toEqual([specResult]);
|
||||
expect(await generateServices(spec, tags)).toEqual([specResult]);
|
||||
});
|
||||
});
|
||||
describe('generateServices()', () => {
|
||||
it('generates generic service with paths', () => {
|
||||
it('generates generic service with paths', async () => {
|
||||
const spec = getSpec();
|
||||
const specResult = getSpecResult();
|
||||
expect(generateServices(spec, tags)).toEqual([specResult]);
|
||||
expect(await generateServices(spec, tags)).toEqual([specResult]);
|
||||
});
|
||||
|
||||
it('generates routes with request validator plugin from operation over path over global', () => {
|
||||
it('generates routes with request validator plugin from operation over path over global', async () => {
|
||||
const spec = getSpec({
|
||||
// global req validator plugin
|
||||
[xKongPluginRequestValidator]: {
|
||||
@ -271,10 +271,10 @@ describe('services', () => {
|
||||
],
|
||||
},
|
||||
];
|
||||
expect(generateServices(spec, tags)).toEqual([specResult]);
|
||||
expect(await generateServices(spec, tags)).toEqual([specResult]);
|
||||
});
|
||||
|
||||
it('generates routes with plugins from operation over path', () => {
|
||||
it('generates routes with plugins from operation over path', async () => {
|
||||
const spec = getSpec();
|
||||
spec.paths = {
|
||||
'/dogs': {
|
||||
@ -333,10 +333,10 @@ describe('services', () => {
|
||||
],
|
||||
},
|
||||
];
|
||||
expect(generateServices(spec, tags)).toEqual([specResult]);
|
||||
expect(await generateServices(spec, tags)).toEqual([specResult]);
|
||||
});
|
||||
|
||||
it('replaces variables', () => {
|
||||
it('replaces variables', async () => {
|
||||
const spec = getSpec();
|
||||
spec.servers = [
|
||||
{
|
||||
@ -356,7 +356,7 @@ describe('services', () => {
|
||||
specResult.port = 8443;
|
||||
specResult.host = 'demo.saas-app.com';
|
||||
specResult.path = '/v2';
|
||||
expect(generateServices(spec, tags)).toEqual([specResult]);
|
||||
expect(await generateServices(spec, tags)).toEqual([specResult]);
|
||||
});
|
||||
|
||||
describe('x-kong-route-defaults and strip_path', () => {
|
||||
@ -374,33 +374,33 @@ describe('services', () => {
|
||||
operationLevel: true,
|
||||
} as unknown as DCRoute;
|
||||
|
||||
it('root level', () => {
|
||||
it('root level', async () => {
|
||||
const spec = getSpec({
|
||||
[xKongRouteDefaults]: rootLevel,
|
||||
});
|
||||
const specResult = getSpecResult();
|
||||
specResult.routes = specResult.routes.map(route => ({ ...route, ...rootLevel }));
|
||||
expect(generateServices(spec, tags)).toEqual([specResult]);
|
||||
expect(await generateServices(spec, tags)).toEqual([specResult]);
|
||||
});
|
||||
|
||||
it('path level', () => {
|
||||
it('path level', async () => {
|
||||
const spec = getSpec();
|
||||
spec.paths['/dogs'][xKongRouteDefaults] = pathLevel;
|
||||
const specResult = getSpecResult();
|
||||
specResult.routes[1] = { ...specResult.routes[1], ...pathLevel };
|
||||
specResult.routes[2] = { ...specResult.routes[2], ...pathLevel };
|
||||
expect(generateServices(spec, tags)).toEqual([specResult]);
|
||||
expect(await generateServices(spec, tags)).toEqual([specResult]);
|
||||
});
|
||||
|
||||
it('operation level', () => {
|
||||
it('operation level', async () => {
|
||||
const spec = getSpec();
|
||||
(spec.paths['/dogs'].get as OA3Operation)[xKongRouteDefaults] = operationLevel;
|
||||
const specResult = getSpecResult();
|
||||
specResult.routes[1] = { ...specResult.routes[1], ...operationLevel };
|
||||
expect(generateServices(spec, tags)).toEqual([specResult]);
|
||||
expect(await generateServices(spec, tags)).toEqual([specResult]);
|
||||
});
|
||||
|
||||
it('will select (but not merge) the operation level over the root level', () => {
|
||||
it('will select (but not merge) the operation level over the root level', async () => {
|
||||
const spec = getSpec({
|
||||
[xKongRouteDefaults]: rootLevel,
|
||||
});
|
||||
@ -410,20 +410,20 @@ describe('services', () => {
|
||||
...route,
|
||||
...(route.paths[0] === '/cats$' ? operationLevel : rootLevel),
|
||||
}));
|
||||
expect(generateServices(spec, tags)).toEqual([specResult]);
|
||||
expect(await generateServices(spec, tags)).toEqual([specResult]);
|
||||
});
|
||||
|
||||
it('will select (but not merge) the operation level over the path level', () => {
|
||||
it('will select (but not merge) the operation level over the path level', async () => {
|
||||
const spec = getSpec();
|
||||
spec.paths['/dogs'][xKongRouteDefaults] = pathLevel;
|
||||
(spec.paths['/dogs'].post as OA3Operation)[xKongRouteDefaults] = operationLevel;
|
||||
const specResult = getSpecResult();
|
||||
specResult.routes[1] = { ...specResult.routes[1], ...pathLevel };
|
||||
specResult.routes[2] = { ...specResult.routes[2], ...operationLevel };
|
||||
expect(generateServices(spec, tags)).toEqual([specResult]);
|
||||
expect(await generateServices(spec, tags)).toEqual([specResult]);
|
||||
});
|
||||
|
||||
it('will select (but not merge) the path level over the root level', () => {
|
||||
it('will select (but not merge) the path level over the root level', async () => {
|
||||
const spec = getSpec({
|
||||
[xKongRouteDefaults]: rootLevel,
|
||||
});
|
||||
@ -433,19 +433,19 @@ describe('services', () => {
|
||||
...route,
|
||||
...(route.paths[0] === '/cats$' ? pathLevel : rootLevel),
|
||||
}));
|
||||
expect(generateServices(spec, tags)).toEqual([specResult]);
|
||||
expect(await generateServices(spec, tags)).toEqual([specResult]);
|
||||
});
|
||||
|
||||
it('allows overriding strip_path at the path level', () => {
|
||||
it('allows overriding strip_path at the path level', async () => {
|
||||
const spec = getSpec();
|
||||
spec.paths['/cats'][xKongRouteDefaults] = { strip_path: true };
|
||||
const specResult = getSpecResult();
|
||||
const cats = specResult.routes.find(route => route.paths[0] === '/cats$') as DCRoute;
|
||||
cats.strip_path = true;
|
||||
expect(generateServices(spec, tags)).toEqual([specResult]);
|
||||
expect(await generateServices(spec, tags)).toEqual([specResult]);
|
||||
});
|
||||
|
||||
it('allows overriding `strip_path` from `x-kong-route-defaults` at the root', () => {
|
||||
it('allows overriding `strip_path` from `x-kong-route-defaults` at the root', async () => {
|
||||
const spec = getSpec({
|
||||
[xKongRouteDefaults]: {
|
||||
strip_path: true,
|
||||
@ -453,7 +453,7 @@ describe('services', () => {
|
||||
});
|
||||
const specResult = getSpecResult();
|
||||
specResult.routes = specResult.routes.map(route => ({ ...route, strip_path: true }));
|
||||
expect(generateServices(spec, tags)).toEqual([specResult]);
|
||||
expect(await generateServices(spec, tags)).toEqual([specResult]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -4,13 +4,12 @@ import {
|
||||
getAllServers,
|
||||
getName,
|
||||
hasUpstreams,
|
||||
HttpMethod,
|
||||
parseUrl,
|
||||
pathVariablesToRegex,
|
||||
} from '../common';
|
||||
import { DCRoute, DCService } from '../types/declarative-config';
|
||||
import { xKongName, xKongServiceDefaults } from '../types/kong';
|
||||
import { OA3PathItem, OA3Server, OpenApi3Spec } from '../types/openapi3';
|
||||
import { HttpMethods, OA3PathItem, OA3Server, OpenApi3Spec } from '../types/openapi3';
|
||||
import {
|
||||
generateGlobalPlugins,
|
||||
generateOperationPlugins,
|
||||
@ -20,7 +19,7 @@ import {
|
||||
import { generateSecurityPlugins } from './security-plugins';
|
||||
import { appendUpstreamToName } from './upstreams';
|
||||
|
||||
export function generateServices(api: OpenApi3Spec, tags: string[]) {
|
||||
export async function generateServices(api: OpenApi3Spec, tags: string[]) {
|
||||
const servers = getAllServers(api);
|
||||
|
||||
if (servers.length === 0) {
|
||||
@ -28,22 +27,22 @@ export function generateServices(api: OpenApi3Spec, tags: string[]) {
|
||||
}
|
||||
|
||||
// only support one service for now
|
||||
const service = generateService(servers[0], api, tags);
|
||||
const service = await generateService(servers[0], api, tags);
|
||||
return [service];
|
||||
}
|
||||
|
||||
export function generateService(server: OA3Server, api: OpenApi3Spec, tags: string[]) {
|
||||
export async function generateService(server: OA3Server, api: OpenApi3Spec, tags: string[]) {
|
||||
const serverUrl = fillServerVariables(server);
|
||||
const name = getName(api);
|
||||
const parsedUrl = parseUrl(serverUrl);
|
||||
|
||||
let host = parsedUrl.hostname;
|
||||
if (hasUpstreams(api)) {
|
||||
host = appendUpstreamToName(name);
|
||||
host = appendUpstreamToName(name);
|
||||
}
|
||||
|
||||
// Service plugins
|
||||
const globalPlugins = generateGlobalPlugins(api, tags);
|
||||
const globalPlugins = await generateGlobalPlugins(api, tags);
|
||||
const serviceDefaults = api[xKongServiceDefaults] || {};
|
||||
|
||||
if (typeof serviceDefaults !== 'object') {
|
||||
@ -128,11 +127,12 @@ export function generateService(server: OA3Server, api: OpenApi3Spec, tags: stri
|
||||
// Path plugin takes precedence over global
|
||||
const parentValidatorPlugin = pathValidatorPlugin || globalPlugins.requestValidatorPlugin;
|
||||
|
||||
const regularPlugins = generateOperationPlugins({
|
||||
const regularPlugins = await generateOperationPlugins({
|
||||
operation,
|
||||
pathPlugins,
|
||||
parentValidatorPlugin,
|
||||
tags,
|
||||
api,
|
||||
});
|
||||
|
||||
const plugins = [...regularPlugins, ...securityPlugins];
|
||||
@ -151,7 +151,7 @@ export function generateService(server: OA3Server, api: OpenApi3Spec, tags: stri
|
||||
export function generateRouteName(
|
||||
api: OpenApi3Spec,
|
||||
routePath: string,
|
||||
method: keyof typeof HttpMethod,
|
||||
method: HttpMethods
|
||||
) {
|
||||
const name = getName(api);
|
||||
const pathItem = api.paths[routePath];
|
||||
|
@ -7,17 +7,17 @@ import { generateServices } from './services';
|
||||
import { generateUpstreams } from './upstreams';
|
||||
|
||||
describe('tags', () => {
|
||||
it('test that tags are appended to Service entities', () => {
|
||||
it('test that tags are appended to Service entities', async () => {
|
||||
const spec = getSpec();
|
||||
const services = generateServices(spec, tags);
|
||||
const services = await generateServices(spec, tags);
|
||||
services.forEach(service => {
|
||||
expect(service.tags).toEqual(tags);
|
||||
});
|
||||
});
|
||||
|
||||
it('test that tags are appended to Route entities', () => {
|
||||
it('test that tags are appended to Route entities', async () => {
|
||||
const spec = getSpec();
|
||||
const services = generateServices(spec, tags);
|
||||
const services = await generateServices(spec, tags);
|
||||
services.forEach(service => {
|
||||
service.routes.forEach(route => {
|
||||
expect(route.tags).toEqual(tags);
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from '@jest/globals';
|
||||
import fs from 'fs';
|
||||
import { OpenAPIV3 } from 'openapi-types';
|
||||
import path from 'path';
|
||||
import YAML from 'yaml';
|
||||
|
||||
@ -15,6 +16,7 @@ const firstK8sDocument = {
|
||||
},
|
||||
route: {
|
||||
methods: [
|
||||
// There was some discrepency how we "generate" mock data and how we implemented it
|
||||
'get',
|
||||
],
|
||||
},
|
||||
@ -91,7 +93,7 @@ describe('top-level API exports', () => {
|
||||
const parsedSpec = YAML.parse(dcFixtureFileString);
|
||||
const {
|
||||
documents: [dc],
|
||||
} = generateFromSpec(parsedSpec, 'kong-declarative-config') as DeclarativeConfigResult;
|
||||
} = await generateFromSpec(parsedSpec, 'kong-declarative-config') as DeclarativeConfigResult;
|
||||
expect(dc._format_version).toBe('1.1');
|
||||
expect(dc.services.length).toBe(1);
|
||||
expect(dc).not.toHaveProperty('upstreams');
|
||||
@ -104,7 +106,7 @@ describe('top-level API exports', () => {
|
||||
label,
|
||||
documents,
|
||||
warnings,
|
||||
} = generateFromSpec(parsedSpec, 'kong-for-kubernetes') as KongForKubernetesResult;
|
||||
} = await generateFromSpec(parsedSpec, 'kong-for-kubernetes') as KongForKubernetesResult;
|
||||
expect(type).toBe('kong-for-kubernetes');
|
||||
expect(label).toBe('Kong for Kubernetes');
|
||||
expect(documents).toHaveLength(9);
|
||||
@ -133,24 +135,25 @@ describe('top-level API exports', () => {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
} as OpenAPIV3.SchemaObject,
|
||||
},
|
||||
},
|
||||
};
|
||||
const specResolved: OpenApi3Spec = {
|
||||
openapi: '3.0.0',
|
||||
components: partialSpec.components,
|
||||
info: {},
|
||||
info: {
|
||||
title: '',
|
||||
version: '',
|
||||
},
|
||||
paths: {
|
||||
'/': {
|
||||
post: {
|
||||
responses: {
|
||||
200: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
'$ref': '#/components/schemas/dog',
|
||||
} as OpenAPIV3.SchemaObject,
|
||||
} as OpenAPIV3.ResponsesObject,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1,6 +1,6 @@
|
||||
import SwaggerParser from '@apidevtools/swagger-parser';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import SwaggerParser from 'swagger-parser';
|
||||
import YAML from 'yaml';
|
||||
|
||||
import { generateDeclarativeConfigFromSpec } from './declarative-config/generate';
|
||||
@ -30,18 +30,20 @@ export const parseSpec = (spec: string | Record<string, any>) => {
|
||||
|
||||
// Ensure it has some required properties to make parsing a bit less strict
|
||||
if (!api.info) {
|
||||
api.info = {};
|
||||
api.info = {
|
||||
title: '',
|
||||
version: '',
|
||||
};
|
||||
}
|
||||
|
||||
if (api.openapi === '3.0') {
|
||||
api.openapi = '3.0.0';
|
||||
}
|
||||
|
||||
// @ts-expect-error until we make our OpenAPI type extend from the canonical one (i.e. from `openapi-types`, we'll need to shim this here)
|
||||
return SwaggerParser.dereference(api) as Promise<OpenApi3Spec>;
|
||||
return SwaggerParser.bundle(api) as Promise<OpenApi3Spec>;
|
||||
};
|
||||
|
||||
export const generateFromSpec = (
|
||||
export const generateFromSpec = async (
|
||||
api: OpenApi3Spec,
|
||||
type: ConversionResultType,
|
||||
tags: string[] = [],
|
||||
@ -50,7 +52,7 @@ export const generateFromSpec = (
|
||||
|
||||
switch (type) {
|
||||
case 'kong-declarative-config':
|
||||
return generateDeclarativeConfigFromSpec(api, allTags);
|
||||
return await generateDeclarativeConfigFromSpec(api, allTags);
|
||||
|
||||
case 'kong-for-kubernetes':
|
||||
return generateKongForKubernetesConfigFromSpec(api);
|
||||
|
@ -47,6 +47,8 @@ describe('index', () => {
|
||||
'x-kubernetes-ingress-metadata': {
|
||||
name: 'K8s name',
|
||||
},
|
||||
title: '',
|
||||
version: '',
|
||||
},
|
||||
});
|
||||
expect(getSpecName(spec)).toBe('k8s-name');
|
||||
@ -63,6 +65,8 @@ describe('index', () => {
|
||||
'nginx.ingress.kubernetes.io/rewrite-target': '/',
|
||||
},
|
||||
},
|
||||
title: '',
|
||||
version: '',
|
||||
},
|
||||
});
|
||||
const result = generateMetadataAnnotations(spec, {
|
||||
@ -117,6 +121,8 @@ describe('index', () => {
|
||||
name: 'info-name',
|
||||
annotations,
|
||||
},
|
||||
title: '',
|
||||
version: '',
|
||||
},
|
||||
});
|
||||
const result = generateMetadataAnnotations(spec, {
|
||||
@ -610,9 +616,9 @@ describe('index', () => {
|
||||
...pluginKeyAuth,
|
||||
paths: {
|
||||
'/path': {
|
||||
GET: {},
|
||||
PUT: { ...pluginKeyAuth },
|
||||
POST: { ...pluginKeyAuth, ...pluginDummy },
|
||||
get: {},
|
||||
put: { ...pluginKeyAuth },
|
||||
post: { ...pluginKeyAuth, ...pluginDummy },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -40,10 +40,10 @@ export const methodDoc = (method: HttpMethodType | Lowercase<HttpMethodType>): K
|
||||
apiVersion: 'configuration.konghq.com/v1',
|
||||
kind: 'KongIngress',
|
||||
metadata: {
|
||||
name: `${method}-method`,
|
||||
name: `${method}-method`.toLowerCase(),
|
||||
},
|
||||
route: {
|
||||
methods: [method.toUpperCase() as HttpMethodType],
|
||||
methods: [method],
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { beforeEach, describe, expect, it, jest } from '@jest/globals';
|
||||
import { OpenAPIV3 } from 'openapi-types';
|
||||
|
||||
import { HttpMethod } from '../common';
|
||||
import { dummyPluginDoc, pluginDummy } from '../declarative-config/jest/test-helpers';
|
||||
@ -61,7 +62,6 @@ describe('plugins', () => {
|
||||
|
||||
const components: OA3Components = {
|
||||
securitySchemes: {
|
||||
// @ts-expect-error -- TSCONVERSION
|
||||
really_basic: {
|
||||
type: 'http',
|
||||
scheme: 'basic',
|
||||
@ -78,7 +78,6 @@ describe('plugins', () => {
|
||||
},
|
||||
petstore_oauth2: {
|
||||
type: 'oauth2',
|
||||
// @ts-expect-error -- TSCONVERSION
|
||||
flows: {
|
||||
clientCredentials: {
|
||||
tokenUrl: 'http://example.org/api/oauth/dialog',
|
||||
@ -89,7 +88,6 @@ describe('plugins', () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
// @ts-expect-error -- TSCONVERSION
|
||||
petstore_openid: {
|
||||
type: 'openIdConnect',
|
||||
openIdConnectUrl: 'http://example.org/oid-discovery',
|
||||
@ -362,24 +360,24 @@ describe('plugins', () => {
|
||||
|
||||
it('should return plugins for all operations on path', () => {
|
||||
const pathItem: OA3PathItem = {
|
||||
[HttpMethod.get]: {},
|
||||
[HttpMethod.put]: { ...pluginKeyAuth, ...pluginDummy },
|
||||
[HttpMethod.post]: pluginDummy as OA3Operation,
|
||||
get: {},
|
||||
put: { ...pluginKeyAuth, ...pluginDummy },
|
||||
post: pluginDummy as OA3Operation,
|
||||
};
|
||||
const result = getOperationPlugins(pathItem, increment, spec);
|
||||
expect(result).toHaveLength(3);
|
||||
const get = result[0];
|
||||
expect(get.method).toBe(HttpMethod.get);
|
||||
expect(get.method).toBe('get');
|
||||
expect(get.plugins).toHaveLength(0);
|
||||
const put = result[1];
|
||||
expect(put.method).toBe(HttpMethod.put);
|
||||
expect(put.method).toBe('put');
|
||||
expect(put.plugins).toEqual([keyAuthPluginDoc('m0'), dummyPluginDoc('m1')]);
|
||||
const post = result[2];
|
||||
expect(post.method).toBe(HttpMethod.post);
|
||||
expect(post.method).toBe('post');
|
||||
expect(post.plugins).toEqual([dummyPluginDoc('m2')]);
|
||||
});
|
||||
|
||||
it.each(Object.values(HttpMethod))(
|
||||
it.each(Object.values(OpenAPIV3.HttpMethods))(
|
||||
'should extract method plugins for %o from path item',
|
||||
methodName => {
|
||||
const pathItem = {
|
||||
@ -396,7 +394,7 @@ describe('plugins', () => {
|
||||
it('should return security plugin from operation', () => {
|
||||
const api: OpenApi3Spec = { ...spec, components };
|
||||
const pathItem: OA3PathItem = {
|
||||
[HttpMethod.get]: {
|
||||
'get': {
|
||||
security: [
|
||||
{
|
||||
really_basic: [],
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { HttpMethodType } from '../common';
|
||||
import type { OpenAPIV3 } from 'openapi-types';
|
||||
|
||||
import {
|
||||
XKongName,
|
||||
XKongPluginKeyAuth,
|
||||
@ -9,68 +10,31 @@ import {
|
||||
XKongUpstreamDefaults,
|
||||
} from './kong';
|
||||
import { K8sIngressTLS } from './kubernetes-config';
|
||||
import { Taggable } from './outputs';
|
||||
|
||||
export interface StripPath {
|
||||
// eslint-disable-next-line camelcase -- this is defined by a spec that is out of our control
|
||||
strip_path?: boolean;
|
||||
}
|
||||
// eslint-disable-next-line camelcase -- this is defined by a spec that is out of our control
|
||||
strip_path?: boolean;
|
||||
}
|
||||
|
||||
export interface OA3Info {
|
||||
title?: string;
|
||||
version?: string;
|
||||
description?: string;
|
||||
termsOfService?: string;
|
||||
contact?: {
|
||||
name?: string;
|
||||
url?: string;
|
||||
email?: string;
|
||||
};
|
||||
license?: {
|
||||
name: string;
|
||||
url?: string;
|
||||
};
|
||||
'x-kubernetes-ingress-metadata'?: {
|
||||
name?: string;
|
||||
annotations?: Record<string, any>;
|
||||
};
|
||||
}
|
||||
export interface OA3InfoObjectKubernetesIngressMetadata {
|
||||
'x-kubernetes-ingress-metadata'?: {
|
||||
name?: string;
|
||||
annotations?: Record<string, any>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface OA3ExternalDocs {
|
||||
url: string;
|
||||
description?: string;
|
||||
}
|
||||
export type OA3Info = OpenAPIV3.InfoObject & OA3InfoObjectKubernetesIngressMetadata;
|
||||
|
||||
export interface OA3Parameter {
|
||||
name: string;
|
||||
in: 'query' | 'header' | 'path' | 'cookie';
|
||||
description?: string;
|
||||
required?: boolean;
|
||||
deprecated?: boolean;
|
||||
allowEmptyValue?: boolean;
|
||||
style?: 'form' | 'spaceDelimited' | 'pipeDelimited' | 'deepObject' | string;
|
||||
schema?: Record<string, any> | string;
|
||||
content?: Record<string, any>;
|
||||
explode?: boolean;
|
||||
}
|
||||
export type OA3ExternalDocs = OpenAPIV3.ExternalDocumentationObject;
|
||||
|
||||
export type OA3Parameter = OpenAPIV3.ParameterObject;
|
||||
/** see: https://swagger.io/specification/#request-body-object */
|
||||
export interface OA3RequestBody {
|
||||
content?: Record<string, any>; // TODO
|
||||
description?: string;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
export type OA3SecurityRequirement = Record<string, any>;
|
||||
|
||||
/** see: https://swagger.io/specification/#reference-object */
|
||||
export interface OA3Reference {
|
||||
$ref: string;
|
||||
}
|
||||
export type OA3RequestBody = OpenAPIV3.RequestBodyObject | OpenAPIV3.ReferenceObject;
|
||||
|
||||
export type OA3SecurityRequirement = OpenAPIV3.SecurityRequirementObject;
|
||||
export interface OA3ServerKubernetesTLS {
|
||||
'x-kubernetes-tls'?: K8sIngressTLS[];
|
||||
}
|
||||
'x-kubernetes-tls'?: K8sIngressTLS[];
|
||||
}
|
||||
|
||||
export interface OA3ServerKubernetesBackend {
|
||||
'x-kubernetes-backend'?: {
|
||||
@ -93,57 +57,26 @@ export interface OA3ServerKubernetesService {
|
||||
}
|
||||
|
||||
/** see: https://swagger.io/specification/#server-variable-object */
|
||||
export interface OA3ServerVariable {
|
||||
default: string;
|
||||
enum?: string[];
|
||||
description?: string;
|
||||
}
|
||||
export type OA3ServerVariable = OpenAPIV3.ServerVariableObject;
|
||||
|
||||
/** see: https://swagger.io/specification/#server-object */
|
||||
export type OA3Server = {
|
||||
url: string;
|
||||
description?: string;
|
||||
variables?: Record<string, OA3ServerVariable>;
|
||||
} & OA3ServerKubernetesTLS
|
||||
export type OA3Server = OpenAPIV3.ServerObject
|
||||
& OA3ServerKubernetesTLS
|
||||
& OA3ServerKubernetesBackend
|
||||
& OA3ServerKubernetesService;
|
||||
|
||||
export interface OA3ResponsesObject {
|
||||
$ref?: string;
|
||||
}
|
||||
export type OA3ResponsesObject = OpenAPIV3.ResponsesObject;
|
||||
|
||||
/** see: https://swagger.io/specification/#operation-object */
|
||||
export type OA3Operation = {
|
||||
description?: string;
|
||||
summary?: string;
|
||||
externalDocs?: OA3ExternalDocs;
|
||||
responses?: OA3ResponsesObject;
|
||||
operationId?: string;
|
||||
parameters?: (OA3Parameter | OA3Reference)[];
|
||||
requestBody?: OA3RequestBody | OA3Reference;
|
||||
deprecated?: boolean;
|
||||
security?: OA3SecurityRequirement[];
|
||||
servers?: OA3Server[];
|
||||
} & Taggable
|
||||
& XKongName
|
||||
& XKongRouteDefaults
|
||||
& XKongPluginKeyAuth
|
||||
& XKongPluginRequestValidator
|
||||
;
|
||||
export type OA3Operation = OpenAPIV3.OperationObject<XKongName & XKongRouteDefaults & XKongPluginKeyAuth & XKongPluginRequestValidator>;
|
||||
|
||||
type HTTPMethodPaths = Partial<Record<
|
||||
HttpMethodType | Lowercase<HttpMethodType>,
|
||||
OA3Operation
|
||||
>>;
|
||||
export type HttpMethods = OpenAPIV3.HttpMethods | Lowercase<OpenAPIV3.HttpMethods>;
|
||||
|
||||
/** see: https://swagger.io/specification/#path-item-object */
|
||||
export type OA3PathItem = {
|
||||
$ref?: string;
|
||||
summary?: string;
|
||||
description?: string;
|
||||
servers?: OA3Server[];
|
||||
parameters?: OA3Reference | OA3Parameter;
|
||||
} & HTTPMethodPaths
|
||||
export type OA3PathItem = OpenAPIV3.PathItemObject
|
||||
& {
|
||||
[method in HttpMethods]?: OA3Operation;
|
||||
}
|
||||
& XKongName
|
||||
& XKongRouteDefaults
|
||||
& XKongPluginRequestValidator
|
||||
@ -151,111 +84,44 @@ export type OA3PathItem = {
|
||||
;
|
||||
|
||||
/** see: https://swagger.io/specification/#paths-object */
|
||||
export type OA3Paths = Record<string, OA3PathItem>
|
||||
export type OA3Paths = {
|
||||
[pattern: string]: OA3PathItem;
|
||||
}
|
||||
& StripPath
|
||||
& XKongRouteDefaults
|
||||
;
|
||||
|
||||
/** see: https://swagger.io/specification/#security-scheme-object */
|
||||
export interface OA3SecuritySchemeApiKey {
|
||||
type: 'apiKey';
|
||||
name: string;
|
||||
in: 'query' | 'header' | 'cookie';
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/** see: https://swagger.io/specification/#security-scheme-object */
|
||||
export interface OA3SecuritySchemeHttp {
|
||||
type: 'http';
|
||||
name: string;
|
||||
scheme: string;
|
||||
bearerFormat?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/** see: https://swagger.io/specification/#security-scheme-object */
|
||||
export interface OA3SecuritySchemeOpenIdConnect {
|
||||
type: 'openIdConnect';
|
||||
name: string;
|
||||
openIdConnectUrl: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/** see: https://swagger.io/specification/#security-scheme-object */
|
||||
export interface OA3SecuritySchemeOAuth2Flow {
|
||||
authorizationUrl?: string;
|
||||
tokenUrl?: string;
|
||||
refreshUrl?: string;
|
||||
scopes: Record<string, string>;
|
||||
}
|
||||
|
||||
/** see: https://swagger.io/specification/#security-scheme-object */
|
||||
export interface OA3SecuritySchemeOAuth2 {
|
||||
type: 'oauth2';
|
||||
name: string;
|
||||
flows: {
|
||||
implicit: OA3SecuritySchemeOAuth2Flow;
|
||||
password: OA3SecuritySchemeOAuth2Flow;
|
||||
clientCredentials: OA3SecuritySchemeOAuth2Flow;
|
||||
authorizationCode: OA3SecuritySchemeOAuth2Flow;
|
||||
};
|
||||
description?: string;
|
||||
}
|
||||
|
||||
/** see: https://swagger.io/specification/#security-scheme-object */
|
||||
export type OA3SecurityScheme =
|
||||
| OA3SecuritySchemeApiKey
|
||||
| OA3SecuritySchemeHttp
|
||||
| OA3SecuritySchemeOpenIdConnect
|
||||
| OA3SecuritySchemeOAuth2;
|
||||
export type OA3SecurityScheme = OpenAPIV3.SecuritySchemeObject;
|
||||
|
||||
/** see: https://swagger.io/specification/#example-object */
|
||||
export interface OA3Example {
|
||||
summary?: string;
|
||||
description?: string;
|
||||
value?: any;
|
||||
externalValue?: string;
|
||||
}
|
||||
export type OA3Example = OpenAPIV3.ExampleObject;
|
||||
|
||||
/** see: https://swagger.io/specification/#schema-object */
|
||||
export interface OA3Schema {}
|
||||
export type OA3Schema = OpenAPIV3.SchemaObject;
|
||||
|
||||
/** see: https://swagger.io/specification/#header-object */
|
||||
export interface OA3Header {
|
||||
description?: string;
|
||||
required?: boolean;
|
||||
deprecated?: boolean;
|
||||
allowEmptyValue?: boolean;
|
||||
}
|
||||
export type OA3Header = OpenAPIV3.HeaderObject;
|
||||
|
||||
/** see: https://swagger.io/specification/#components-object */
|
||||
export interface OA3Components {
|
||||
schemas?: Record<string, OA3Schema | OA3Reference>;
|
||||
parameters?: Record<string, OA3Parameter | OA3Reference>;
|
||||
headers?: Record<string, OA3Header | OA3Reference>;
|
||||
requestBodies?: Record<string, OA3RequestBody | OA3Reference>;
|
||||
examples?: Record<string, OA3Example | OA3Reference>;
|
||||
securitySchemes?: Record<string, OA3SecurityScheme | OA3Reference>;
|
||||
}
|
||||
export type OA3Components = OpenAPIV3.ComponentsObject;
|
||||
|
||||
/** see: https://swagger.io/specification/#tag-object */
|
||||
export interface TagObject {
|
||||
name: string;
|
||||
description?: string;
|
||||
externalDocs?: Record<string, any>;
|
||||
}
|
||||
export type TagObject = OpenAPIV3.TagObject;
|
||||
|
||||
/** see: https://swagger.io/specification/#openapi-object */
|
||||
export type OpenApi3Spec = {
|
||||
openapi: string;
|
||||
info: OA3Info;
|
||||
paths: OA3Paths;
|
||||
servers?: OA3Server[];
|
||||
components?: OA3Components;
|
||||
security?: OA3SecurityRequirement[];
|
||||
externalDocs?: OA3ExternalDocs;
|
||||
tags?: TagObject[];
|
||||
}
|
||||
export type OpenApi3Spec =
|
||||
{
|
||||
openapi: string;
|
||||
info: OA3Info;
|
||||
servers?: OA3Server[];
|
||||
paths: OA3Paths;
|
||||
components?: OA3Components;
|
||||
security?: OA3SecurityRequirement[];
|
||||
tags?: TagObject[];
|
||||
externalDocs?: OA3ExternalDocs;
|
||||
'x-express-openapi-additional-middleware'?: (((request: any, response: any, next: any) => Promise<void>) | ((request: any, response: any, next: any) => void))[];
|
||||
'x-express-openapi-validation-strict'?: boolean;
|
||||
}
|
||||
& XKongName
|
||||
& XKongPluginKeyAuth
|
||||
& XKongPluginRequestTermination
|
||||
|
Loading…
Reference in New Issue
Block a user