From 4480db0390f69b68863ad1d2465c544b24eebd4e Mon Sep 17 00:00:00 2001 From: kyle Date: Wed, 4 Apr 2018 18:55:10 -0700 Subject: [PATCH 1/4] Use non-normalized operationId for layout management, if it is available (#4410) * test(e2e): modify e2e hot server to peacefully coexist with dev server * tests: add failing e2e test case * fix: use originalOperationId for layout targeting, if available --- package.json | 2 +- src/core/components/operation.jsx | 3 ++- src/core/containers/OperationContainer.jsx | 3 ++- test/e2e/scenarios/bugs/4409.js | 29 ++++++++++++++++++++++ test/e2e/scenarios/informationContainer.js | 8 +++--- test/e2e/scenarios/models.js | 4 +-- test/e2e/scenarios/oas3/callbacks.js | 4 +-- test/e2e/scenarios/oas3/pet.js | 4 +-- test/e2e/scenarios/on-complete.js | 2 +- test/e2e/scenarios/operations/pet.js | 4 +-- test/e2e/scenarios/operations/store.js | 4 +-- test/e2e/scenarios/operations/user.js | 4 +-- test/e2e/scenarios/refs.js | 4 +-- test/e2e/scenarios/schemeContainer.js | 4 +-- test/e2e/scenarios/topbar.js | 4 +-- test/e2e/specs/bugs/4409.yaml | 24 ++++++++++++++++++ 16 files changed, 81 insertions(+), 26 deletions(-) create mode 100644 test/e2e/scenarios/bugs/4409.js create mode 100644 test/e2e/specs/bugs/4409.yaml diff --git a/package.json b/package.json index 9c138098..cb16c010 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "test-e2e": "sleep 3 && nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch.json", "e2e-initial-render": "nightwatch test/e2e/scenarios/ --config test/e2e/nightwatch.json --group initial-render", "mock-api": "json-server --watch test/e2e/db.json --port 3204", - "hot-e2e-server": "webpack-dev-server --content-base test/e2e/helpers --host 0.0.0.0 --config webpack-hot-dev-server.config.js --inline --hot --progress", + "hot-e2e-server": "webpack-dev-server --port 3230 --content-base test/e2e/helpers --host 0.0.0.0 --config webpack-hot-dev-server.config.js --inline --hot --progress", "e2e": "npm-run-all --parallel -r hot-e2e-server mock-api test-e2e" }, "dependencies": { diff --git a/src/core/components/operation.jsx b/src/core/components/operation.jsx index f04c070e..e58047d8 100644 --- a/src/core/components/operation.jsx +++ b/src/core/components/operation.jsx @@ -72,6 +72,7 @@ export default class Operation extends PureComponent { tag, showSummary, operationId, + originalOperationId, allowTryItOut, displayOperationId, displayRequestDuration, @@ -140,7 +141,7 @@ export default class Operation extends PureComponent { } - { displayOperationId && operationId ? {operationId} : null } + { displayOperationId && (originalOperationId || operationId) ? {originalOperationId || operationId} : null } { (!security || !security.count()) ? null : diff --git a/src/core/containers/OperationContainer.jsx b/src/core/containers/OperationContainer.jsx index 687182dd..06ab130b 100644 --- a/src/core/containers/OperationContainer.jsx +++ b/src/core/containers/OperationContainer.jsx @@ -58,7 +58,7 @@ export default class OperationContainer extends PureComponent { const { op, layoutSelectors, getConfigs } = props const { docExpansion, deepLinking, displayOperationId, displayRequestDuration, supportedSubmitMethods } = getConfigs() const showSummary = layoutSelectors.showSummary() - const operationId = op.getIn(["operation", "operationId"]) || op.getIn(["operation", "__originalOperationId"]) || opId(op.get("operation"), props.path, props.method) || op.get("id") + const operationId = op.getIn(["operation", "__originalOperationId"]) || op.getIn(["operation", "operationId"]) || opId(op.get("operation"), props.path, props.method) || op.get("id") const isShownKey = ["operations", props.tag, operationId] const isDeepLinkingEnabled = deepLinking && deepLinking !== "false" const allowTryItOut = supportedSubmitMethods.indexOf(props.method) >= 0 && (typeof props.allowTryItOut === "undefined" ? @@ -204,6 +204,7 @@ export default class OperationContainer extends PureComponent { security, isAuthorized, operationId, + originalOperationId: resolvedSubtree.getIn(["operation", "__originalOperationId"]), showSummary, isShown, jumpToKey, diff --git a/test/e2e/scenarios/bugs/4409.js b/test/e2e/scenarios/bugs/4409.js new file mode 100644 index 00000000..74c4069e --- /dev/null +++ b/test/e2e/scenarios/bugs/4409.js @@ -0,0 +1,29 @@ +describe("bug #4409: operationId normalization and layout tracking", function () { + let mainPage + beforeEach(function (client, done) { + mainPage = client + .url("localhost:3230") + .page.main() + + client.waitForElementVisible(".download-url-input", 5000) + .pause(5000) + .clearValue(".download-url-input") + .setValue(".download-url-input", "http://localhost:3230/test-specs/bugs/4409.yaml") + .click("button.download-url-button") + .pause(1000) + + done() + }) + afterEach(function (client, done) { + done() + }) + it("expands an operation that has a normalizable operationId", function (client) { + client.waitForElementVisible(".opblock-tag-section", 10000) + .assert.containsText(".opblock-summary-path span", "/myApi") + .click(".opblock") + .waitForElementVisible(".opblock-body", 5000) + .assert.cssClassPresent(".opblock", "is-open") + + client.end() + }) +}) diff --git a/test/e2e/scenarios/informationContainer.js b/test/e2e/scenarios/informationContainer.js index c5af4148..2d1b1748 100644 --- a/test/e2e/scenarios/informationContainer.js +++ b/test/e2e/scenarios/informationContainer.js @@ -4,12 +4,12 @@ describe("render informationContainer", function () { beforeEach(function (client, done) { mainPage = client - .url("localhost:3200") + .url("localhost:3230") .page.main() client.waitForElementVisible(".download-url-input", 5000) .pause(5000) .clearValue(".download-url-input") - .setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json") + .setValue(".download-url-input", "http://localhost:3230/test-specs/petstore.json") .click("button.download-url-button") .pause(1000) @@ -29,8 +29,8 @@ describe("render informationContainer", function () { .assert.containsText("@title", "Swagger Petstore") .assert.containsText("@version", "1.0.0") .assert.containsText("@baseUrl", "[ Base URL: localhost:3204/ ]") - .assert.attributeEquals("@mainUrl", "href", "http://localhost:3200/test-specs/petstore.json") - .assert.containsText("@mainUrlContent", "http://localhost:3200/test-specs/petstore.json") + .assert.attributeEquals("@mainUrl", "href", "http://localhost:3230/test-specs/petstore.json") + .assert.containsText("@mainUrlContent", "http://localhost:3230/test-specs/petstore.json") .assert.containsText("@description", "This is a sample server Petstore server. You can find out more about Swagger at http://swagger.io or on irc.freenode.net, #swagger. For this sample, you can use the api key special-key to test the authorization filters.") .assert.attributeEquals("@swaggerUrl", "href", "http://swagger.io/") .assert.attributeEquals("@swaggerircUrl", "href", "http://swagger.io/irc/") diff --git a/test/e2e/scenarios/models.js b/test/e2e/scenarios/models.js index 9749b811..ce2ac5ba 100644 --- a/test/e2e/scenarios/models.js +++ b/test/e2e/scenarios/models.js @@ -3,12 +3,12 @@ describe("Render Model Wrapper", function () { beforeEach(function (client, done) { mainPage = client - .url("localhost:3200") + .url("localhost:3230") .page.main() client.waitForElementVisible(".download-url-input", 5000) .pause(5000) .clearValue(".download-url-input") - .setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json") + .setValue(".download-url-input", "http://localhost:3230/test-specs/petstore.json") .click("button.download-url-button") .pause(1000) diff --git a/test/e2e/scenarios/oas3/callbacks.js b/test/e2e/scenarios/oas3/callbacks.js index 4f06d3f0..3615fe28 100644 --- a/test/e2e/scenarios/oas3/callbacks.js +++ b/test/e2e/scenarios/oas3/callbacks.js @@ -3,13 +3,13 @@ describe("render pet api container", function () { let apiWrapper beforeEach(function (client, done) { mainPage = client - .url("localhost:3200") + .url("localhost:3230") .page.main() client.waitForElementVisible(".download-url-input", 5000) .pause(5000) .clearValue(".download-url-input") - .setValue(".download-url-input", "http://localhost:3200/test-specs/callbacks.openapi.yaml") + .setValue(".download-url-input", "http://localhost:3230/test-specs/callbacks.openapi.yaml") .click("button.download-url-button") .pause(1000) diff --git a/test/e2e/scenarios/oas3/pet.js b/test/e2e/scenarios/oas3/pet.js index 267b7bc9..6dc20a2c 100644 --- a/test/e2e/scenarios/oas3/pet.js +++ b/test/e2e/scenarios/oas3/pet.js @@ -3,13 +3,13 @@ describe("render pet api container", function () { let apiWrapper beforeEach(function (client, done) { mainPage = client - .url("localhost:3200") + .url("localhost:3230") .page.main() client.waitForElementVisible(".download-url-input", 5000) .pause(5000) .clearValue(".download-url-input") - .setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.openapi.yaml") + .setValue(".download-url-input", "http://localhost:3230/test-specs/petstore.openapi.yaml") .click("button.download-url-button") .pause(1000) diff --git a/test/e2e/scenarios/on-complete.js b/test/e2e/scenarios/on-complete.js index d32cd089..d7e44ad2 100644 --- a/test/e2e/scenarios/on-complete.js +++ b/test/e2e/scenarios/on-complete.js @@ -4,7 +4,7 @@ describe("onComplete option", function () { let mainPage beforeEach(function (client, done) { mainPage = client - .url("localhost:3200") + .url("localhost:3230") .page.main() client.waitForElementVisible(".opblock-tag-section", 5000) diff --git a/test/e2e/scenarios/operations/pet.js b/test/e2e/scenarios/operations/pet.js index 337b5877..5d074b1a 100644 --- a/test/e2e/scenarios/operations/pet.js +++ b/test/e2e/scenarios/operations/pet.js @@ -3,13 +3,13 @@ describe("render pet api container", function () { let apiWrapper beforeEach(function (client, done) { mainPage = client - .url("localhost:3200") + .url("localhost:3230") .page.main() client.waitForElementVisible(".download-url-input", 5000) .pause(5000) .clearValue(".download-url-input") - .setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json") + .setValue(".download-url-input", "http://localhost:3230/test-specs/petstore.json") .click("button.download-url-button") .pause(1000) diff --git a/test/e2e/scenarios/operations/store.js b/test/e2e/scenarios/operations/store.js index 7dbcbc24..c76fb1dd 100644 --- a/test/e2e/scenarios/operations/store.js +++ b/test/e2e/scenarios/operations/store.js @@ -3,13 +3,13 @@ describe("render store api container", function(){ let apiWrapper beforeEach( function(client, done){ mainPage = client - .url("localhost:3200") + .url("localhost:3230") .page.main() client.waitForElementVisible(".download-url-input", 5000) .pause(3000) .clearValue(".download-url-input") - .setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json") + .setValue(".download-url-input", "http://localhost:3230/test-specs/petstore.json") .click("button.download-url-button") .pause(1000) diff --git a/test/e2e/scenarios/operations/user.js b/test/e2e/scenarios/operations/user.js index e84984b7..760150dc 100644 --- a/test/e2e/scenarios/operations/user.js +++ b/test/e2e/scenarios/operations/user.js @@ -3,13 +3,13 @@ describe("render user api container", function(){ let apiWrapper beforeEach( function(client, done){ mainPage = client - .url("localhost:3200") + .url("localhost:3230") .page.main() client.waitForElementVisible(".download-url-input", 5000) .pause(5000) .clearValue(".download-url-input") - .setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json") + .setValue(".download-url-input", "http://localhost:3230/test-specs/petstore.json") .click("button.download-url-button") .pause(1000) diff --git a/test/e2e/scenarios/refs.js b/test/e2e/scenarios/refs.js index ec6a2043..e8296507 100644 --- a/test/e2e/scenarios/refs.js +++ b/test/e2e/scenarios/refs.js @@ -4,13 +4,13 @@ describe("Remote $ref rendering", function () { mainPage = client // expand the models so we don't have to manually do it - .url("localhost:3200?defaultModelsExpandDepth=5") + .url("localhost:3230?defaultModelsExpandDepth=5") .page.main() client.waitForElementVisible(".download-url-input", 5000) .pause(2000) .clearValue(".download-url-input") - .setValue(".download-url-input", "http://localhost:3200/test-specs/refs/api1.yaml") + .setValue(".download-url-input", "http://localhost:3230/test-specs/refs/api1.yaml") .click("button.download-url-button") .pause(1000) diff --git a/test/e2e/scenarios/schemeContainer.js b/test/e2e/scenarios/schemeContainer.js index 51a309b1..18f83f4c 100644 --- a/test/e2e/scenarios/schemeContainer.js +++ b/test/e2e/scenarios/schemeContainer.js @@ -4,7 +4,7 @@ describe("Render scheme", function () { beforeEach(function (client, done) { mainPage = client - .url("localhost:3200") + .url("localhost:3230") .page.main() schemeContainer = mainPage.section.schemeContainer @@ -12,7 +12,7 @@ describe("Render scheme", function () { client.waitForElementVisible(".download-url-input", 5000) .pause(5000) .clearValue(".download-url-input") - .setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json") + .setValue(".download-url-input", "http://localhost:3230/test-specs/petstore.json") .click("button.download-url-button") .pause(1000) diff --git a/test/e2e/scenarios/topbar.js b/test/e2e/scenarios/topbar.js index d7a17efe..7410b91f 100644 --- a/test/e2e/scenarios/topbar.js +++ b/test/e2e/scenarios/topbar.js @@ -18,7 +18,7 @@ describe("initial render", function () { beforeEach(function (client, done) { mainPage = client - .url("localhost:3200") + .url("localhost:3230") .page.main() topbar = mainPage.section.topbar @@ -26,7 +26,7 @@ describe("initial render", function () { client.waitForElementVisible(".download-url-input", 10000) .pause(5000) .clearValue(".download-url-input") - .setValue(".download-url-input", "http://localhost:3200/test-specs/petstore.json") + .setValue(".download-url-input", "http://localhost:3230/test-specs/petstore.json") .click("button.download-url-button") .pause(1000) diff --git a/test/e2e/specs/bugs/4409.yaml b/test/e2e/specs/bugs/4409.yaml new file mode 100644 index 00000000..fe398840 --- /dev/null +++ b/test/e2e/specs/bugs/4409.yaml @@ -0,0 +1,24 @@ +openapi: 3.0.0 +servers: + - url: 'https://__host__/' +info: + description: Test API + version: '1.0' + title: Test API + contact: + name: Sample Person + email: Sample.Person@adp.com +tags: + - name: Test API with Schema + description: Test API with Schema +paths: + /myApi: + get: + operationId: some guid + responses: + '200': + description: Returns documents + description: Returns documents + tags: + - Test API with Schema + summary: Test API with Schema From d90353228a844c5fd41de83ac9a4d5ac18a27cad Mon Sep 17 00:00:00 2001 From: kyle Date: Thu, 5 Apr 2018 15:30:31 -0700 Subject: [PATCH 2/4] pass meta error information to JsonSchemaForm correctly (#4416) * tests: add failing e2e tests * fix: pass meta error information to JsonSchemaForm correctly --- src/core/components/parameter-row.jsx | 2 +- test/e2e/scenarios/bugs/4374.js | 32 ++++ test/e2e/specs/bugs/4374.yaml | 228 ++++++++++++++++++++++++++ 3 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 test/e2e/scenarios/bugs/4374.js create mode 100644 test/e2e/specs/bugs/4374.yaml diff --git a/src/core/components/parameter-row.jsx b/src/core/components/parameter-row.jsx index 8ed25ecc..d935e91e 100644 --- a/src/core/components/parameter-row.jsx +++ b/src/core/components/parameter-row.jsx @@ -188,7 +188,7 @@ export default class ParameterRow extends Component { required={ required } description={param.get("description") ? `${param.get("name")} - ${param.get("description")}` : `${param.get("name")}`} onChange={ this.onChangeWrapper } - errors={ param.get("errors") } + errors={ paramWithMeta.get("errors") } schema={ isOAS3 && isOAS3() ? param.get("schema") : param }/> } diff --git a/test/e2e/scenarios/bugs/4374.js b/test/e2e/scenarios/bugs/4374.js new file mode 100644 index 00000000..601a6d91 --- /dev/null +++ b/test/e2e/scenarios/bugs/4374.js @@ -0,0 +1,32 @@ +describe("bug #4374: OAS3 parameters should be visibly validated in Try-It-Out", function () { + let mainPage + beforeEach(function (client, done) { + mainPage = client + .url("localhost:3230") + .page.main() + + client.waitForElementVisible(".download-url-input", 10000) + .pause(1000) + .clearValue(".download-url-input") + .setValue(".download-url-input", "http://localhost:3230/test-specs/bugs/4374.yaml") + .click("button.download-url-button") + .pause(1000) + + done() + }) + afterEach(function (client, done) { + done() + }) + it("indicates an error when a required parameter is not selected", function (client) { + client.waitForElementVisible(".opblock-tag-section", 10000) + .assert.containsText(".opblock-summary-path span", "/pet/findByStatus") + .click(".opblock") + .waitForElementVisible(".opblock.is-open", 5000) + .click(".try-out__btn") + .click(".btn.execute") + .pause(100) + .assert.cssClassPresent(".parameters-col_description select", "invalid") + + client.end() + }) +}) diff --git a/test/e2e/specs/bugs/4374.yaml b/test/e2e/specs/bugs/4374.yaml new file mode 100644 index 00000000..61ecf810 --- /dev/null +++ b/test/e2e/specs/bugs/4374.yaml @@ -0,0 +1,228 @@ +--- +openapi: 3.0.0 +servers: +- url: http://localhost:3204/ +info: + description: 'This is a sample server Petstore server. You can find out more about + Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For + this sample, you can use the api key `special-key` to test the authorization filters.' + version: 1.0.0 + title: Swagger Petstore + termsOfService: http://swagger.io/terms/ + contact: + email: apiteam@swagger.io + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html +tags: +- name: pet + description: Everything about your Pets + externalDocs: + description: Find out more + url: http://swagger.io +- name: store + description: Access to Petstore orders +- name: user + description: Operations about user + externalDocs: + description: Find out more about our store + url: http://swagger.io +paths: + "/pet/findByStatus": + get: + tags: + - pet + summary: Finds Pets by status + description: Multiple status values can be provided with comma separated strings + operationId: findPetsByStatus + parameters: + - name: status + in: query + description: Status values that need to be considered for filter + required: true + explode: true + schema: + type: array + items: + type: string + enum: + - available + - pending + - sold + default: available + responses: + '200': + description: successful operation + content: + application/xml: + schema: + type: array + items: + "$ref": "#/components/schemas/Pet" + application/json: + schema: + type: array + items: + "$ref": "#/components/schemas/Pet" + '400': + description: Invalid status value + security: + - petstore_auth: + - write:pets + - read:pets +externalDocs: + description: Find out more about Swagger + url: http://swagger.io +components: + schemas: + Order: + type: object + properties: + id: + type: integer + format: int64 + petId: + type: integer + format: int64 + quantity: + type: integer + format: int32 + shipDate: + type: string + format: date-time + status: + type: string + description: Order Status + enum: + - placed + - approved + - delivered + complete: + type: boolean + default: false + xml: + name: Order + Category: + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Category + User: + type: object + properties: + id: + type: integer + format: int64 + username: + type: string + firstName: + type: string + lastName: + type: string + email: + type: string + password: + type: string + phone: + type: string + userStatus: + type: integer + format: int32 + description: User Status + xml: + name: User + Tag: + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Tag + Pet: + type: object + required: + - name + - photoUrls + properties: + id: + type: integer + format: int64 + category: + "$ref": "#/components/schemas/Category" + name: + type: string + example: doggie + photoUrls: + type: array + xml: + name: photoUrl + wrapped: true + items: + type: string + tags: + type: array + xml: + name: tag + wrapped: true + items: + "$ref": "#/components/schemas/Tag" + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: Pet + ApiResponse: + type: object + properties: + code: + type: integer + format: int32 + type: + type: string + message: + type: string + requestBodies: + Pet: + content: + application/json: + schema: + "$ref": "#/components/schemas/Pet" + application/xml: + schema: + "$ref": "#/components/schemas/Pet" + description: Pet object that needs to be added to the store + required: true + UserArray: + content: + application/json: + schema: + type: array + items: + "$ref": "#/components/schemas/User" + description: List of user object + required: true + securitySchemes: + petstore_auth: + type: oauth2 + flows: + implicit: + authorizationUrl: http://petstore.swagger.io/oauth/dialog + scopes: + write:pets: modify pets in your account + read:pets: read your pets + api_key: + type: apiKey + name: api_key + in: header From 39d34523b98bb6223f1654087265d9bdbd362281 Mon Sep 17 00:00:00 2001 From: David DE CARVALHO Date: Fri, 6 Apr 2018 03:48:19 +0200 Subject: [PATCH 3/4] Improve enum values for Enum Type in Swagger ReadOnly documentation (#4191) * Adding enum values for Enum Type in Swagger ReadOnly documentation * Adding enum values for Enum Type in Swagger ReadOnly documentation (optimisation) and also adding default/example value * Add new display enums, defaults, and examples when not in TIO mode (another way to have enums values in swagger.json) * Fix npm test result * review corrections * fix: don't render parameter description if field is empty * use cross-version schema variable to access properties * pass className through Markdown component usage * add per-field classNames to Markdown for easier styling + testing * remove parameter Example field (out-of-scope for this PR) * get default value from schema instead of top-level parameter * tests: add e2e cases for swagger2 and oas3 * remove `swagger-petstore-enum.json` the purpose of this file lives on in the e2e test specs folder * add missing proptypes validation * use `classnames` to more effectively union class names --- src/core/components/parameter-row.jsx | 58 +++++++++---------- src/core/components/providers/markdown.jsx | 8 ++- .../plugins/oas3/wrap-components/markdown.js | 10 ++-- .../features/parameter-enum-rendering.js | 58 +++++++++++++++++++ .../parameter-enum-rendering.openapi.yaml | 21 +++++++ .../parameter-enum-rendering.swagger.yaml | 20 +++++++ 6 files changed, 138 insertions(+), 37 deletions(-) create mode 100644 test/e2e/scenarios/features/parameter-enum-rendering.js create mode 100644 test/e2e/specs/features/parameter-enum-rendering.openapi.yaml create mode 100644 test/e2e/specs/features/parameter-enum-rendering.swagger.yaml diff --git a/src/core/components/parameter-row.jsx b/src/core/components/parameter-row.jsx index d935e91e..f6e3860e 100644 --- a/src/core/components/parameter-row.jsx +++ b/src/core/components/parameter-row.jsx @@ -107,40 +107,43 @@ export default class ParameterRow extends Component { let paramWithMeta = specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in")) - let schema = param.get("schema") - let type = isOAS3 && isOAS3() ? param.getIn(["schema", "type"]) : param.get("type") + let schema = isOAS3 && isOAS3() ? param.get("schema") : param + let type = schema.get("type") let isFormData = inType === "formData" let isFormDataSupported = "FormData" in win let required = param.get("required") - let itemType = param.getIn(isOAS3 && isOAS3() ? ["schema", "items", "type"] : ["items", "type"]) + let itemType = schema.getIn(["items", "type"]) let value = paramWithMeta ? paramWithMeta.get("value") : "" let extensions = getExtensions(param) let paramItems // undefined - let paramItemsEnum // undefined - let isDisplayParamItemsEnum = false + let paramEnum // undefined + let paramDefaultValue // undefined + let paramExample // undefined + let isDisplayParamEnum = false + if ( param !== undefined ) { - paramItems = param.get("items") + paramItems = schema.get("items") } - if ( paramItems !== undefined ) { - paramItemsEnum = param.get("items").get("enum") + + if (paramItems !== undefined) { + paramEnum = paramItems.get("enum") + paramDefaultValue = paramItems.get("default") + } else { + paramEnum = schema.get("enum") } - if ( paramItemsEnum !== undefined ) { - if (paramItemsEnum.size > 0) { - isDisplayParamItemsEnum = true - } + + if ( paramEnum !== undefined && paramEnum.size > 0) { + isDisplayParamEnum = true } // Default and Example Value for readonly doc - let paramDefaultValue // undefined - let paramExample // undefined if ( param !== undefined ) { - paramDefaultValue = param.get("default") + paramDefaultValue = schema.get("default") paramExample = param.get("example") - } - - if (isDisplayParamItemsEnum) { // if we have an array, default value is in "items" - paramDefaultValue = paramItems.get("default") + if (paramExample === undefined) { + paramExample = param.get("x-example") + } } return ( @@ -159,23 +162,18 @@ export default class ParameterRow extends Component { - + { param.get("description") ? : null } - { (bodyParam || !isExecute) && isDisplayParamItemsEnum ? - Available values: " + paramItemsEnum.map(function(item) { + { (bodyParam || !isExecute) && isDisplayParamEnum ? + Available values : " + paramEnum.map(function(item) { return item }).toArray().join(", ")}/> : null } { (bodyParam || !isExecute) && paramDefaultValue !== undefined ? - Default value: " + paramDefaultValue}/> - : null - } - - { (bodyParam || !isExecute) && paramExample !== undefined ? - Example: " + paramExample}/> + Default value : " + paramDefaultValue}/> : null } @@ -189,7 +187,7 @@ export default class ParameterRow extends Component { description={param.get("description") ? `${param.get("name")} - ${param.get("description")}` : `${param.get("name")}`} onChange={ this.onChangeWrapper } errors={ paramWithMeta.get("errors") } - schema={ isOAS3 && isOAS3() ? param.get("schema") : param }/> + schema={ schema }/> } diff --git a/src/core/components/providers/markdown.jsx b/src/core/components/providers/markdown.jsx index 9707ece6..a4bca726 100644 --- a/src/core/components/providers/markdown.jsx +++ b/src/core/components/providers/markdown.jsx @@ -2,11 +2,12 @@ import React from "react" import PropTypes from "prop-types" import Remarkable from "remarkable" import sanitize from "sanitize-html" +import cx from "classnames" // eslint-disable-next-line no-useless-escape const isPlainText = (str) => /^[A-Z\s0-9!?\.]+$/gi.test(str) -function Markdown({ source }) { +function Markdown({ source, className = "" }) { if(isPlainText(source)) { // If the source text is not Markdown, // let's save some time and just render it. @@ -28,12 +29,13 @@ function Markdown({ source }) { } return ( -
+
) } Markdown.propTypes = { - source: PropTypes.string.isRequired + source: PropTypes.string.isRequired, + className: PropTypes.string.isRequired } export default Markdown diff --git a/src/core/plugins/oas3/wrap-components/markdown.js b/src/core/plugins/oas3/wrap-components/markdown.js index 2d7f27e7..9bfdffb9 100644 --- a/src/core/plugins/oas3/wrap-components/markdown.js +++ b/src/core/plugins/oas3/wrap-components/markdown.js @@ -1,11 +1,12 @@ import React from "react" import PropTypes from "prop-types" import ReactMarkdown from "react-markdown" +import cx from "classnames" import { Parser, HtmlRenderer } from "commonmark" import { OAS3ComponentWrapFactory } from "../helpers" import { sanitizer } from "core/components/providers/markdown" -export const Markdown = ({ source }) => { +export const Markdown = ({ source, className = "" }) => { if ( source ) { const parser = new Parser() const writer = new HtmlRenderer() @@ -19,14 +20,15 @@ export const Markdown = ({ source }) => { return ( ) } return null } Markdown.propTypes = { - source: PropTypes.string + source: PropTypes.string, + className: PropTypes.string, } -export default OAS3ComponentWrapFactory(Markdown) \ No newline at end of file +export default OAS3ComponentWrapFactory(Markdown) diff --git a/test/e2e/scenarios/features/parameter-enum-rendering.js b/test/e2e/scenarios/features/parameter-enum-rendering.js new file mode 100644 index 00000000..e478794d --- /dev/null +++ b/test/e2e/scenarios/features/parameter-enum-rendering.js @@ -0,0 +1,58 @@ +describe("parameter enum rendering", function () { + describe("swagger 2.0", () => { + beforeEach(function (client, done) { + client + .url("localhost:3230") + .waitForElementVisible(".download-url-input", 10000) + .pause(1000) + .clearValue(".download-url-input") + .setValue(".download-url-input", "http://localhost:3230/test-specs/features/parameter-enum-rendering.swagger.yaml") + .click("button.download-url-button") + .pause(1000) + + done() + }) + afterEach(function (client, done) { + done() + }) + it("reveals a string parameter's enums and defaults when viewing that parameter", function (client) { + client.waitForElementVisible(".opblock-tag-section", 10000) + .assert.containsText(".opblock-summary-path span", "/report") + .click(".opblock") + .waitForElementVisible(".opblock.is-open", 5000) + .pause(500) + .assert.containsText("div.parameter__enum", "today, yesterday, lastweek") + .assert.containsText("div.parameter__default", "today") + + client.end() + }) + }) + describe("openapi 3.0", () => { + beforeEach(function (client, done) { + client + .url("localhost:3230") + .waitForElementVisible(".download-url-input", 10000) + .pause(1000) + .clearValue(".download-url-input") + .setValue(".download-url-input", "http://localhost:3230/test-specs/features/parameter-enum-rendering.openapi.yaml") + .click("button.download-url-button") + .pause(1000) + + done() + }) + afterEach(function (client, done) { + done() + }) + it("reveals a string parameter's enums and defaults when viewing that parameter", function (client) { + client.waitForElementVisible(".opblock-tag-section", 10000) + .assert.containsText(".opblock-summary-path span", "/report") + .click(".opblock") + .waitForElementVisible(".opblock.is-open", 5000) + .pause(500) + .assert.containsText("div.parameter__enum", "today, yesterday, lastweek") + .assert.containsText("div.parameter__default", "today") + + client.end() + }) + }) +}) diff --git a/test/e2e/specs/features/parameter-enum-rendering.openapi.yaml b/test/e2e/specs/features/parameter-enum-rendering.openapi.yaml new file mode 100644 index 00000000..444cf19c --- /dev/null +++ b/test/e2e/specs/features/parameter-enum-rendering.openapi.yaml @@ -0,0 +1,21 @@ +openapi: 3.0.0 +info: + title: test + version: 0.0.0 +paths: + /report: + get: + parameters: + - in: query + name: rel_date + required: true + schema: + type: string + default: today + enum: + - today + - yesterday + - lastweek + responses: + '200': + description: OK diff --git a/test/e2e/specs/features/parameter-enum-rendering.swagger.yaml b/test/e2e/specs/features/parameter-enum-rendering.swagger.yaml new file mode 100644 index 00000000..b81c88d9 --- /dev/null +++ b/test/e2e/specs/features/parameter-enum-rendering.swagger.yaml @@ -0,0 +1,20 @@ +swagger: '2.0' +info: + title: test + version: 0.0.0 +paths: + /report: + get: + parameters: + - in: query + name: rel_date + required: true + type: string + default: today + enum: + - today + - yesterday + - lastweek + responses: + 200: + description: OK From e2d8a4e3969fe07c7a09a4443e0263cdbbae3c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luka=20=C5=BDitnik?= Date: Fri, 6 Apr 2018 04:20:20 +0200 Subject: [PATCH 4/4] Fix(auth): improper resolution of relative token urls (#4180) * fix(auth): improper resolution of relative token urls * revert cc58ba7 for OAS2 In OAS2, relative token URLs are resolved against the host that serves the specs. --- src/core/plugins/auth/actions.js | 13 +++--- test/core/plugins/auth/actions.js | 75 +++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 test/core/plugins/auth/actions.js diff --git a/src/core/plugins/auth/actions.js b/src/core/plugins/auth/actions.js index d264c65d..f578f73d 100644 --- a/src/core/plugins/auth/actions.js +++ b/src/core/plugins/auth/actions.js @@ -1,3 +1,4 @@ +import parseUrl from "url-parse" import win from "core/window" import { btoa, buildFormData } from "core/utils" @@ -139,13 +140,15 @@ export const authorizeAccessCodeWithBasicAuthentication = ( { auth, redirectUrl return authActions.authorizeRequest({body: buildFormData(form), name, url: schema.get("tokenUrl"), auth, headers}) } -export const authorizeRequest = ( data ) => ( { fn, getConfigs, authActions, errActions, authSelectors } ) => { +export const authorizeRequest = ( data ) => ( { fn, getConfigs, authActions, errActions, oas3Selectors, specSelectors } ) => { let { body, query={}, headers={}, name, url, auth } = data - let { additionalQueryStringParams } = authSelectors.getConfigs() || {} - let fetchUrl = url - for (let key in additionalQueryStringParams) { - url += "&" + key + "=" + encodeURIComponent(additionalQueryStringParams[key]) + let fetchUrl + + if (specSelectors.isOAS3()) { + fetchUrl = parseUrl(url, oas3Selectors.selectedServer()).toString() + } else { + fetchUrl = parseUrl(url, specSelectors.url()).toString() } let _headers = Object.assign({ diff --git a/test/core/plugins/auth/actions.js b/test/core/plugins/auth/actions.js new file mode 100644 index 00000000..bce850ba --- /dev/null +++ b/test/core/plugins/auth/actions.js @@ -0,0 +1,75 @@ +/* eslint-env mocha */ +import expect, { createSpy } from "expect" +import { authorizeRequest } from "corePlugins/auth/actions" + +describe("auth plugin - actions", () => { + + describe("authorizeRequest", () => { + + [ + [ + { + oas3: true, + server: "https://host/resource", + scheme: "http", + host: null, + url: "http://specs/file", + }, + "https://host/authorize" + ], + [ + { + oas3: false, + server: null, + scheme: "https", + host: undefined, + url: "https://specs/file", + }, + "https://specs/authorize" + ], + [ + { + oas3: false, + server: null, + scheme: "https", + host: "host", + url: "http://specs/file", + }, + "http://specs/authorize" + ], + ].forEach(([{oas3, server, scheme, host, url}, expectedFetchUrl]) => { + it("should resolve authorization endpoint against the server URL", () => { + + // Given + const data = { + url: "/authorize" + } + const system = { + fn: { + fetch: createSpy().andReturn(Promise.resolve()) + }, + getConfigs: () => ({}), + authSelectors: { + getConfigs: () => ({}) + }, + oas3Selectors: { + selectedServer: () => server + }, + specSelectors: { + isOAS3: () => oas3, + operationScheme: () => scheme, + host: () => host, + url: () => url + } + } + + // When + authorizeRequest(data)(system) + + // Then + expect(system.fn.fetch.calls.length).toEqual(1) + expect(system.fn.fetch.calls[0].arguments[0]).toInclude({url: expectedFetchUrl}) + }) + }) + }) +})