diff --git a/src/core/components/parameter-include-empty.jsx b/src/core/components/parameter-include-empty.jsx
index ef56663f..6656574c 100644
--- a/src/core/components/parameter-include-empty.jsx
+++ b/src/core/components/parameter-include-empty.jsx
@@ -1,22 +1,53 @@
-import React from "react"
+import React, { Component } from "react"
import cx from "classnames"
import PropTypes from "prop-types"
-export const ParameterIncludeEmpty = ({ isIncluded, onChange, isDisabled }) => {
- const onCheckboxChange = e => {
- onChange(e.target.checked)
- }
- return
-}
-ParameterIncludeEmpty.propTypes = {
+
+const noop = () => { }
+
+const ParameterIncludeEmptyPropTypes = {
isIncluded: PropTypes.bool.isRequired,
isDisabled: PropTypes.bool.isRequired,
+ isIncludedOptions: PropTypes.object,
onChange: PropTypes.func.isRequired,
}
-export default ParameterIncludeEmpty
+const ParameterIncludeEmptyDefaultProps = {
+ onChange: noop,
+ isIncludedOptions: {},
+}
+export default class ParameterIncludeEmpty extends Component {
+ static propTypes = ParameterIncludeEmptyPropTypes
+ static defaultProps = ParameterIncludeEmptyDefaultProps
+
+ componentDidMount() {
+ const { isIncludedOptions, onChange } = this.props
+ const { shouldDispatchInit, defaultValue } = isIncludedOptions
+ if (shouldDispatchInit) {
+ onChange(defaultValue)
+ }
+ }
+
+ onCheckboxChange = e => {
+ const { onChange } = this.props
+ onChange(e.target.checked)
+ }
+
+ render() {
+ let { isIncluded, isDisabled } = this.props
+
+ return (
+
+
+
+ )
+ }
+}
diff --git a/src/core/plugins/oas3/components/request-body.jsx b/src/core/plugins/oas3/components/request-body.jsx
index b21acc4c..b33dd5ef 100644
--- a/src/core/plugins/oas3/components/request-body.jsx
+++ b/src/core/plugins/oas3/components/request-body.jsx
@@ -53,6 +53,19 @@ const RequestBody = ({
const handleFile = (e) => {
onChange(e.target.files[0])
}
+ const setIsIncludedOptions = (key) => {
+ let options = {
+ key,
+ shouldDispatchInit: false,
+ defaultValue: true
+ }
+ let currentInclusion = requestBodyInclusionSetting.get(key, "no value")
+ if (currentInclusion === "no value") {
+ options.shouldDispatchInit = true
+ // future: can get/set defaultValue from a config setting
+ }
+ return options
+ }
const Markdown = getComponent("Markdown", true)
const ModelExample = getComponent("modelExample")
@@ -173,7 +186,8 @@ const RequestBody = ({
{required ? null : (
onChangeIncludeEmpty(key, value)}
- isIncluded={requestBodyInclusionSetting.get(key)}
+ isIncluded={requestBodyInclusionSetting.get(key) || false}
+ isIncludedOptions={setIsIncludedOptions(key)}
isDisabled={!isEmptyValue(currentValue)}
/>
)}
diff --git a/test/e2e-cypress/static/documents/features/petstore-only-pet.openapi.yaml b/test/e2e-cypress/static/documents/features/petstore-only-pet.openapi.yaml
new file mode 100644
index 00000000..2d5d0060
--- /dev/null
+++ b/test/e2e-cypress/static/documents/features/petstore-only-pet.openapi.yaml
@@ -0,0 +1,418 @@
+openapi: 3.0.2
+servers:
+ - url: /v3
+info:
+ description: |-
+ This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about
+ Swagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!
+ You can now help us improve the API whether it's by making changes to the definition itself or to the code.
+ That way, with time, we can improve the API in general, and expose some of the new features in OAS3.
+
+ Some useful links:
+ - [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)
+ - [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)
+ version: 1.0.5-SNAPSHOT
+ title: Swagger Petstore - OpenAPI 3.0
+ 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'
+paths:
+ /pet:
+ post:
+ tags:
+ - pet
+ summary: Add a new pet to the store
+ description: Add a new pet to the store
+ operationId: addPet
+ responses:
+ '200':
+ description: Successful operation
+ content:
+ application/xml:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ '405':
+ description: Invalid input
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ requestBody:
+ description: Create a new pet in the store
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ application/xml:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ application/x-www-form-urlencoded:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ put:
+ tags:
+ - pet
+ summary: Update an existing pet
+ description: Update an existing pet by Id
+ operationId: updatePet
+ responses:
+ '200':
+ description: Successful operation
+ content:
+ application/xml:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ '400':
+ description: Invalid ID supplied
+ '404':
+ description: Pet not found
+ '405':
+ description: Validation exception
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ requestBody:
+ description: Update an existent pet in the store
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ application/xml:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ application/x-www-form-urlencoded:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ /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: false
+ explode: true
+ schema:
+ 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'
+ /pet/findByTags:
+ get:
+ tags:
+ - pet
+ summary: Finds Pets by tags
+ description: >-
+ Multiple tags can be provided with comma separated strings. Use tag1,
+ tag2, tag3 for testing.
+ operationId: findPetsByTags
+ parameters:
+ - name: tags
+ in: query
+ description: Tags to filter by
+ required: false
+ explode: true
+ schema:
+ type: array
+ items:
+ type: string
+ 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 tag value
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ '/pet/{petId}':
+ get:
+ tags:
+ - pet
+ summary: Find pet by ID
+ description: Returns a single pet
+ operationId: getPetById
+ parameters:
+ - name: petId
+ in: path
+ description: ID of pet to return
+ required: true
+ schema:
+ type: integer
+ format: int64
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/xml:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ '400':
+ description: Invalid ID supplied
+ '404':
+ description: Pet not found
+ security:
+ - api_key: []
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ post:
+ tags:
+ - pet
+ summary: Updates a pet in the store with form data
+ description: ''
+ operationId: updatePetWithForm
+ parameters:
+ - name: petId
+ in: path
+ description: ID of pet that needs to be updated
+ required: true
+ schema:
+ type: integer
+ format: int64
+ - name: name
+ in: query
+ description: Name of pet that needs to be updated
+ required: true
+ schema:
+ type: string
+ - name: status
+ in: query
+ description: Status of pet that needs to be updated
+ schema:
+ type: string
+ responses:
+ '405':
+ description: Invalid input
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ delete:
+ tags:
+ - pet
+ summary: Deletes a pet
+ description: ''
+ operationId: deletePet
+ parameters:
+ - name: api_key
+ in: header
+ description: ''
+ required: false
+ schema:
+ type: string
+ - name: petId
+ in: path
+ description: Pet id to delete
+ required: true
+ schema:
+ type: integer
+ format: int64
+ responses:
+ '400':
+ description: Invalid pet value
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ '/pet/{petId}/uploadImage':
+ post:
+ tags:
+ - pet
+ summary: uploads an image
+ description: ''
+ operationId: uploadFile
+ parameters:
+ - name: petId
+ in: path
+ description: ID of pet to update
+ required: true
+ schema:
+ type: integer
+ format: int64
+ - name: additionalMetadata
+ in: query
+ description: Additional Metadata
+ required: false
+ schema:
+ type: string
+ responses:
+ '200':
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ApiResponse'
+ security:
+ - petstore_auth:
+ - 'write:pets'
+ - 'read:pets'
+ requestBody:
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ format: binary
+externalDocs:
+ description: Find out more about Swagger
+ url: 'http://swagger.io'
+components:
+ schemas:
+ Category:
+ x-swagger-router-model: io.swagger.petstore.model.Category
+ properties:
+ id:
+ type: integer
+ format: int64
+ example: 1
+ name:
+ type: string
+ example: Dogs
+ xml:
+ name: category
+ type: object
+ Tag:
+ x-swagger-router-model: io.swagger.petstore.model.Tag
+ properties:
+ id:
+ type: integer
+ format: int64
+ name:
+ type: string
+ xml:
+ name: tag
+ type: object
+ Pet:
+ x-swagger-router-model: io.swagger.petstore.model.Pet
+ required:
+ - name
+ - photoUrls
+ properties:
+ id:
+ type: integer
+ format: int64
+ example: 10
+ name:
+ type: string
+ example: doggie
+ category:
+ $ref: '#/components/schemas/Category'
+ photoUrls:
+ type: array
+ xml:
+ wrapped: true
+ items:
+ type: string
+ xml:
+ name: photoUrl
+ tags:
+ type: array
+ xml:
+ wrapped: true
+ items:
+ $ref: '#/components/schemas/Tag'
+ xml:
+ name: tag
+ status:
+ type: string
+ description: pet status in the store
+ enum:
+ - available
+ - pending
+ - sold
+ xml:
+ name: pet
+ type: object
+ ApiResponse:
+ properties:
+ code:
+ type: integer
+ format: int32
+ type:
+ type: string
+ message:
+ type: string
+ xml:
+ name: '##default'
+ type: object
+ 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
+ securitySchemes:
+ petstore_auth:
+ type: oauth2
+ flows:
+ implicit:
+ authorizationUrl: 'https://petstore.swagger.io/oauth/authorize'
+ scopes:
+ 'write:pets': modify pets in your account
+ 'read:pets': read your pets
+ api_key:
+ type: apiKey
+ name: api_key
+ in: header
\ No newline at end of file
diff --git a/test/e2e-cypress/tests/features/oas3-request-body-allow-empty-values.js b/test/e2e-cypress/tests/features/oas3-request-body-allow-empty-values.js
new file mode 100644
index 00000000..65c7383c
--- /dev/null
+++ b/test/e2e-cypress/tests/features/oas3-request-body-allow-empty-values.js
@@ -0,0 +1,132 @@
+/**
+ * @prettier
+ */
+
+describe("OpenAPI 3.0 Allow Empty Values in Request Body", () => {
+ it("should not apply or render to required fields", () => {
+ 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()
+ // Request Body
+ .get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(2) > .parameters-col_description .parameter__empty_value_toggle input")
+ .should("not.exist")
+ })
+
+ it("by default, should be checked for all non-required fields", () => {
+ 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()
+ // Request Body
+ .get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .parameter__empty_value_toggle input")
+ .should("be.checked")
+ .get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(6) > .parameters-col_description .parameter__empty_value_toggle input")
+ .should("be.checked")
+ })
+
+ it("checkbox should be toggle-able", () => {
+ 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()
+ // Request Body
+ .get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .parameter__empty_value_toggle input")
+ .should("be.checked")
+ .uncheck()
+ .get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .parameter__empty_value_toggle input")
+ .should("not.be.checked")
+ })
+
+ it("on execute, should allow send with all empty values", () => {
+ 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()
+ // Execute
+ .get(".execute.opblock-control__btn")
+ .click()
+ // cURL component
+ .get(".responses-wrapper .copy-paste")
+ .should("exist")
+ .get(".responses-wrapper .copy-paste textarea")
+ .should("contains.text", "tags=&status=")
+ })
+
+ it("on execute, should allow send with some empty values", () => {
+ 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()
+ // Request Body
+ .get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .parameter__empty_value_toggle input")
+ .uncheck()
+ // Execute
+ .get(".execute.opblock-control__btn")
+ .click()
+ // cURL component
+ .get(".responses-wrapper .copy-paste")
+ .should("exist")
+ .get(".responses-wrapper .copy-paste textarea")
+ .should("contains.text", "&status=")
+ .should("not.contains.text", "tags=")
+ })
+
+ it("on execute, should allow send with skip all empty values", () => {
+ 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()
+ // Request Body
+ .get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .parameter__empty_value_toggle input")
+ .uncheck()
+ .get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(6) > .parameters-col_description .parameter__empty_value_toggle input")
+ .uncheck()
+ // Execute
+ .get(".execute.opblock-control__btn")
+ .click()
+ // cURL component
+ .get(".responses-wrapper .copy-paste")
+ .should("exist")
+ .get(".responses-wrapper .copy-paste textarea")
+ .should("not.contains.text", "tags=")
+ .should("not.contains.text", "status=")
+ })
+
+})