feat: support for webhooks to the split command (#550)

Co-authored-by: Anton_Kozachuk <anton.kozachuk@cloudmobilizd.com>
Co-authored-by: romanhotsiy <gotsijroman@gmail.com>
This commit is contained in:
Anton Kozachuk
2022-02-17 16:22:45 +02:00
committed by GitHub
parent 964c2ba797
commit 98d6122a9f
16 changed files with 629 additions and 91 deletions

View File

@@ -26,7 +26,7 @@ function getEntrypoints(folderPath: string) {
return Object.keys(redoclyYaml.apiDefinitions);
}
function getBundleResult(params: string[], folderPath: string) {
function getCommandOutput(params: string[], folderPath: string) {
const result = spawnSync('ts-node', params, {
cwd: folderPath,
env: {
@@ -72,6 +72,47 @@ describe('E2E', () => {
}
});
describe('split', () => {
test('without option: outDir', () => {
const folderPath = join(__dirname, `split/missing-outDir`);
const args = [
'--transpile-only',
'../../../packages/cli/src/index.ts',
'split',
'../../../__tests__/split/test-split/spec.json',
];
const result = getCommandOutput(args, folderPath);
(<any>expect(result)).toMatchSpecificSnapshot(join(folderPath, 'snapshot.js'));
});
test('swagger', () => {
const folderPath = join(__dirname, `split/oas2`);
const args = [
'--transpile-only',
'../../../packages/cli/src/index.ts',
'split',
'../../../__tests__/split/oas2/openapi.yaml',
'--outDir=output'
];
const result = getCommandOutput(args, folderPath);
(<any>expect(result)).toMatchSpecificSnapshot(join(folderPath, 'snapshot.js'));
});
test('openapi with no errors', () => {
const folderPath = join(__dirname, `split/oas3-no-errors`);
const file = '../../../__tests__/split/oas3-no-errors/openapi.yaml';
const args = [
'--transpile-only',
'../../../packages/cli/src/index.ts',
'split',
file,
'--outDir=output'
];
const result = getCommandOutput(args, folderPath);
(<any>expect(result)).toMatchSpecificSnapshot(join(folderPath, 'snapshot.js'));
});
});
describe('join', () => {
const folderPath = join(__dirname, 'join');
const contents = readdirSync(folderPath);
@@ -171,14 +212,14 @@ describe('E2E', () => {
test.each(['codeframe','stylish','json','checkstyle'])('bundle lint: should be formatted by format: %s', (format) => {
const params = [...args, `--format=${format}`];
const result = getBundleResult(params, folderPath);
const result = getCommandOutput(params, folderPath);
(<any>expect(result)).toMatchSpecificSnapshot(join(folderPath, `${format}-format-snapshot.js`));
});
test.each(['noFormatParameter','emptyFormatValue'])('bundle lint: no format parameter or empty value should be formatted as codeframe', (format) => {
const formatArgument = format === 'emptyFormatValue' ? ['--format'] : [];
const params = [...args, ... formatArgument];
const result = getBundleResult(params, folderPath);
const result = getCommandOutput(params, folderPath);
(<any>expect(result)).toMatchSpecificSnapshot(join(folderPath, `${format}-snapshot.js`));
});
});
@@ -193,7 +234,7 @@ describe('E2E', () => {
"--remove-unused-components",
...entryPoints,
];
const result = getBundleResult(args, folderPath);
const result = getCommandOutput(args, folderPath);
(<any>expect(result)).toMatchSpecificSnapshot(join(folderPath, 'remove-unused-components-snapshot.js'));
});
});

View File

@@ -0,0 +1,19 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`E2E split without option: outDir 1`] = `
index.ts split [entrypoint]
Split definition into a multi-file structure.
Positionals:
entrypoint API definition file that you want to split [string] [required]
Options:
--version Show version number. [boolean]
--help Show help. [boolean]
--outDir Output directory where files will be saved. [string] [required]
Missing required argument: outDir
`;

View File

@@ -0,0 +1,3 @@
{
"openapi": "3.0.1"
}

View File

@@ -0,0 +1,45 @@
swagger: "2.0"
info:
version: "1.0.0"
title: "Swagger Petstore"
description: "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification"
termsOfService: "http://swagger.io/terms/"
contact:
name: "Swagger API Team"
license:
name: "MIT"
host: "petstore.swagger.io"
basePath: "/api"
schemes:
- "http"
consumes:
- "application/json"
produces:
- "application/json"
paths:
/pets:
get:
description: "Returns all pets from the system that the user has access to"
produces:
- "application/json"
responses:
"200":
description: "A list of pets."
schema:
type: "array"
items:
$ref: "#/definitions/Pet"
definitions:
Pet:
type: "object"
required:
- "id"
- "name"
properties:
id:
type: "integer"
format: "int64"
name:
type: "string"
tag:
type: "string"

View File

@@ -0,0 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`E2E split swagger 1`] = `
OpenAPI 2 is not supported by this command
`;

View File

@@ -0,0 +1,111 @@
openapi: "3.0.0"
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
servers:
- url: http://petstore.swagger.io/v1
paths:
/pets:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
responses:
'200':
description: A paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Pets"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
post:
summary: Create a pet
operationId: createPets
tags:
- pets
responses:
'201':
description: Null response
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/pets/{petId}:
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
'200':
description: Expected response to a valid request
content:
application/json:
schema:
$ref: "#/components/schemas/Pet"
default:
description: unexpected error
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components:
schemas:
Pet:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
items:
$ref: "#/components/schemas/Pet"
Error:
type: object
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string

View File

@@ -0,0 +1,119 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`E2E split openapi with no errors 1`] = `
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
type: array
items:
$ref: ./Pet.yaml
type: object
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
schema:
type: integer
format: int32
responses:
'200':
description: A paged array of pets
headers:
x-next:
description: A link to the next page of responses
schema:
type: string
content:
application/json:
schema:
$ref: '#/components/schemas/Pets'
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
post:
summary: Create a pet
operationId: createPets
tags:
- pets
responses:
'201':
description: Null response
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
schema:
type: string
responses:
'200':
description: Expected response to a valid request
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
default:
description: unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
openapi: 3.0.0
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
servers:
- url: http://petstore.swagger.io/v1
paths:
/pets:
$ref: paths/pets.yaml
/pets/{petId}:
$ref: paths/pets@{petId}.yaml
🪓 Document: ../../../__tests__/split/oas3-no-errors/openapi.yaml is successfully split
and all related files are saved to the directory: output
../../../__tests__/split/oas3-no-errors/openapi.yaml: split processed in <test>ms
`;

View File

@@ -44,5 +44,27 @@
}
}
}
},
"x-webhooks": {
"test": {
"post": {
"summary": "New pet",
"description": "Information about a new pet in the systems",
"operationId": "newPet",
"tags": ["pet"],
"requestBody": {
"content": {
"application/json": {
"schema": { "$ref": "#/components/schemas/Test" }
}
}
},
"responses": {
"200": {
"description": "Return a 200 status to indicate that the data was received successfully"
}
}
}
}
}
}

