fix(oas3): fix getting initial values for request body in OpenAPI 3.x (#9762)
Refs #9745
This commit is contained in:
@@ -150,39 +150,53 @@ const RequestBody = ({
|
|||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
{
|
{
|
||||||
Map.isMap(bodyProperties) && bodyProperties.entrySeq().map(([key, prop]) => {
|
Map.isMap(bodyProperties) && bodyProperties.entrySeq().map(([key, schema]) => {
|
||||||
if (prop.get("readOnly")) return
|
if (schema.get("readOnly")) return
|
||||||
|
|
||||||
let commonExt = showCommonExtensions ? getCommonExtensions(prop) : null
|
const schemaWithoutKeywords = schema.filter((v, k) => k !== "oneOf"
|
||||||
|
&& k !== "anyOf"
|
||||||
|
&& k !== "$$ref"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (schemaWithoutKeywords.size === 0) {
|
||||||
|
const oneOf = schema.get("oneOf")
|
||||||
|
const anyOf = schema.get("anyOf")
|
||||||
|
const nestedSchema = List.isList(oneOf)
|
||||||
|
? oneOf.get(0)
|
||||||
|
: List.isList(anyOf)
|
||||||
|
? anyOf.get(0)
|
||||||
|
: null
|
||||||
|
|
||||||
|
if (Map.isMap(nestedSchema)) {
|
||||||
|
schema = nestedSchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let commonExt = showCommonExtensions ? getCommonExtensions(schema) : null
|
||||||
const required = schemaForMediaType.get("required", List()).includes(key)
|
const required = schemaForMediaType.get("required", List()).includes(key)
|
||||||
const type = prop.get("type")
|
const type = schema.get("type")
|
||||||
const format = prop.get("format")
|
const format = schema.get("format")
|
||||||
const description = prop.get("description")
|
const description = schema.get("description")
|
||||||
const currentValue = requestBodyValue.getIn([key, "value"])
|
const currentValue = requestBodyValue.getIn([key, "value"])
|
||||||
const currentErrors = requestBodyValue.getIn([key, "errors"]) || requestBodyErrors
|
const currentErrors = requestBodyValue.getIn([key, "errors"]) || requestBodyErrors
|
||||||
const included = requestBodyInclusionSetting.get(key) || false
|
const included = requestBodyInclusionSetting.get(key) || false
|
||||||
|
|
||||||
const useInitialValFromSchemaSamples = prop.has("default")
|
let initialValue = fn.getSampleSchema(schema, false, {
|
||||||
|| prop.has("example")
|
includeWriteOnly: true
|
||||||
|| prop.hasIn(["items", "example"])
|
})
|
||||||
|| prop.hasIn(["items", "default"])
|
|
||||||
const useInitialValFromEnum = prop.has("enum") && (prop.get("enum").size === 1 || required)
|
|
||||||
const useInitialValue = useInitialValFromSchemaSamples || useInitialValFromEnum
|
|
||||||
|
|
||||||
let initialValue = ""
|
if (initialValue === false) {
|
||||||
if (type === "array" && !useInitialValue) {
|
initialValue = "false"
|
||||||
initialValue = []
|
|
||||||
}
|
}
|
||||||
if (type === "object" || useInitialValue) {
|
|
||||||
// TODO: what about example or examples from requestBody could be passed as exampleOverride
|
if (initialValue === 0) {
|
||||||
initialValue = fn.getSampleSchema(prop, false, {
|
initialValue = "0"
|
||||||
includeWriteOnly: true
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof initialValue !== "string" && type === "object") {
|
if (typeof initialValue !== "string" && type === "object") {
|
||||||
initialValue = stringify(initialValue)
|
initialValue = stringify(initialValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof initialValue === "string" && type === "array") {
|
if (typeof initialValue === "string" && type === "array") {
|
||||||
initialValue = JSON.parse(initialValue)
|
initialValue = JSON.parse(initialValue)
|
||||||
}
|
}
|
||||||
@@ -201,7 +215,7 @@ const RequestBody = ({
|
|||||||
{!showCommonExtensions || !commonExt.size ? null : commonExt.entrySeq().map(([key, v]) => <ParameterExt key={`${key}-${v}`} xKey={key} xVal={v} />)}
|
{!showCommonExtensions || !commonExt.size ? null : commonExt.entrySeq().map(([key, v]) => <ParameterExt key={`${key}-${v}`} xKey={key} xVal={v} />)}
|
||||||
</div>
|
</div>
|
||||||
<div className="parameter__deprecated">
|
<div className="parameter__deprecated">
|
||||||
{ prop.get("deprecated") ? "deprecated": null }
|
{ schema.get("deprecated") ? "deprecated": null }
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="parameters-col_description">
|
<td className="parameters-col_description">
|
||||||
@@ -210,7 +224,7 @@ const RequestBody = ({
|
|||||||
<JsonSchemaForm
|
<JsonSchemaForm
|
||||||
fn={fn}
|
fn={fn}
|
||||||
dispatchInitialValue={!isFile}
|
dispatchInitialValue={!isFile}
|
||||||
schema={prop}
|
schema={schema}
|
||||||
description={key}
|
description={key}
|
||||||
getComponent={getComponent}
|
getComponent={getComponent}
|
||||||
value={currentValue === undefined ? initialValue : currentValue}
|
value={currentValue === undefined ? initialValue : currentValue}
|
||||||
|
|||||||
@@ -31,8 +31,12 @@ describe("OpenAPI 3.0 Allow Empty Values in Request Body", () => {
|
|||||||
.get(".try-out__btn")
|
.get(".try-out__btn")
|
||||||
.click()
|
.click()
|
||||||
// Request Body
|
// Request Body
|
||||||
|
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .json-schema-form-item-remove")
|
||||||
|
.click()
|
||||||
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .parameter__empty_value_toggle input")
|
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .parameter__empty_value_toggle input")
|
||||||
.should("be.checked")
|
.should("be.checked")
|
||||||
|
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(6) > .parameters-col_description select")
|
||||||
|
.select("--")
|
||||||
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(6) > .parameters-col_description .parameter__empty_value_toggle input")
|
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(6) > .parameters-col_description .parameter__empty_value_toggle input")
|
||||||
.should("be.checked")
|
.should("be.checked")
|
||||||
})
|
})
|
||||||
@@ -49,6 +53,8 @@ describe("OpenAPI 3.0 Allow Empty Values in Request Body", () => {
|
|||||||
.get(".try-out__btn")
|
.get(".try-out__btn")
|
||||||
.click()
|
.click()
|
||||||
// Request Body
|
// Request Body
|
||||||
|
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .json-schema-form-item-remove")
|
||||||
|
.click()
|
||||||
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .parameter__empty_value_toggle input")
|
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .parameter__empty_value_toggle input")
|
||||||
.should("be.checked")
|
.should("be.checked")
|
||||||
.uncheck()
|
.uncheck()
|
||||||
@@ -67,9 +73,11 @@ describe("OpenAPI 3.0 Allow Empty Values in Request Body", () => {
|
|||||||
// Expand Try It Out
|
// Expand Try It Out
|
||||||
.get(".try-out__btn")
|
.get(".try-out__btn")
|
||||||
.click()
|
.click()
|
||||||
// add item to pass required validation
|
// Remove example values
|
||||||
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(4) > .parameters-col_description button")
|
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .json-schema-form-item-remove")
|
||||||
.click()
|
.click()
|
||||||
|
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(6) > .parameters-col_description select")
|
||||||
|
.select("--")
|
||||||
// Execute
|
// Execute
|
||||||
.get(".execute.opblock-control__btn")
|
.get(".execute.opblock-control__btn")
|
||||||
.click()
|
.click()
|
||||||
@@ -92,11 +100,11 @@ describe("OpenAPI 3.0 Allow Empty Values in Request Body", () => {
|
|||||||
.get(".try-out__btn")
|
.get(".try-out__btn")
|
||||||
.click()
|
.click()
|
||||||
// Request Body
|
// Request Body
|
||||||
|
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .json-schema-form-item-remove")
|
||||||
|
.click()
|
||||||
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .parameter__empty_value_toggle input")
|
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .parameter__empty_value_toggle input")
|
||||||
.uncheck()
|
.uncheck()
|
||||||
// add item to pass required validation
|
// add item to pass required validation
|
||||||
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(4) > .parameters-col_description button")
|
|
||||||
.click()
|
|
||||||
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(4) input")
|
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(4) input")
|
||||||
.clear()
|
.clear()
|
||||||
// Execute
|
// Execute
|
||||||
@@ -122,15 +130,14 @@ describe("OpenAPI 3.0 Allow Empty Values in Request Body", () => {
|
|||||||
.get(".try-out__btn")
|
.get(".try-out__btn")
|
||||||
.click()
|
.click()
|
||||||
// Request Body
|
// Request Body
|
||||||
|
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .json-schema-form-item-remove")
|
||||||
|
.click()
|
||||||
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .parameter__empty_value_toggle input")
|
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(5) > .parameters-col_description .parameter__empty_value_toggle input")
|
||||||
.uncheck()
|
.uncheck()
|
||||||
|
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(6) > .parameters-col_description select")
|
||||||
|
.select("--")
|
||||||
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(6) > .parameters-col_description .parameter__empty_value_toggle input")
|
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(6) > .parameters-col_description .parameter__empty_value_toggle input")
|
||||||
.uncheck()
|
.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()
|
|
||||||
.get(".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(4) input")
|
|
||||||
.clear()
|
|
||||||
// Execute
|
// Execute
|
||||||
.get(".execute.opblock-control__btn")
|
.get(".execute.opblock-control__btn")
|
||||||
.click()
|
.click()
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ describe("OpenAPI 3.0 Validation for Required Request Body and Request Body Fiel
|
|||||||
.get(
|
.get(
|
||||||
".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(1) > .parameters-col_description input"
|
".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(1) > .parameters-col_description input"
|
||||||
)
|
)
|
||||||
|
.clear()
|
||||||
.should("not.have.class", "invalid")
|
.should("not.have.class", "invalid")
|
||||||
// Execute
|
// Execute
|
||||||
.get(".execute.opblock-control__btn")
|
.get(".execute.opblock-control__btn")
|
||||||
@@ -163,11 +164,6 @@ describe("OpenAPI 3.0 Validation for Required Request Body and Request Body Fiel
|
|||||||
// Expand Try It Out
|
// Expand Try It Out
|
||||||
.get(".try-out__btn")
|
.get(".try-out__btn")
|
||||||
.click()
|
.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
|
// Execute
|
||||||
.get(".execute.opblock-control__btn")
|
.get(".execute.opblock-control__btn")
|
||||||
.click()
|
.click()
|
||||||
@@ -217,15 +213,6 @@ describe("OpenAPI 3.0 Validation for Required Request Body and Request Body Fiel
|
|||||||
".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(2) > .parameters-col_description input"
|
".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(2) > .parameters-col_description input"
|
||||||
)
|
)
|
||||||
.should("not.have.class", "invalid")
|
.should("not.have.class", "invalid")
|
||||||
// add item to get input, just an extra confirmation of non-invalid class
|
|
||||||
.get(
|
|
||||||
".opblock-body .opblock-section .opblock-section-request-body .parameters:nth-child(4) > .parameters-col_description button"
|
|
||||||
)
|
|
||||||
.click()
|
|
||||||
.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", () => {
|
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")
|
cy.visit("/?url=/documents/features/petstore-only-pet.openapi.yaml")
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* @prettier
|
||||||
|
*/
|
||||||
|
|
||||||
|
describe("OpenAPI 3.0 oneOf and anyOf example", () => {
|
||||||
|
it("should show example values for multipart/form-data and application/x-www-form-urlencoded content types", () => {
|
||||||
|
cy.visit("/?url=/documents/features/oas3-one-of-any-of-example.yaml").then(
|
||||||
|
() => {
|
||||||
|
cy.contains("/documentsWithCombineOneOf")
|
||||||
|
.click()
|
||||||
|
.get(".try-out__btn")
|
||||||
|
.click()
|
||||||
|
.get("textarea")
|
||||||
|
.contains("documentDate")
|
||||||
|
.should("exist")
|
||||||
|
cy.contains("/documentsWithCombineOneOf").click()
|
||||||
|
cy.contains("/documentsWithCombineAnyOf")
|
||||||
|
.click()
|
||||||
|
.get(".try-out__btn")
|
||||||
|
.contains("Try it out")
|
||||||
|
.click()
|
||||||
|
.get("textarea")
|
||||||
|
.contains("documentDate")
|
||||||
|
.should("exist")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
openapi: 3.0.3
|
||||||
|
info:
|
||||||
|
title: Example app
|
||||||
|
description: This is my test applicaiton
|
||||||
|
version: 1.0.0
|
||||||
|
contact:
|
||||||
|
name: John Doe
|
||||||
|
email: jonhdoe@contact.com
|
||||||
|
servers:
|
||||||
|
- url: https://dev.app.com/appApplicationName
|
||||||
|
description: DEV
|
||||||
|
paths:
|
||||||
|
/documentsWithCombineOneOf:
|
||||||
|
post:
|
||||||
|
description: ''
|
||||||
|
summary: Add a document - KO
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
multipart/form-data:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ModelPostDocumentOneOfCombineModelFile'
|
||||||
|
operationId: testOneOf
|
||||||
|
security:
|
||||||
|
- Basic: []
|
||||||
|
tags: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
$ref: '#/components/responses/Default'
|
||||||
|
/documentsWithCombineAnyOf:
|
||||||
|
post:
|
||||||
|
description: ''
|
||||||
|
summary: Add a document - KO
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/x-www-form-urlencoded:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ModelPostDocumentAnyOfCombineModelFile'
|
||||||
|
operationId: testAnyOf
|
||||||
|
security:
|
||||||
|
- Basic: []
|
||||||
|
tags: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
$ref: '#/components/responses/Default'
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
AbstractModelPostDocument:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
title:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
maxLength: 64
|
||||||
|
example: MyDocument.pdf'
|
||||||
|
category:
|
||||||
|
type: string
|
||||||
|
pattern: ''
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
description: Type depends on category
|
||||||
|
issuingApplicationId:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- App1
|
||||||
|
- App2
|
||||||
|
- App3
|
||||||
|
- App4
|
||||||
|
documentDate:
|
||||||
|
type: string
|
||||||
|
description: >-
|
||||||
|
Creation date of the document. Example : For ID card, it is the
|
||||||
|
issuing date of the card.
|
||||||
|
format: date
|
||||||
|
sharedWith:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- CustomerPortal
|
||||||
|
- PartnerPortal
|
||||||
|
uniqueItems: true
|
||||||
|
minItems: 0
|
||||||
|
maxItems: 2
|
||||||
|
required:
|
||||||
|
- title
|
||||||
|
- category
|
||||||
|
- type
|
||||||
|
- documentDate
|
||||||
|
- file
|
||||||
|
ModelPostDocumentPolicy:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/AbstractModelPostDocument'
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
policyNumber:
|
||||||
|
type: string
|
||||||
|
description: Policy Number
|
||||||
|
example: '132456'
|
||||||
|
minLength: 1
|
||||||
|
maxLength: 36
|
||||||
|
pattern: ''
|
||||||
|
description: Metadata for policy documents
|
||||||
|
required:
|
||||||
|
- policyNumber
|
||||||
|
ModelPostDocumentCustomer:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/AbstractModelPostDocument'
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
customerNumber:
|
||||||
|
type: number
|
||||||
|
description: Customer Number
|
||||||
|
exclusiveMinimum: true
|
||||||
|
minimum: 0
|
||||||
|
exclusiveMaximum: true
|
||||||
|
maximum: 99999999999999
|
||||||
|
organisationName:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- Organisation1
|
||||||
|
- Organisation2
|
||||||
|
- Organisation3
|
||||||
|
description: Organisation Name
|
||||||
|
required:
|
||||||
|
- organisationName
|
||||||
|
- customerNumber
|
||||||
|
description: Metadata for Customer Documents
|
||||||
|
ModelPostDocumentInvoice:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/AbstractModelPostDocument'
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
invoiceNumber:
|
||||||
|
type: string
|
||||||
|
description: Invoice Number
|
||||||
|
example: '132456'
|
||||||
|
minLength: 1
|
||||||
|
maxLength: 36
|
||||||
|
pattern: ''
|
||||||
|
amount:
|
||||||
|
type: number
|
||||||
|
example: 1234.56
|
||||||
|
description: Invoice Amount
|
||||||
|
description: Metadata for invoice documents
|
||||||
|
required:
|
||||||
|
- amount
|
||||||
|
- invoiceNumber
|
||||||
|
ModelPostDocumentOneOf:
|
||||||
|
oneOf:
|
||||||
|
- $ref: '#/components/schemas/ModelPostDocumentPolicy'
|
||||||
|
- $ref: '#/components/schemas/ModelPostDocumentCustomer'
|
||||||
|
- $ref: '#/components/schemas/ModelPostDocumentInvoice'
|
||||||
|
ModelFile:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
content:
|
||||||
|
type: string
|
||||||
|
format: binary
|
||||||
|
required:
|
||||||
|
- content
|
||||||
|
ModelPostDocumentOneOfCombineModelFile:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/ModelFile'
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
metadata:
|
||||||
|
$ref: '#/components/schemas/ModelPostDocumentOneOf'
|
||||||
|
required:
|
||||||
|
- metadata
|
||||||
|
ModelPostDocumentAnyOf:
|
||||||
|
anyOf:
|
||||||
|
- $ref: '#/components/schemas/ModelPostDocumentPolicy'
|
||||||
|
- $ref: '#/components/schemas/ModelPostDocumentCustomer'
|
||||||
|
- $ref: '#/components/schemas/ModelPostDocumentInvoice'
|
||||||
|
ModelPostDocumentAnyOfCombineModelFile:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/ModelFile'
|
||||||
|
- type: object
|
||||||
|
properties:
|
||||||
|
metadata:
|
||||||
|
$ref: '#/components/schemas/ModelPostDocumentAnyOf'
|
||||||
|
required:
|
||||||
|
- metadata
|
||||||
|
securitySchemes:
|
||||||
|
Basic:
|
||||||
|
type: http
|
||||||
|
scheme: basic
|
||||||
|
responses:
|
||||||
|
Default:
|
||||||
|
description: Not needed
|
||||||
|
headers: {}
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: not needed
|
||||||
|
tags: []
|
||||||
Reference in New Issue
Block a user