@@ -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 <label className={cx("parameter__empty_value_toggle", { | |||
"disabled": isDisabled | |||
})}> | |||
<input type="checkbox" disabled={isDisabled} checked={!isDisabled && isIncluded} onChange={onCheckboxChange} /> | |||
Send empty value | |||
</label> | |||
} | |||
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 ( | |||
<div> | |||
<label className={cx("parameter__empty_value_toggle", { | |||
"disabled": isDisabled | |||
})}> | |||
<input type="checkbox" | |||
disabled={isDisabled} | |||
checked={!isDisabled && isIncluded} | |||
onChange={this.onCheckboxChange} /> | |||
Send empty value | |||
</label> | |||
</div> | |||
) | |||
} | |||
} |
@@ -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 : ( | |||
<ParameterIncludeEmpty | |||
onChange={(value) => onChangeIncludeEmpty(key, value)} | |||
isIncluded={requestBodyInclusionSetting.get(key)} | |||
isIncluded={requestBodyInclusionSetting.get(key) || false} | |||
isIncludedOptions={setIsIncludedOptions(key)} | |||
isDisabled={!isEmptyValue(currentValue)} | |||
/> | |||
)} | |||
@@ -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 |
@@ -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=") | |||
}) | |||
}) |