View File

@@ -0,0 +1,88 @@
{
"openapi": "3.1.0",
"info": {
"title": "Webhook Example",
"version": "1.0.0"
},
"paths": {
"/pets": {
"get": {
"summary": "List all pets",
"operationId": "listPets",
"parameters": [
{
"name": "limit",
"in": "query",
"description": "How many items to return at one time (max 100)",
"required": false,
"schema": {
"type": "integer",
"format": "int32"
}
}
],
"responses": {
"200": {
"description": "A paged array of pets",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pets"
}
}
}
}
}
}
}
},
"webhooks": {
"test": {
"post": {
"requestBody": {
"description": "Information about a new pet in the system",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
}
},
"responses": {
"200": {
"description": "Return a 200 status to indicate that the data was received successfully"
}
}
}
}
},
"components": {
"schemas": {
"Pet": {
"required": [
"id",
"name"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
},
"tag": {
"type": "string"
}
}
},
"Pets": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Pet"
}
}
}
}
}

View File

@@ -1,32 +1,76 @@
import { iteratePaths } from '../index';
import { iteratePathItems, handleSplit } from '../index';
import * as path from 'path';
import * as openapiCore from '@redocly/openapi-core';
import {
ComponentsFiles,
} from '../types';
import { blue, green } from 'colorette';
jest.mock('../../../utils', () => ({
...jest.requireActual('../../../utils'),
writeYaml: jest.fn(),
}));
jest.mock('@redocly/openapi-core', () => ({
...jest.requireActual('@redocly/openapi-core'),
isRef: jest.fn(),
}));
describe('#split', () => {
it('should have correct paths for mac', () => {
const pathsDir = 'test/paths';
const openapiDir = 'test';
const componentsFiles: ComponentsFiles = {};
it('should split the file and show the success message', async () => {
const filePath = "./packages/cli/src/commands/split/__tests__/fixtures/spec.json";
jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
await handleSplit (
{
entrypoint: filePath,
outDir: openapiDir,
}
);
expect(process.stderr.write).toBeCalledTimes(2);
expect((process.stderr.write as jest.Mock).mock.calls[0][0]).toBe(
`🪓 Document: ${blue(filePath!)} ${green('is successfully split')}
and all related files are saved to the directory: ${blue(openapiDir)} \n`
);
expect((process.stderr.write as jest.Mock).mock.calls[1][0]).toContain(
`${filePath}: split processed in <test>ms`
);
});
it('should have correct path with paths', () => {
const openapi = require("./fixtures/spec.json");
jest.spyOn(openapiCore, 'slash').mockImplementation(() => 'paths/test.yaml');
jest.spyOn(path, 'relative').mockImplementation(() => 'paths/test.yaml');
iteratePaths(require("./fixtures/spec.json"), pathsDir, openapiDir);
iteratePathItems(openapi.paths, openapiDir, path.join(openapiDir, 'paths'), componentsFiles);
expect(openapiCore.slash).toHaveBeenCalledWith('paths/test.yaml');
expect(path.relative).toHaveBeenCalledWith('test', 'test/paths/test.yaml');
});
it('should have correct paths for windows', () => {
const pathsDir = 'test\\paths';
const openapiDir = 'test';
it('should have correct path with webhooks', () => {
const openapi = require("./fixtures/webhooks.json");
jest.spyOn(openapiCore, 'slash').mockImplementation(() => 'paths\\test.yaml');
jest.spyOn(path, 'relative').mockImplementation(() => 'paths\\test.yaml');
iteratePaths(require("./fixtures/spec.json"), pathsDir, openapiDir);
expect(openapiCore.slash).toHaveBeenCalledWith('paths\\test.yaml');
expect(path.relative).toHaveBeenCalledWith('test', 'test\\paths/test.yaml');
jest.spyOn(openapiCore, 'slash').mockImplementation(() => 'webhooks/test.yaml');
jest.spyOn(path, 'relative').mockImplementation(() => 'webhooks/test.yaml');
iteratePathItems(openapi.webhooks, openapiDir, path.join(openapiDir, 'webhooks'), componentsFiles, 'webhook_');
expect(openapiCore.slash).toHaveBeenCalledWith('webhooks/test.yaml');
expect(path.relative).toHaveBeenCalledWith('test', 'test/webhooks/test.yaml');
});
it('should have correct path with x-webhooks', () => {
const openapi = require("./fixtures/spec.json");
jest.spyOn(openapiCore, 'slash').mockImplementation(() => 'webhooks/test.yaml');
jest.spyOn(path, 'relative').mockImplementation(() => 'webhooks/test.yaml');
iteratePathItems(openapi['x-webhooks'], openapiDir, path.join(openapiDir, 'webhooks'), componentsFiles, 'webhook_');
expect(openapiCore.slash).toHaveBeenCalledWith('webhooks/test.yaml');
expect(path.relative).toHaveBeenCalledWith('test', 'test/webhooks/test.yaml');
});
});

View File

@@ -1,6 +1,6 @@
import { red, blue, yellow, green } from 'colorette';
import * as fs from 'fs';
import { parseYaml, slash } from '@redocly/openapi-core';
import { parseYaml, slash, isRef } from '@redocly/openapi-core';
import * as path from 'path';
import { performance } from 'perf_hooks';
const isEqual = require('lodash.isequal');
@@ -12,6 +12,7 @@ import {
Oas2Definition,
Oas3Schema,
Oas3Definition,
Oas3_1Definition,
Oas3Components,
Oas3ComponentName,
ComponentsFiles,
@@ -20,19 +21,19 @@ import {
OPENAPI3_COMPONENT,
COMPONENTS,
componentsPath,
PATHS,
OPENAPI3_METHOD_NAMES,
OPENAPI3_COMPONENT_NAMES
} from './types'
OPENAPI3_COMPONENT_NAMES,
Referenced
} from './types';
export async function handleSplit (argv: {
entrypoint?: string;
entrypoint: string;
outDir: string
}) {
const startedAt = performance.now();
const { entrypoint, outDir } = argv;
validateDefinitionFileName(entrypoint!);
const openapi = readYaml(entrypoint!) as Oas3Definition;
const openapi = readYaml(entrypoint!) as Oas3Definition | Oas3_1Definition;
splitDefinition(openapi, outDir);
process.stderr.write(
`🪓 Document: ${blue(entrypoint!)} ${green('is successfully split')}
@@ -41,23 +42,16 @@ export async function handleSplit (argv: {
printExecutionTime('split', startedAt, entrypoint!);
}
function splitDefinition(openapi: Oas3Definition, openapiDir: string) {
function splitDefinition(openapi: Oas3Definition | Oas3_1Definition, openapiDir: string) {
fs.mkdirSync(openapiDir, { recursive: true });
const pathsDir = path.join(openapiDir, PATHS);
fs.mkdirSync(pathsDir, { recursive: true });
const componentsFiles: ComponentsFiles = {};
iteratePaths(openapi, pathsDir, openapiDir);
iterateComponents(openapi, openapiDir, componentsFiles);
iteratePathItems(openapi.paths, openapiDir, path.join(openapiDir, 'paths'), componentsFiles);
const webhooks = (openapi as Oas3_1Definition).webhooks || (openapi as Oas3Definition)['x-webhooks'];
// use webhook_ prefix for code samples to prevent potential name-clashes with paths samples
iteratePathItems(webhooks, openapiDir, path.join(openapiDir, 'webhooks'), componentsFiles, 'webhook_');
function traverseDirectoryDeepCallback(filename: string, directory: string) {
if (isNotYaml(filename)) return;
const pathData = readYaml(filename);
replace$Refs(pathData, directory, componentsFiles);
writeYaml(pathData, filename);
}
traverseDirectoryDeep(pathsDir, traverseDirectoryDeepCallback);
replace$Refs(openapi, openapiDir, componentsFiles);
writeYaml(openapi, path.join(openapiDir, 'openapi.yaml'));
}
@@ -82,7 +76,7 @@ function validateDefinitionFileName(fileName: string) {
if (!fs.existsSync(fileName)) exitWithError(`File ${blue(fileName)} does not exist \n`);
const file = loadFile(fileName);
if ((file as Oas2Definition).swagger) exitWithError('OpenAPI 2 is not supported by this command');
if (!(file as Oas3Definition).openapi) exitWithError('File does not conform to the OpenAPI Specification. OpenAPI version is not specified');
if (!(file as Oas3Definition | Oas3_1Definition).openapi) exitWithError('File does not conform to the OpenAPI Specification. OpenAPI version is not specified');
return true;
}
@@ -100,19 +94,30 @@ function langToExt(lang: string) {
return langObj[lang];
}
function traverseDirectoryDeep(directory: string, callback: any) {
function traverseDirectoryDeep(directory: string, callback: any, componentsFiles: object) {
if (!fs.existsSync(directory) || !fs.statSync(directory).isDirectory()) return;
const files = fs.readdirSync(directory);
for (const f of files) {
const filename = path.join(directory, f);
if (fs.statSync(filename).isDirectory()) {
traverseDirectoryDeep(filename, callback);
traverseDirectoryDeep(filename, callback, componentsFiles);
} else {
callback(filename, directory);
callback(filename, directory, componentsFiles);
}
}
}
function traverseDirectoryDeepCallback(
filename: string,
directory: string,
componentsFiles: object,
) {
if (isNotYaml(filename)) return;
const pathData = readYaml(filename);
replace$Refs(pathData, directory, componentsFiles);
writeYaml(pathData, filename);
}
function crawl(object: any, visitor: any) {
if (!isObject(object)) return;
for (const key of Object.keys(object)) {
@@ -199,7 +204,7 @@ function doesFileDiffer(filename: string, componentData: any) {
return fs.existsSync(filename) && !isEqual(readYaml(filename), componentData);
}
function removeEmptyComponents(openapi: Oas3Definition, componentType: Oas3ComponentName) {
function removeEmptyComponents(openapi: Oas3Definition | Oas3_1Definition, componentType: Oas3ComponentName) {
if (openapi.components && isEmptyObject(openapi.components[componentType])) {
delete openapi.components[componentType];
}
@@ -237,49 +242,57 @@ function gatherComponentsFiles(
componentsFiles[componentType][componentName] = { inherits, filename };
}
function iteratePaths(
openapi: Oas3Definition,
pathsDir: string,
openapiDir: string
function iteratePathItems(
pathItems: Record<string, Referenced<Oas3PathItem>> | undefined,
openapiDir: string,
outDir: string,
componentsFiles: object,
codeSamplesPathPrefix: string = '',
) {
const { paths } = openapi;
if (paths) {
for (const oasPath of Object.keys(paths)) {
const pathFile = path.join(pathsDir, pathToFilename(oasPath)) + '.yaml';
const pathData: Oas3PathItem = paths[oasPath] as Oas3PathItem;
if (!pathItems) return;
fs.mkdirSync(outDir, { recursive: true });
for (const pathName of Object.keys(pathItems)) {
const pathFile = `${path.join(outDir, pathToFilename(pathName))}.yaml`;
const pathData = pathItems[pathName] as Oas3PathItem;
if (isRef(pathData)) continue;
for (const method of OPENAPI3_METHOD_NAMES) {
const methodData = pathData[method];
const methodDataXCode = methodData?.['x-code-samples'] || methodData?.['x-codeSamples'];
if (!methodDataXCode || !Array.isArray(methodDataXCode)) { continue; }
if (!methodDataXCode || !Array.isArray(methodDataXCode)) {
continue;
}
for (const sample of methodDataXCode) {
if (sample.source && (sample.source as any).$ref) continue;
const sampleFileName = path.join(
openapiDir,
'code_samples',
sample.lang,
pathToFilename(oasPath),
method + langToExt(sample.lang)
codeSamplesPathPrefix + pathToFilename(pathName),
method + langToExt(sample.lang),
);
fs.mkdirSync(path.dirname(sampleFileName), { recursive: true });
fs.writeFileSync(sampleFileName, sample.source);
// @ts-ignore
sample.source = {
$ref: slash(path.relative(pathsDir, sampleFileName))
$ref: slash(path.relative(outDir, sampleFileName))
};
}
}
writeYaml(pathData, pathFile);
paths[oasPath] = {
pathItems[pathName] = {
$ref: slash(path.relative(openapiDir, pathFile))
};
}
traverseDirectoryDeep(outDir, traverseDirectoryDeepCallback, componentsFiles);
}
}
function iterateComponents(
openapi: Oas3Definition,
openapi: Oas3Definition | Oas3_1Definition,
openapiDir: string,
componentsFiles: ComponentsFiles
) {
@@ -331,6 +344,4 @@ function iterateComponents(
}
}
export {
iteratePaths,
}
export { iteratePathItems };

View File

@@ -1,14 +1,18 @@
import {
Oas3Schema,
Oas3_1Schema,
Oas3Definition,
Oas3_1Definition,
Oas3Components,
Oas3PathItem,
Oas3Paths,
Oas3ComponentName,
Oas2Definition
Oas3_1Webhooks,
Oas2Definition,
Referenced
} from "@redocly/openapi-core";
export { Oas3Definition, Oas2Definition, Oas3Components, Oas3Paths, Oas3PathItem, Oas3ComponentName, Oas3Schema }
export type Definition = Oas3Definition | Oas2Definition;
export { Oas3_1Definition, Oas3Definition, Oas2Definition, Oas3Components, Oas3Paths, Oas3PathItem, Oas3ComponentName, Oas3_1Schema, Oas3Schema, Oas3_1Webhooks, Referenced }
export type Definition = Oas3_1Definition | Oas3Definition | Oas2Definition;
export interface ComponentsFiles {
[schemas: string]: any;
}
@@ -18,6 +22,8 @@ export interface refObj {
export const COMPONENTS = 'components';
export const PATHS = 'paths';
export const WEBHOOKS = 'webhooks';
export const xWEBHOOKS = 'x-webhooks';
export const componentsPath = `#/${COMPONENTS}/`;
enum OPENAPI3_METHOD {

View File

@@ -35,13 +35,21 @@ yargs
'split [entrypoint]',
'Split definition into a multi-file structure.',
(yargs) =>
yargs.positional('entrypoint', { type: 'string' }).option({
yargs
.positional('entrypoint', {
description: 'API definition file that you want to split',
type: 'string'
})
.option({
outDir: {
description: 'Output directory where files will be saved.',
required: true,
type: 'string',
},
}),
})
.demandOption(
'entrypoint'
),
handleSplit,
)
.command(
@@ -207,7 +215,7 @@ yargs
description: 'Remove unused components.',
type: 'boolean',
default: false,
}
},
}),
(argv) => {
handleBundle(argv, version);

View File

@@ -65,7 +65,7 @@ export function pathToFilename(path: string) {
return path
.replace(/~1/g, '/')
.replace(/~0/g, '~')
.substring(1)
.replace(/^\//, '')
.replace(/\//g, '@');
}

View File

@@ -5,12 +5,16 @@ export { Oas2Types } from './types/oas2';
export { ConfigTypes } from './types/redocly-yaml';
export {
Oas3Definition,
Oas3_1Definition,
Oas3Components,
Oas3PathItem,
Oas3Paths,
Oas3ComponentName,
Oas3Schema,
Oas3_1Schema,
Oas3Tag,
Oas3_1Webhooks,
Referenced
} from './typings/openapi';
export { Oas2Definition } from './typings/swagger';
export { StatsAccumulator, StatsName } from './typings/common';
@@ -30,7 +34,7 @@ export {
makeDocumentFromString,
} from './resolve';
export { parseYaml, stringifyYaml } from './js-yaml';
export { unescapePointer } from './ref-utils';
export { unescapePointer, isRef } from './ref-utils';
export { detectOpenAPI, OasMajorVersion, openAPIMajor, OasVersion } from './oas-types';
export { normalizeVisitors } from './visitors';

View File

@@ -7,6 +7,7 @@ export interface Oas3Definition {
security?: Oas3SecurityRequirement[];
tags?: Oas3Tag[];
externalDocs?: Oas3ExternalDocs;
'x-webhooks'?: Oas3_1Webhooks;
}
export interface Oas3Info {
@@ -154,6 +155,14 @@ export interface Oas3_1Schema extends Oas3Schema {
examples?: any[];
}
export interface Oas3_1Definition extends Oas3Definition {
webhooks?: Oas3_1Webhooks;
}
export interface Oas3_1Webhooks {
[webhook: string]: Referenced<Oas3PathItem>;
}
export interface Oas3Discriminator {
propertyName: string;
mapping?: { [name: string]: string };