feat(RequestBody): validation support for required fields (#6223)

fixes #5181

* application/json
* application/xml
* application/x-www-form-urlencoded

* Set requestBodyValue values to be an immutable Map, as "value". Previously stored as a normal String.
* This enables adding "errors" to the Map, for validation use

* note: getOAS3RequiredRequestBodyContentType requires state.spec,
* which is not available to state.oas3
This commit is contained in:
Tim Lai
2020-07-16 17:53:28 -07:00
committed by GitHub
parent b68942c043
commit 2fd1e4037c
15 changed files with 1029 additions and 26 deletions

View File

@@ -0,0 +1,27 @@
info:
title: Required parameter missing, doesn't block request from executing.
version: '1'
openapi: 3.0.0
servers:
- url: http://httpbin.org/anything
paths:
/foos:
post:
requestBody:
content:
application/x-www-form-urlencoded:
# application/json:
# application/xml:
schema:
properties:
foo:
type: string
bar:
type: string
required:
- foo
type: object
required: true # Note this doesn't have an impact
responses:
default:
description: ok

View File

@@ -415,4 +415,4 @@ components:
api_key:
type: apiKey
name: api_key
in: header
in: header

View File

@@ -67,6 +67,9 @@ describe("OpenAPI 3.0 Allow Empty Values in Request Body", () => {
// Expand Try It Out
.get(".try-out__btn")
.click()
// add item to pass required validation
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(4) > .parameters-col_description button")
.click()
// Execute
.get(".execute.opblock-control__btn")
.click()
@@ -91,6 +94,9 @@ describe("OpenAPI 3.0 Allow Empty Values in Request Body", () => {
// Request Body
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .parameter__empty_value_toggle input")
.uncheck()
// add item to pass required validation
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(4) > .parameters-col_description button")
.click()
// Execute
.get(".execute.opblock-control__btn")
.click()
@@ -118,6 +124,9 @@ describe("OpenAPI 3.0 Allow Empty Values in Request Body", () => {
.uncheck()
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(6) > .parameters-col_description .parameter__empty_value_toggle input")
.uncheck()
// add item to pass required validation
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(4) > .parameters-col_description button")
.click()
// Execute
.get(".execute.opblock-control__btn")
.click()

View File

@@ -0,0 +1,214 @@
/**
* @prettier
*/
describe("OpenAPI 3.0 Validation for Required Request Body and Request Body Fields", () => {
describe("Request Body required bug/5181", () => {
it("on execute, if empty value, SHOULD render class 'invalid' and should NOT render cURL component", () => {
cy.visit(
"/?url=/documents/bugs/5181.yaml"
)
.get("#operations-default-post_foos")
.click()
// Expand Try It Out
.get(".try-out__btn")
.click()
// get input
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(1) > .parameters-col_description input")
.should("not.have.class", "invalid")
// Execute
.get(".execute.opblock-control__btn")
.click()
// class "invalid" should now exist (and render red, which we won't check)
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(1) > .parameters-col_description input")
.should("have.class", "invalid")
// cURL component should not exist
.get(".responses-wrapper .copy-paste")
.should("not.exist")
})
it("on execute, if value exists, should NOT render class 'invalid' and SHOULD render cURL component", () => {
cy.visit(
"/?url=/documents/bugs/5181.yaml"
)
.get("#operations-default-post_foos")
.click()
// Expand Try It Out
.get(".try-out__btn")
.click()
// get input
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(1) > .parameters-col_description input")
.type("abc")
// Execute
.get(".execute.opblock-control__btn")
.click()
.should("not.have.class", "invalid")
// cURL component should exist
.get(".responses-wrapper .copy-paste")
.should("exist")
})
})
describe("Request Body required fields - application/json", () => {
it("on execute, if empty value, SHOULD render class 'invalid' and should NOT render cURL component", () => {
cy.visit(
"/?url=/documents/features/petstore-only-pet.openapi.yaml"
)
.get("#operations-pet-addPet")
.click()
// Expand Try It Out
.get(".try-out__btn")
.click()
// get and clear textarea
.get(".opblock-body .opblock-section .opblock-section-request-body .body-param textarea")
.should("not.have.class", "invalid")
.clear()
// Execute
.get(".execute.opblock-control__btn")
.click()
// class "invalid" should now exist (and render red, which we won't check)
.get(".opblock-body .opblock-section .opblock-section-request-body .body-param textarea")
.should("have.class", "invalid")
// cURL component should not exist
.get(".responses-wrapper .copy-paste")
.should("not.exist")
})
it("on execute, if value exists, even if just single space, should NOT render class 'invalid' and SHOULD render cURL component that contains the single space", () => {
cy.visit(
"/?url=/documents/features/petstore-only-pet.openapi.yaml"
)
.get("#operations-pet-addPet")
.click()
// Expand Try It Out
.get(".try-out__btn")
.click()
// get, clear, then modify textarea
.get(".opblock-body .opblock-section .opblock-section-request-body .body-param textarea")
.clear()
.type(" ")
// Execute
.get(".execute.opblock-control__btn")
.click()
.get(".opblock-body .opblock-section .opblock-section-request-body .body-param textarea")
.should("not.have.class", "invalid")
// cURL component should exist
.get(".responses-wrapper .copy-paste")
.should("exist")
.get(".responses-wrapper .copy-paste textarea")
.should("contains.text", "-d \" \"")
})
})
/*
petstore ux notes:
- required field, but if example value exists, will populate the field. So this test will clear the example value.
- "add item" will insert an empty array, and display an input text box. This establishes a value for the field.
*/
describe("Request Body required fields - application/x-www-form-urlencoded", () => {
it("on execute, if empty value, SHOULD render class 'invalid' and should NOT render cURL component", () => {
cy.visit(
"/?url=/documents/features/petstore-only-pet.openapi.yaml"
)
.get("#operations-pet-addPet")
.click()
.get(".opblock-section .opblock-section-request-body .body-param-content-type > select")
.select("application/x-www-form-urlencoded")
// Expand Try It Out
.get(".try-out__btn")
.click()
// get and clear input populated from example value
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(2) > .parameters-col_description input")
.clear()
// Execute
.get(".execute.opblock-control__btn")
.click()
// class "invalid" should now exist (and render red, which we won't check)
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(2) > .parameters-col_description input")
.should("have.class", "invalid")
// cURL component should not exist
.get(".responses-wrapper .copy-paste")
.should("not.exist")
})
it("on execute, if all values exist, even if array exists but is empty, should NOT render class 'invalid' and SHOULD render cURL component", () => {
cy.visit(
"/?url=/documents/features/petstore-only-pet.openapi.yaml"
)
.get("#operations-pet-addPet")
.click()
.get(".opblock-section .opblock-section-request-body .body-param-content-type > select")
.select("application/x-www-form-urlencoded")
// Expand Try It Out
.get(".try-out__btn")
.click()
// add item to get input
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(4) > .parameters-col_description button")
.click()
// Execute
.get(".execute.opblock-control__btn")
.click()
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(2) > .parameters-col_description input")
.should("have.value", "doggie")
.should("not.have.class", "invalid")
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(4) > .parameters-col_description input")
.should("have.value", "")
.should("not.have.class", "invalid")
// cURL component should exist
.get(".responses-wrapper .copy-paste")
.should("exist")
})
})
describe("Request Body: switching between Content Types", () => {
it("after application/json 'invalid' error, on switch content type to application/x-www-form-urlencoded, SHOULD be free of errors", () => {
cy.visit(
"/?url=/documents/features/petstore-only-pet.openapi.yaml"
)
.get("#operations-pet-addPet")
.click()
// Expand Try It Out
.get(".try-out__btn")
.click()
// get and clear textarea
.get(".opblock-body .opblock-section .opblock-section-request-body .body-param textarea")
.should("not.have.class", "invalid")
.clear()
// Execute
.get(".execute.opblock-control__btn")
.click()
.get(".opblock-body .opblock-section .opblock-section-request-body .body-param textarea")
.should("have.class", "invalid")
// switch content type
.get(".opblock-section .opblock-section-request-body .body-param-content-type > select")
.select("application/x-www-form-urlencoded")
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(2) > .parameters-col_description input")
.should("not.have.class", "invalid")
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(4) > .parameters-col_description input")
.should("not.have.class", "invalid")
})
it("after application/x-www-form-urlencoded 'invalid' error, on switch content type to application/json, SHOULD be free of errors", () => {
cy.visit(
"/?url=/documents/features/petstore-only-pet.openapi.yaml"
)
.get("#operations-pet-addPet")
.click()
.get(".opblock-section .opblock-section-request-body .body-param-content-type > select")
.select("application/x-www-form-urlencoded")
// Expand Try It Out
.get(".try-out__btn")
.click()
// get and clear input
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(2) > .parameters-col_description input")
.clear()
// Execute
.get(".execute.opblock-control__btn")
.click()
// class "invalid" should now exist (and render red, which we won't check)
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(2) > .parameters-col_description input")
.should("have.class", "invalid")
// switch content type
.get(".opblock-section .opblock-section-request-body .body-param-content-type > select")
.select("application/json")
.get(".opblock-body .opblock-section .opblock-section-request-body .body-param textarea")
.should("not.have.class", "invalid")
})
})
})