From 5ea3dc68dd8b8806458b4e13d9b72fcbc4300fe2 Mon Sep 17 00:00:00 2001 From: Emmanuel Quentin Date: Wed, 3 Oct 2018 21:09:39 +0200 Subject: [PATCH] feat: support x-response-samples to render code responses --- README.md | 1 + demo/openapi.yaml | 27 ++++++++-- docs/redoc-vendor-extensions.md | 51 ++++++++++++++++--- .../ResponseSamples/ResponseSamples.tsx | 13 +++++ src/services/models/Operation.ts | 3 ++ src/types/open-api.d.ts | 7 +++ src/utils/openapi.ts | 1 + 7 files changed, 94 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3708a335..fb569dde 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,7 @@ ReDoc makes use of the following [vendor extensions](http://swagger.io/specifica * [`x-logo`](docs/redoc-vendor-extensions.md#x-logo) - is used to specify API logo * [`x-traitTag`](docs/redoc-vendor-extensions.md#x-traitTag) - useful for handling out common things like Pagination, Rate-Limits, etc * [`x-code-samples`](docs/redoc-vendor-extensions.md#x-code-samples) - specify operation code samples +* [`x-response-samples`](docs/redoc-vendor-extensions.md#x-response-samples) - specify operation response code samples * [`x-examples`](docs/redoc-vendor-extensions.md#x-examples) - specify JSON example for requests * [`x-nullable`](docs/redoc-vendor-extensions.md#nullable) - mark schema param as a nullable * [`x-displayName`](docs/redoc-vendor-extensions.md#x-displayname) - specify human-friendly names for the menu categories diff --git a/demo/openapi.yaml b/demo/openapi.yaml index 1a06f4f6..ecb9ec24 100644 --- a/demo/openapi.yaml +++ b/demo/openapi.yaml @@ -17,14 +17,14 @@ info: It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo) tool and [ReDoc](https://github.com/Rebilly/ReDoc) documentation. In addition to standard OpenAPI syntax we use a few [vendor extensions](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md). - + # OpenAPI Specification This API is documented in **OpenAPI format** and is based on [Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team. It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo) tool and [ReDoc](https://github.com/Rebilly/ReDoc) documentation. In addition to standard OpenAPI syntax we use a few [vendor extensions](https://github.com/Rebilly/ReDoc/blob/master/docs/redoc-vendor-extensions.md). - + # Cross-Origin Resource Sharing This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/). And that allows cross-domain communication from the browser. @@ -38,7 +38,7 @@ info: OAuth2 - an open protocol to allow secure authorization in a simple and standard method from web, mobile and desktop applications. - + version: 1.0.0 title: Swagger Petstore @@ -187,6 +187,27 @@ paths: description: Invalid ID supplied '404': description: Pet not found + x-code-samples: + - lang: 'Javascript' + source: | + const pet = new PetStore.v1.Pet(); + await pet.get(1, (err, pet); + x-response-samples: + - lang: 'Javascript' + source: | + Pet { + id: number; + name: String; + + getName(): String; + } + - lang: 'Javascript' + label: 'Javascript error' + source: | + ServerResponse { + status: number; + message: String; + } security: - api_key: [] post: diff --git a/docs/redoc-vendor-extensions.md b/docs/redoc-vendor-extensions.md index 062aec3c..eb3b30e6 100644 --- a/docs/redoc-vendor-extensions.md +++ b/docs/redoc-vendor-extensions.md @@ -183,15 +183,54 @@ Operation code sample ###### Code Sample Object example json ```json -{ - "lang": "JavaScript", - "source": "console.log('Hello World');" -} +[ + { + "lang": "JavaScript", + "source": "console.log('Hello World');" + } +] + ``` yaml ```yaml -lang: JavaScript -source: console.log('Hello World'); +- + lang: JavaScript + source: console.log('Hello World'); +``` + +#### x-response-samples +| Field Name | Type | Description | +| :----------------- | :------: | :---------- | +| x-response-samples | [ [Code Sample Object](#responseSampleObject) ] | A list of response samples associated with operation, useful when your SDK renders different structure of your API | + +###### Usage in ReDoc +`x-response-samples` are rendered on the right panel of ReDoc, after response tabs. + +#### Code Sample Object +Operation code sample +###### Fixed fields +| Field Name | Type | Description | +| :---------- | :------: | :----------- | +| lang | string | Code sample language. Value should be one of the following [list](https://github.com/github/linguist/blob/master/lib/linguist/popular.yml) | +| label | string? | Code sample label e.g. `Node` or `Python2.7`, _optional_, `lang` will be used by default | +| source | string | Code sample source code | + + +###### Response Sample Object example +json +```json +[ + { + "lang": "JavaScript", + "source": "console.log('Hello World');" + } +] +``` +yaml +```yaml +- + lang: JavaScript + source: console.log('Hello World'); ``` ### Parameter Object vendor extensions diff --git a/src/components/ResponseSamples/ResponseSamples.tsx b/src/components/ResponseSamples/ResponseSamples.tsx index 3f9eefa0..73bffe5f 100644 --- a/src/components/ResponseSamples/ResponseSamples.tsx +++ b/src/components/ResponseSamples/ResponseSamples.tsx @@ -5,6 +5,7 @@ import { OperationModel } from '../../services/models'; import { RightPanelHeader, Tab, TabList, TabPanel, Tabs } from '../../common-elements'; import { PayloadSamples } from '../PayloadSamples/PayloadSamples'; +import { SourceCodeWithCopy } from '../SourceCode/SourceCode'; export interface ResponseSamplesProps { operation: OperationModel; @@ -16,6 +17,8 @@ export class ResponseSamples extends React.Component { render() { const { operation } = this.props; + const samples = operation.responseSamples; + const responses = operation.responses.filter(response => { return response.content && response.content.hasSample; }); @@ -32,6 +35,11 @@ export class ResponseSamples extends React.Component { {response.code} ))} + {samples.map(sample => ( + + {sample.label || sample.lang} + + ))} {responses.map(response => ( @@ -40,6 +48,11 @@ export class ResponseSamples extends React.Component { ))} + {samples.map(sample => ( + + + + ))} )) || diff --git a/src/services/models/Operation.ts b/src/services/models/Operation.ts index de558086..74cdd7c4 100644 --- a/src/services/models/Operation.ts +++ b/src/services/models/Operation.ts @@ -9,6 +9,7 @@ import { OpenAPIPath, OpenAPIServer, OpenAPIXCodeSample, + OpenAPIXResponseSample, } from '../../types'; import { @@ -62,6 +63,7 @@ export class OperationModel implements IMenuItem { servers: OpenAPIServer[]; security: SecurityRequirementModel[]; codeSamples: OpenAPIXCodeSample[]; + responseSamples: OpenAPIXResponseSample[]; extensions: Dict; constructor( @@ -89,6 +91,7 @@ export class OperationModel implements IMenuItem { this.deprecated = !!operationSpec.deprecated; this.operationId = operationSpec.operationId; this.codeSamples = operationSpec['x-code-samples'] || []; + this.responseSamples = operationSpec['x-response-samples'] || []; this.path = operationSpec.pathName; const pathInfo = parser.byRef( diff --git a/src/types/open-api.d.ts b/src/types/open-api.d.ts index 4fb04a83..b8551f1a 100644 --- a/src/types/open-api.d.ts +++ b/src/types/open-api.d.ts @@ -63,6 +63,12 @@ export interface OpenAPIXCodeSample { source: string; } +export interface OpenAPIXResponseSample { + lang: string; + label?: string; + source: string; +} + export interface OpenAPIOperation { tags?: string[]; summary?: string; @@ -77,6 +83,7 @@ export interface OpenAPIOperation { security?: OpenAPISecurityRequirement[]; servers?: OpenAPIServer[]; 'x-code-samples'?: OpenAPIXCodeSample[]; + 'x-response-samples'?: OpenAPIXResponseSample[]; } export interface OpenAPIParameter { diff --git a/src/utils/openapi.ts b/src/utils/openapi.ts index 16c74a76..3f43764a 100644 --- a/src/utils/openapi.ts +++ b/src/utils/openapi.ts @@ -291,6 +291,7 @@ export function isRedocExtension(key: string): boolean { const redocExtensions = { 'x-circular-ref': true, 'x-code-samples': true, + 'x-response-samples': true, 'x-displayName': true, 'x-examples': true, 'x-ignoredHeaderParameters': true,