fix(oas3): switching media types should update schema properties (#6518)

* When the media-type is changed, there is a new `onChangeMediaType` method to handle actions.
* If target schema properties key/value pairs does NOT equals current schema properties, clear the requestBodyValue, try-it-out request/response and validation params.
* If target schema properties key/value pairs DOES equals current schema properties, do not change or re-render schema properties
* oas3Selector `validateShallowRequired` now also validates required keys against target media-type

Fixes #6201, #6250, #6476
This commit is contained in:
Tim Lai
2020-10-14 16:24:07 -07:00
committed by GitHub
parent b9137dcacc
commit 3905fadfbe
9 changed files with 363 additions and 14 deletions

View File

@@ -0,0 +1,50 @@
openapi: 3.0.0
info:
title: Switching between multiple content-type test
version: 1.0.0
servers:
- url: https://httpbin.org
paths:
/post:
post:
requestBody:
content:
multipart/form-data:
schema:
$ref: '#/components/schemas/Bar'
application/x-www-form-urlencoded:
schema:
$ref: '#/components/schemas/Foo'
application/json:
schema:
$ref: '#/components/schemas/FooBar'
responses:
'200':
description: ok
components:
schemas:
Foo:
type: object
properties:
foo:
type: string
example: ''
Bar:
type: object
required: [bar]
properties:
bar:
type: integer
example: 1
FooBar:
type: object
required:
- bar
properties:
foo:
type: string
example: ''
bar:
type: integer
example: 1

View File

@@ -0,0 +1,174 @@
// https://github.com/swagger-api/swagger-ui/issues/6201
// https://github.com/swagger-api/swagger-ui/issues/6250
// https://github.com/swagger-api/swagger-ui/issues/6476
describe("OpenAPI 3.0 Multiple Media Types with different schemas", () => {
const mediaTypeFormData = "multipart/form-data"
const mediaTypeUrlencoded = "application/x-www-form-urlencoded"
const mediaTypeJson = "application/json"
beforeEach(() => {
cy.visit(
"/?url=/documents/features/oas3-multiple-media-type.yaml"
)
.get("#operations-default-post_post")
.click()
// Expand Try It Out
.get(".try-out__btn")
.click()
// @alias Execute Button
cy.get(".execute.opblock-control__btn").as("executeBtn")
// @alias Media Type Dropdown
cy.get(".opblock-section-request-body .content-type").as("selectMediaType")
})
// In all cases,
// - assume that examples are populated based on schema (not explicitly tested)
// - assume validation passes based on successful "execute"
// - expect final cURL command result doees not contain unexpected artifacts from other content-type schemas
describe("multipart/form-data (only 'bar')", () => {
it("should execute multipart/form-data", () => {
cy.get("@selectMediaType")
.select(mediaTypeFormData)
cy.get("@executeBtn")
.click()
// cURL component
cy.get(".responses-wrapper .curl-command")
.should("exist")
.get(".responses-wrapper .curl-command span")
.should("contains.text", "bar")
.should("not.contains.text", "foo")
})
it("should execute application/x-www-form-urlencoded THEN execute multipart/form-data", () => {
cy.get("@selectMediaType")
.select(mediaTypeUrlencoded)
cy.get("@executeBtn")
.click()
cy.get("@selectMediaType")
.select(mediaTypeFormData)
cy.get("@executeBtn")
.click()
// cURL component
cy.get(".responses-wrapper .curl-command")
.should("exist")
.get(".responses-wrapper .curl-command span")
.should("contains.text", "bar")
.should("not.contains.text", "foo")
})
it("should execute application/json THEN execute multipart/form-data", () => {
cy.get("@selectMediaType")
.select(mediaTypeJson)
cy.get("@executeBtn")
.click()
cy.get("@selectMediaType")
.select(mediaTypeFormData)
cy.get("@executeBtn")
.click()
// cURL component
cy.get(".responses-wrapper .curl-command")
.should("exist")
.get(".responses-wrapper .curl-command span")
.should("contains.text", "bar")
.should("not.contains.text", "foo")
})
})
describe("application/x-www-form-urlencoded (only 'foo')", () => {
it("should execute application/x-www-form-urlencoded", () => {
cy.get("@selectMediaType")
.select(mediaTypeUrlencoded)
cy.get("@executeBtn")
.click()
// cURL component
cy.get(".responses-wrapper .curl-command")
.should("exist")
.get(".responses-wrapper .curl-command span")
.should("contains.text", "foo")
.should("not.contains.text", "bar")
})
it("should execute multipart/form-data THEN execute application/x-www-form-urlencoded", () => {
cy.get("@selectMediaType")
.select(mediaTypeFormData)
cy.get("@executeBtn")
.click()
cy.get("@selectMediaType")
.select(mediaTypeUrlencoded)
cy.get("@executeBtn")
.click()
// cURL component
cy.get(".responses-wrapper .curl-command")
.should("exist")
.get(".responses-wrapper .curl-command span")
.should("contains.text", "foo")
.should("not.contains.text", "bar")
})
it("should execute application/json THEN execute application/x-www-form-urlencoded", () => {
cy.get("@selectMediaType")
.select(mediaTypeJson)
cy.get("@executeBtn")
.click()
cy.get("@selectMediaType")
.select(mediaTypeUrlencoded)
cy.get("@executeBtn")
.click()
// cURL component
cy.get(".responses-wrapper .curl-command")
.should("exist")
.get(".responses-wrapper .curl-command span")
.should("contains.text", "foo")
.should("not.contains.text", "bar")
})
})
describe("application/json (both 'foo' and 'bar')", () => {
// note: form input for "application/json" is a string; not multiple form fields
it("should execute application/json", () => {
// final curl should have both "bar" and "foo"
cy.get("@selectMediaType")
.select(mediaTypeJson)
cy.get("@executeBtn")
.click()
cy.get("@executeBtn")
.click()
// cURL component
cy.get(".responses-wrapper .curl-command")
.should("exist")
.get(".responses-wrapper .curl-command span")
.should("contains.text", "foo")
.should("contains.text", "bar")
})
it("should execute multipart/form-data THEN execute application/json", () => {
cy.get("@selectMediaType")
.select(mediaTypeFormData)
cy.get("@executeBtn")
.click()
cy.get("@selectMediaType")
.select(mediaTypeJson)
cy.get("@executeBtn")
.click()
// cURL component
cy.get(".responses-wrapper .curl-command")
.should("exist")
.get(".responses-wrapper .curl-command span")
.should("contains.text", "foo")
.should("contains.text", "bar")
})
it("should execute application/x-www-form-urlencoded THEN execute application/json", () => {
// final curl should have both "bar" and "foo"
cy.get("@selectMediaType")
.select(mediaTypeUrlencoded)
cy.get("@executeBtn")
.click()
cy.get("@selectMediaType")
.select(mediaTypeJson)
cy.get("@executeBtn")
.click()
// cURL component
cy.get(".responses-wrapper .curl-command")
.should("exist")
.get(".responses-wrapper .curl-command span")
.should("contains.text", "foo")
.should("contains.text", "bar")
})
})
})

View File

@@ -504,4 +504,79 @@ describe("oas3 plugin - reducer", function () {
})
})
describe("CLEAR_REQUEST_BODY_VALUE", function () {
const clearRequestBodyValue = reducer["oas3_clear_request_body_value"]
describe("when requestBodyValue is a String", () => {
it("should clear requestBodyValue with empty String", () => {
const state = fromJS({
requestData: {
"/pet": {
post: {
bodyValue: "some random string",
requestContentType: "application/json"
}
}
}
})
const result = clearRequestBodyValue(state, {
payload: {
pathMethod: ["/pet", "post"],
}
})
const expectedResult = {
requestData: {
"/pet": {
post: {
bodyValue: "",
requestContentType: "application/json",
}
}
}
}
expect(result.toJS()).toEqual(expectedResult)
})
})
describe("when requestBodyValue is a Map", () => {
it("should clear requestBodyValue with empty Map", () => {
const state = fromJS({
requestData: {
"/pet": {
post: {
bodyValue: {
id: {
value: "10",
},
},
requestContentType: "application/x-www-form-urlencoded"
}
}
}
})
const result = clearRequestBodyValue(state, {
payload: {
pathMethod: ["/pet", "post"],
}
})
const expectedResult = {
requestData: {
"/pet": {
post: {
bodyValue: {},
requestContentType: "application/x-www-form-urlencoded",
}
}
}
}
expect(result.toJS()).toEqual(expectedResult)
})
})
})
})