From 347bfdb6ae5c81e369b8b29f4df541249df1906e Mon Sep 17 00:00:00 2001 From: Shelby Sanders Date: Mon, 19 Oct 2020 16:43:54 -0700 Subject: [PATCH 1/3] Added support for showExtensions on Response objects --- docs/usage/configuration.md | 2 +- src/core/components/response-extension.jsx | 12 ++++++++++++ src/core/components/response.jsx | 7 ++++++- src/core/presets/base.js | 2 ++ src/style/_table.scss | 8 ++++++++ test/unit/components/response.jsx | 3 +++ 6 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 src/core/components/response-extension.jsx diff --git a/docs/usage/configuration.md b/docs/usage/configuration.md index 2204b6da58a..77740567fd6 100644 --- a/docs/usage/configuration.md +++ b/docs/usage/configuration.md @@ -56,7 +56,7 @@ Parameter name | Docker variable | Description `filter` | `FILTER` | `Boolean=false OR String`. If set, enables filtering. The top bar will show an edit box that you can use to filter the tagged operations that are shown. Can be Boolean to enable or disable, or a string, in which case filtering will be enabled using that string as the filter expression. Filtering is case sensitive matching the filter expression anywhere inside the tag. `maxDisplayedTags` | `MAX_DISPLAYED_TAGS` | `Number`. If set, limits the number of tagged operations displayed to at most this many. The default is to show all operations. `operationsSorter` | _Unavailable_ | `Function=(a => a)`. Apply a sort to the operation list of each API. It can be 'alpha' (sort by paths alphanumerically), 'method' (sort by HTTP method) or a function (see Array.prototype.sort() to know how sort function works). Default is the order returned by the server unchanged. -`showExtensions` | `SHOW_EXTENSIONS` | `Boolean=false`. Controls the display of vendor extension (`x-`) fields and values for Operations, Parameters, and Schema. +`showExtensions` | `SHOW_EXTENSIONS` | `Boolean=false`. Controls the display of vendor extension (`x-`) fields and values for Operations, Parameters, Responses, and Schema. `showCommonExtensions` | `SHOW_COMMON_EXTENSIONS` | `Boolean=false`. Controls the display of extensions (`pattern`, `maxLength`, `minLength`, `maximum`, `minimum`) fields and values for Parameters. `tagsSorter` | _Unavailable_ | `Function=(a => a)`. Apply a sort to the tag list of each API. It can be 'alpha' (sort by paths alphanumerically) or a function (see [Array.prototype.sort()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) to learn how to write a sort function). Two tag name strings are passed to the sorter for each pass. Default is the order determined by Swagger UI. `useUnsafeMarkdown` | `USE_UNSAFE_MARKDOWN` | `Boolean=false`. When enabled, sanitizer will leave `style`, `class` and `data-*` attributes untouched on all HTML Elements declared inside markdown strings. This parameter is **Deprecated** and will be removed in `4.0.0`. diff --git a/src/core/components/response-extension.jsx b/src/core/components/response-extension.jsx new file mode 100644 index 00000000000..4afb2beaf73 --- /dev/null +++ b/src/core/components/response-extension.jsx @@ -0,0 +1,12 @@ +import React from "react" +import PropTypes from "prop-types" + +export const ResponseExt = ({ xKey, xVal }) => { + return
{ xKey }: { String(xVal) }
+} +ResponseExt.propTypes = { + xKey: PropTypes.string, + xVal: PropTypes.any +} + +export default ResponseExt diff --git a/src/core/components/response.jsx b/src/core/components/response.jsx index d4c7c782b4a..160fcc69b4d 100644 --- a/src/core/components/response.jsx +++ b/src/core/components/response.jsx @@ -3,7 +3,7 @@ import PropTypes from "prop-types" import ImPropTypes from "react-immutable-proptypes" import cx from "classnames" import { fromJS, Seq, Iterable, List, Map } from "immutable" -import { getSampleSchema, fromJSOrdered, stringify } from "core/utils" +import { getExtensions, getSampleSchema, fromJSOrdered, stringify } from "core/utils" import { isFunc } from "../utils" const getExampleComponent = ( sampleResponse, HighlightCode, getConfigs ) => { @@ -88,9 +88,12 @@ export default class Response extends React.Component { let { inferSchema } = fn let isOAS3 = specSelectors.isOAS3() + const { showExtensions } = getConfigs() + let extensions = showExtensions ? getExtensions(response) : null let headers = response.get("headers") let links = response.get("links") + const ResponseExt = getComponent("ResponseExt") const Headers = getComponent("headers") const HighlightCode = getComponent("highlightCode") const ModelExample = getComponent("modelExample") @@ -188,6 +191,8 @@ export default class Response extends React.Component { + { !showExtensions || !extensions.size ? null : extensions.map((v, key) => )} + {isOAS3 && response.get("content") ? (
", function () { } const props = { getComponent: c => components[c], + getConfigs: () => { + return {} + }, specSelectors: { isOAS3() { return false From c98be57ed5d85dbeb0bd38affe5209c6da92d1fb Mon Sep 17 00:00:00 2001 From: Shelby Sanders Date: Tue, 20 Oct 2020 17:41:04 -0700 Subject: [PATCH 2/3] Rename ResponseExt to ResponseExtension --- src/core/components/response-extension.jsx | 6 +++--- src/core/components/response.jsx | 4 ++-- src/core/presets/base.js | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/components/response-extension.jsx b/src/core/components/response-extension.jsx index 4afb2beaf73..e8f1b039693 100644 --- a/src/core/components/response-extension.jsx +++ b/src/core/components/response-extension.jsx @@ -1,12 +1,12 @@ import React from "react" import PropTypes from "prop-types" -export const ResponseExt = ({ xKey, xVal }) => { +export const ResponseExtension = ({ xKey, xVal }) => { return
{ xKey }: { String(xVal) }
} -ResponseExt.propTypes = { +ResponseExtension.propTypes = { xKey: PropTypes.string, xVal: PropTypes.any } -export default ResponseExt +export default ResponseExtension diff --git a/src/core/components/response.jsx b/src/core/components/response.jsx index 160fcc69b4d..548b6dcfb38 100644 --- a/src/core/components/response.jsx +++ b/src/core/components/response.jsx @@ -93,7 +93,7 @@ export default class Response extends React.Component { let extensions = showExtensions ? getExtensions(response) : null let headers = response.get("headers") let links = response.get("links") - const ResponseExt = getComponent("ResponseExt") + const ResponseExtension = getComponent("ResponseExtension") const Headers = getComponent("headers") const HighlightCode = getComponent("highlightCode") const ModelExample = getComponent("modelExample") @@ -191,7 +191,7 @@ export default class Response extends React.Component {
- { !showExtensions || !extensions.size ? null : extensions.map((v, key) => )} + { !showExtensions || !extensions.size ? null : extensions.map((v, key) => )} {isOAS3 && response.get("content") ? (
diff --git a/src/core/presets/base.js b/src/core/presets/base.js index c96a457854a..73565cdf7cd 100644 --- a/src/core/presets/base.js +++ b/src/core/presets/base.js @@ -43,7 +43,7 @@ import OperationExtRow from "core/components/operation-extension-row" import HighlightCode from "core/components/highlight-code" import Responses from "core/components/responses" import Response from "core/components/response" -import ResponseExt from "core/components/response-extension" +import ResponseExtension from "core/components/response-extension" import ResponseBody from "core/components/response-body" import { Parameters } from "core/components/parameters" import ParameterExt from "core/components/parameter-extension" @@ -120,7 +120,7 @@ export default function() { highlightCode: HighlightCode, responses: Responses, response: Response, - ResponseExt: ResponseExt, + ResponseExtension: ResponseExtension, responseBody: ResponseBody, parameters: Parameters, parameterRow: ParameterRow, From 47a846cde9b4a8c262e4e076c603e51e8d7e5af8 Mon Sep 17 00:00:00 2001 From: Shelby Sanders Date: Tue, 20 Oct 2020 20:34:01 -0700 Subject: [PATCH 3/3] Added Cypress tests for Response Extensions --- .../features/response-extension.openapi.yaml | 19 +++++++ .../features/response-extension.swagger.yaml | 15 ++++++ .../tests/features/response-extension.js | 49 +++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 test/e2e-cypress/static/documents/features/response-extension.openapi.yaml create mode 100644 test/e2e-cypress/static/documents/features/response-extension.swagger.yaml create mode 100644 test/e2e-cypress/tests/features/response-extension.js diff --git a/test/e2e-cypress/static/documents/features/response-extension.openapi.yaml b/test/e2e-cypress/static/documents/features/response-extension.openapi.yaml new file mode 100644 index 00000000000..31e1da11865 --- /dev/null +++ b/test/e2e-cypress/static/documents/features/response-extension.openapi.yaml @@ -0,0 +1,19 @@ +openapi: "3.0.0" + +paths: + /: + get: + operationId: "myOperation" + tags: ["myTag"] + summary: an operation + responses: + '200': + description: a pet to be returned + content: + application/json: + schema: + type: object + '404': + x-error: true + x-error-codes: [NOT_FOUND] + description: Not found diff --git a/test/e2e-cypress/static/documents/features/response-extension.swagger.yaml b/test/e2e-cypress/static/documents/features/response-extension.swagger.yaml new file mode 100644 index 00000000000..a25c8f1c184 --- /dev/null +++ b/test/e2e-cypress/static/documents/features/response-extension.swagger.yaml @@ -0,0 +1,15 @@ +swagger: "2.0" + +paths: + /: + get: + operationId: "myOperation" + tags: ["myTag"] + summary: an operation + responses: + "200": + description: ok + '404': + x-error: true + x-error-codes: [NOT_FOUND] + description: Not found diff --git a/test/e2e-cypress/tests/features/response-extension.js b/test/e2e-cypress/tests/features/response-extension.js new file mode 100644 index 00000000000..c1b8553949b --- /dev/null +++ b/test/e2e-cypress/tests/features/response-extension.js @@ -0,0 +1,49 @@ +describe("Response extension feature", () => { + describe("in Swagger 2", () => { + const swagger2BaseUrl = "/?showExtensions=true&docExpansion=full&url=/documents/features/response-extension.swagger.yaml" + + describe("without x- values", () => { + it("should omit response extensions section", () => { + cy.visit(swagger2BaseUrl) + .get("tr.response[data-code='200'] td.response-col_description div.response__extension") + .should("not.exist") + }) + }) + + describe("with x- values", () => { + it("should list each value", () => { + const page = cy.visit(swagger2BaseUrl) + + page.get("tr.response[data-code='404'] td.response-col_description div.response__extension:nth-child(2)") + .should("have.text", "x-error: true") + + page.get("tr.response[data-code='404'] td.response-col_description div.response__extension:nth-child(3)") + .should("have.text", "x-error-codes: List [ \"NOT_FOUND\" ]") + }) + }) + }) + + describe("in OpenAPI 3", () => { + const openAPI3BaseUrl = "/?showExtensions=true&docExpansion=full&url=/documents/features/response-extension.openapi.yaml" + + describe("without x- values", () => { + it("should omit response extensions section", () => { + cy.visit(openAPI3BaseUrl) + .get("tr.response[data-code='200'] td.response-col_description div.response__extension") + .should("not.exist") + }) + }) + + describe("with x- values", () => { + it("should list each value", () => { + const page = cy.visit(openAPI3BaseUrl) + + page.get("tr.response[data-code='404'] td.response-col_description div.response__extension:nth-child(2)") + .should("have.text", "x-error: true") + + page.get("tr.response[data-code='404'] td.response-col_description div.response__extension:nth-child(3)") + .should("have.text", "x-error-codes: List [ \"NOT_FOUND\" ]") + }) + }) + }) +})