Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions src/core/containers/OperationContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,29 @@ export default class OperationContainer extends PureComponent {

if (contentType === "application/x-www-form-urlencoded" || contentType === "multipart/form-data") {
const jsonRequestBodyValue = JSON.parse(defaultRequestBodyValue)

try {
const [pathName, method] = pathMethod
const requestBodyDef = this.props.specSelectors.specResolvedSubtree([
"paths",
pathName,
method,
"requestBody",
])
const schemaForMediaType = requestBodyDef?.getIn(["content", contentType, "schema"]) || Map()
const properties = schemaForMediaType.get("properties") || Map()
if (properties && typeof jsonRequestBodyValue === "object" && jsonRequestBodyValue !== null) {
properties.keySeq().toArray().forEach((key) => {
const propSchema = properties.get(key) || Map()
const schemaType = propSchema.get("type")
if (schemaType === "string" && key in jsonRequestBodyValue && typeof jsonRequestBodyValue[key] !== "string") {
jsonRequestBodyValue[key] = String(jsonRequestBodyValue[key])
}
})
}
} catch (e) {
// noop: fallback to parsed defaults without coercion
}
Object.entries(jsonRequestBodyValue).forEach(([key, value]) => {
if (Array.isArray(value)) {
jsonRequestBodyValue[key] = jsonRequestBodyValue[key].map((val) => {
Expand Down
24 changes: 23 additions & 1 deletion src/core/plugins/oas3/components/request-body.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,24 @@ export const getDefaultRequestBodyValue = (requestBody, mediaType, activeExample
])
: exampleSchema

const exampleValue = fn.getSampleSchema(
let exampleValue = fn.getSampleSchema(
schema,
mediaType,
{
includeWriteOnly: true
},
mediaTypeExample
)

const isFormLike = mediaType === "application/x-www-form-urlencoded" || mediaType.indexOf("multipart/") === 0
if (isFormLike && schema && schema.type === "object" && schema.properties && exampleValue && typeof exampleValue === "object") {
Object.entries(schema.properties).forEach(([prop, propSchema]) => {
const propType = Array.isArray(propSchema?.type) ? propSchema.type[0] : propSchema?.type
if (propType === "string" && propSchema && Object.prototype.hasOwnProperty.call(propSchema, "default")) {
exampleValue[prop] = String(propSchema.default)
}
})
}
return stringify(exampleValue)
}

Expand Down Expand Up @@ -179,6 +189,18 @@ const RequestBody = ({
initialValue = "0"
}

// If schema defines a default and the property is string-typed, prefer it verbatim.
// This preserves values like "1.0" exactly as specified by the spec.
if (objectType === "string") {
const schemaDefault = schema.get("default")
if (schemaDefault !== undefined) {
initialValue = String(schemaDefault)
} else if (typeof initialValue !== "string") {
// Otherwise ensure the sampled value is coerced to string.
initialValue = String(initialValue)
}
}

if (typeof initialValue !== "string" && objectType === "object") {
initialValue = stringify(initialValue)
}
Expand Down
22 changes: 18 additions & 4 deletions src/core/plugins/spec/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -428,13 +428,27 @@ export const executeRequest = (req) =>
const requestBodyInclusionSetting = oas3Selectors.requestBodyInclusionSetting(pathName, method)

if(requestBody && requestBody.toJS) {
const contentType = req.requestContentType
const requestBodyDef = specSelectors.specResolvedSubtree([
"paths",
pathName,
method,
"requestBody",
])
const schemaForMediaType = requestBodyDef?.getIn(["content", contentType, "schema"]) || ImmutableMap()
const properties = schemaForMediaType.get("properties") || ImmutableMap()

req.requestBody = requestBody
.map(
(val) => {
if (ImmutableMap.isMap(val)) {
return val.get("value")
(val, key) => {
const propSchema = properties.get(key) || ImmutableMap()
const schemaType = propSchema.get("type")
let outVal = ImmutableMap.isMap(val) ? val.get("value") : val
// If schema says string but value is not string, coerce
if (schemaType === "string" && typeof outVal !== "string" && outVal !== undefined && outVal !== null) {
outVal = String(outVal)
}
return val
return outVal
}
)
.filter(
Expand Down
62 changes: 62 additions & 0 deletions test/e2e-cypress/e2e/bugs/10047.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
describe("#10047: String default '1.0' preserved for form-urlencoded", () => {
it("should preserve string default value '1.0' exactly as specified", () => {
cy
.visit("?url=/documents/bugs/10047.yaml")
.get("#operations-default-testOp")
.click()
// Expand Try It Out
.get(".try-out__btn")
.click()
// Check that the default value is "1.0" (not "1")
.get(`.parameters[data-property-name="versionTag"] input`)
.should("have.value", "1.0")
// Execute the request and check the curl command
.get(".btn.execute")
.click()
// Check that the curl command contains versionTag=1.0
.get(".curl")
.should("contain", "versionTag=1.0")
.should("not.contain", "versionTag=1")
})

it("should preserve user-entered value '2.0' correctly", () => {
cy
.visit("?url=/documents/bugs/10047.yaml")
.get("#operations-default-testOp")
.click()
// Expand Try It Out
.get(".try-out__btn")
.click()
// Change the value
.get(`.parameters[data-property-name="versionTag"] input`)
.clear()
.type("2.0")
// Execute the request
.get(".btn.execute")
.click()
// Check that the curl command contains versionTag=2.0
.get(".curl")
.should("contain", "versionTag=2.0")
})

it("should reset to default value '1.0' correctly", () => {
cy
.visit("?url=/documents/bugs/10047.yaml")
.get("#operations-default-testOp")
.click()
// Expand Try It Out
.get(".try-out__btn")
.click()
// Change the value
.get(`.parameters[data-property-name="versionTag"] input`)
.clear()
.type("2.0")
// Reset
.get(".try-out__btn.reset")
.click()
// Check that the value is reset to "1.0" (not "1")
.get(`.parameters[data-property-name="versionTag"] input`)
.should("have.value", "1.0")
})
})

26 changes: 26 additions & 0 deletions test/e2e-cypress/static/documents/bugs/10047.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
openapi: 3.0.1
info:
title: Reproducer
version: "1.0"
paths:
/test:
post:
operationId: testOp
requestBody:
content:
application/x-www-form-urlencoded:
schema:
$ref: '#/components/schemas/Example'
responses:
200:
description: "Success"

components:
schemas:
Example:
type: object
properties:
versionTag:
type: string
default: "1.0"