diff --git a/src/core/components/param-body.jsx b/src/core/components/param-body.jsx
index 878b4546ab4..23238db7448 100644
--- a/src/core/components/param-body.jsx
+++ b/src/core/components/param-body.jsx
@@ -57,7 +57,10 @@ export default class ParamBody extends PureComponent {
this.setState({ value: val })
this.onChange(val, {isXml: isXml, isEditBox: isExecute})
} else {
- if (isXml) {
+ let xExampleValue = this.xExampleValue()
+ if (xExampleValue !== undefined) {
+ this.onChange(xExampleValue, {isXml: isXml, isEditBox: isExecute})
+ } else if (isXml) {
this.onChange(this.sample("xml"), {isXml: isXml, isEditBox: isExecute})
} else {
this.onChange(this.sample(), {isEditBox: isExecute})
@@ -65,6 +68,29 @@ export default class ParamBody extends PureComponent {
}
}
+ xExampleValue = () => {
+ const { param } = this.props
+ const xExamples = param.get("x-examples")
+ if (!xExamples || typeof xExamples.get !== "function" || xExamples.size === 0) {
+ return undefined
+ }
+ const defaultExample = xExamples.get("default")
+ const example = defaultExample !== undefined ? defaultExample : xExamples.first()
+ if (example === undefined) {
+ return undefined
+ }
+ if (typeof example === "string") {
+ return example
+ }
+ if (typeof example.toJS === "function") {
+ return JSON.stringify(example.toJS(), null, 2)
+ }
+ if (typeof example === "object") {
+ return JSON.stringify(example, null, 2)
+ }
+ return String(example)
+ }
+
sample = (xml) => {
let { param, fn} = this.props
let schema = fn.inferSchema(param.toJS())
diff --git a/test/e2e-cypress/e2e/bugs/3233.cy.js b/test/e2e-cypress/e2e/bugs/3233.cy.js
new file mode 100644
index 00000000000..b881744e253
--- /dev/null
+++ b/test/e2e-cypress/e2e/bugs/3233.cy.js
@@ -0,0 +1,25 @@
+describe("#3233: x-examples should populate the body parameter example for OAS 2.0", () => {
+ it("renders the x-examples 'default' value as the request body example", () => {
+ cy.visit("?url=/documents/bugs/3233.yaml")
+ .get("#operations-default-dataTargets")
+ .click()
+ .get(".opblock-section")
+ .within(() => {
+ cy.get(".body-param__example")
+ .should("be.visible")
+ .should("include.text", "targets")
+ .should("include.text", "1")
+ .should("include.text", "4")
+ })
+ })
+
+ it("populates the request body textarea with x-examples when 'Try it out' is enabled", () => {
+ cy.visit("?url=/documents/bugs/3233.yaml")
+ .get("#operations-default-dataTargets")
+ .click()
+ .get(".try-out__btn")
+ .click()
+ .get("textarea.body-param__text")
+ .should("contain.value", "targets")
+ })
+})
diff --git a/test/e2e-cypress/static/documents/bugs/3233.yaml b/test/e2e-cypress/static/documents/bugs/3233.yaml
new file mode 100644
index 00000000000..30ff607da7e
--- /dev/null
+++ b/test/e2e-cypress/static/documents/bugs/3233.yaml
@@ -0,0 +1,28 @@
+swagger: "2.0"
+info:
+ description: "OAS 2.0 sample with x-examples in body parameter (issue #3233)"
+ version: "0.0.1"
+ title: "Swagger Sample"
+paths:
+ /data/targets:
+ post:
+ summary: "Returns validation results of given list of targets."
+ description: ""
+ operationId: "dataTargets"
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ parameters:
+ - in: body
+ name: body
+ description: Targets list in JSON format.
+ required: true
+ schema:
+ type: object
+ x-examples:
+ default:
+ targets: [1, 2, 3, 4]
+ responses:
+ "200":
+ description: OK
diff --git a/test/unit/components/param-body.jsx b/test/unit/components/param-body.jsx
new file mode 100644
index 00000000000..31619e0bc37
--- /dev/null
+++ b/test/unit/components/param-body.jsx
@@ -0,0 +1,150 @@
+import React from "react"
+import expect from "expect"
+import { mount } from "enzyme"
+import { fromJS } from "immutable"
+import ParamBody from "core/components/param-body"
+
+describe("", () => {
+ const baseFn = {
+ inferSchema: () => ({}),
+ getSampleSchema: () => "GENERATED_SAMPLE",
+ }
+
+ const baseSpecSelectors = {
+ parameterWithMetaByIdentity: (pathMethod, param) => param,
+ contentTypeValues: () => fromJS({ requestContentType: "application/json" }),
+ }
+
+ const fakeGetComponent = () => () => null
+
+ const createProps = (overrides = {}) => ({
+ fn: baseFn,
+ getComponent: fakeGetComponent,
+ specSelectors: baseSpecSelectors,
+ pathMethod: ["/foo", "post"],
+ consumes: fromJS(["application/json"]),
+ consumesValue: "application/json",
+ isExecute: false,
+ onChange: jest.fn(),
+ onChangeConsumes: jest.fn(),
+ ...overrides,
+ })
+
+ it("emits the user-provided value when one is set", () => {
+ const onChange = jest.fn()
+ const props = createProps({
+ param: fromJS({
+ name: "body",
+ in: "body",
+ value: "{\"hello\":\"world\"}",
+ }),
+ onChange,
+ })
+
+ mount()
+
+ expect(onChange).toHaveBeenCalledWith("{\"hello\":\"world\"}", false)
+ })
+
+ it("emits the generated sample when no value and no x-examples are present", () => {
+ const onChange = jest.fn()
+ const props = createProps({
+ param: fromJS({
+ name: "body",
+ in: "body",
+ schema: { type: "string" },
+ }),
+ onChange,
+ })
+
+ mount()
+
+ expect(onChange).toHaveBeenCalledWith("GENERATED_SAMPLE", undefined)
+ })
+
+ it("prefers the x-examples 'default' value over the generated sample", () => {
+ const onChange = jest.fn()
+ const props = createProps({
+ param: fromJS({
+ name: "body",
+ in: "body",
+ schema: { type: "object" },
+ "x-examples": {
+ default: { targets: [1, 2, 3, 4] },
+ },
+ }),
+ onChange,
+ })
+
+ mount()
+
+ expect(onChange).toHaveBeenCalledTimes(1)
+ expect(onChange).toHaveBeenCalledWith(
+ JSON.stringify({ targets: [1, 2, 3, 4] }, null, 2),
+ false
+ )
+ })
+
+ it("falls back to the first x-examples entry when 'default' is missing", () => {
+ const onChange = jest.fn()
+ const props = createProps({
+ param: fromJS({
+ name: "body",
+ in: "body",
+ schema: { type: "object" },
+ "x-examples": {
+ first: { foo: "bar" },
+ second: { baz: "qux" },
+ },
+ }),
+ onChange,
+ })
+
+ mount()
+
+ expect(onChange).toHaveBeenCalledTimes(1)
+ expect(onChange).toHaveBeenCalledWith(
+ JSON.stringify({ foo: "bar" }, null, 2),
+ false
+ )
+ })
+
+ it("uses string x-examples values verbatim", () => {
+ const onChange = jest.fn()
+ const props = createProps({
+ param: fromJS({
+ name: "body",
+ in: "body",
+ schema: { type: "string" },
+ "x-examples": {
+ default: "{\"targets\": \"[1, 2, 3, 4]\"}",
+ },
+ }),
+ onChange,
+ })
+
+ mount()
+
+ expect(onChange).toHaveBeenCalledWith(
+ "{\"targets\": \"[1, 2, 3, 4]\"}",
+ false
+ )
+ })
+
+ it("ignores empty x-examples and falls back to the generated sample", () => {
+ const onChange = jest.fn()
+ const props = createProps({
+ param: fromJS({
+ name: "body",
+ in: "body",
+ schema: { type: "object" },
+ "x-examples": {},
+ }),
+ onChange,
+ })
+
+ mount()
+
+ expect(onChange).toHaveBeenCalledWith("GENERATED_SAMPLE", undefined)
+ })
+})