feat: Multiple Examples for OpenAPI 3 Parameters, Request Bodies, and Responses (via #5427)

* add opt-in Prettier config

* remove legacy `examples` implementation

* create ExamplesSelect

* support `Response.examples` in OpenAPI 3

* create response controls group

* prettier reformat

* prepare to break up Parameters

* reunify Parameters and OAS3 Parameters

* Parameter Examples

* Example component

* handle parameter value stringification correctly

* FOR REVIEW: add prop for controlling Select

* use regular header for param examples in Try-It-Out

* manage active examples member via Redux

* Request Body Try-It-Out examples

* remove special Response description styling

* omit Example value display in Try-It-Out

* support disabled text inputs in JsonSchemaForm

* Example.omitValue => Example.showValue

* ExamplesSelectValueRetainer

* styling for disabled inputs

* remove console.log

* support "Modified Values" in ExamplesSelect

* remove Examples component
(wasn't used anywhere)

* use ParameterRow.getParamKey for active examples member keying

* split-rendering of examples in ParameterRow

* send disabled prop to JsonSchemaForm

* use content type to key request body active examples members

* remove debugger

* rewire RequestBodyEditor to be a controlled component

REVIEW: does this have perf implications?

* trigger synthetic onSelect events in ExamplesSelect

* prettier updates

* remove outdated Examples usage in RequestBody

* don't handle examples changes in ESVR

* make RequestBodyEditor semi-controlled

* don't default to an empty Map for request bodies

* add namespaceKey to ESVR for state mgmt

* don't key RequestBody activeExampleKeys on media type

* tweak ESVR isModifiedValueSelected calculation

* add trace class to ExamplesSelect

* remove usage of ESVR.currentNamespace

* reset to first example if currentExampleKey is invalid

* add default values to RequestBody rendering

* stringify things in ESVR

* avoid null select value (silences React warning)

* detect user inputs that match any examples member's value

* add trace class for json-schema-array

* shallowly convert namespace state, to preserve Immutable stucts in state

* stringify RBE values; don't trim JSON in editor

* match user input to an example when non-primitives are expressed in state as strings

* update Cypress

* don't apply sample values in JsonSchema_Object

* support disabling all JsonSchemaForm subcomponents

* Core tests

* style changes to accomodate Examples

* fix version-checking error in Response

* disable SCU for Responses

* don't stringify Select values

* ModelExample: default to Model tab if no example is available; provide a default no example message

* don't trim JSON ParamBody inputs

* read directly from 2.0 Response.schema instead of inferring a value

* show current Example information in RequestBody

* show label for Examples dropdown by default

* rework Response content ordering

* style disabled textareas like other read-only blocks

* meta: fix sourcemaps

* refactor ESVR setNameForNamespace

* protect second half of ternary expession

* cypress: `select.examples-select` => `.examples-select > select`

* clarify ModelExample.componentWillReceiveProps

* add gates/defaults to prevent issues in very bare-boned documents

* fix test block organization problem

* simplify RequestBodyEditor interface

* linter fixes

* prettier updates

* use plugin system for new components

* move ME Cypress helpers to other file
This commit is contained in:
kyle
2019-06-29 19:52:51 +01:00
committed by GitHub
parent 332ddaedcd
commit 23d7260f92
34 changed files with 3148 additions and 653 deletions

View File

@@ -1,9 +1,9 @@
import React, { Component } from "react"
import { Map } from "immutable"
import { Map, List } from "immutable"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
import win from "core/window"
import { getExtensions, getCommonExtensions, numberToString } from "core/utils"
import { getExtensions, getCommonExtensions, numberToString, stringify } from "core/utils"
export default class ParameterRow extends Component {
static propTypes = {
@@ -18,7 +18,9 @@ export default class ParameterRow extends Component {
specActions: PropTypes.object.isRequired,
pathMethod: PropTypes.array.isRequired,
getConfigs: PropTypes.func.isRequired,
specPath: ImPropTypes.list.isRequired
specPath: ImPropTypes.list.isRequired,
oas3Actions: PropTypes.object.isRequired,
oas3Selectors: PropTypes.object.isRequired,
}
constructor(props, context) {
@@ -29,7 +31,7 @@ export default class ParameterRow extends Component {
componentWillReceiveProps(props) {
let { specSelectors, pathMethod, rawParam } = props
let { isOAS3 } = specSelectors
let isOAS3 = specSelectors.isOAS3()
let parameterWithMeta = specSelectors.parameterWithMetaByIdentity(pathMethod, rawParam) || new Map()
// fallback, if the meta lookup fails
@@ -37,7 +39,7 @@ export default class ParameterRow extends Component {
let enumValue
if(isOAS3()) {
if(isOAS3) {
let schema = parameterWithMeta.get("schema") || Map()
enumValue = schema.get("enum")
} else {
@@ -74,6 +76,15 @@ export default class ParameterRow extends Component {
return onChange(rawParam, valueForUpstream, isXml)
}
_onExampleSelect = (key, /* { isSyntheticChange } = {} */) => {
this.props.oas3Actions.setActiveExamplesMember({
name: key,
pathMethod: this.props.pathMethod,
contextType: "parameters",
contextName: this.getParamKey()
})
}
onChangeIncludeEmpty = (newValue) => {
let { specActions, param, pathMethod } = this.props
const paramName = param.get("name")
@@ -82,10 +93,9 @@ export default class ParameterRow extends Component {
}
setDefaultValue = () => {
let { specSelectors, pathMethod, rawParam } = this.props
let paramWithMeta = specSelectors.parameterWithMetaByIdentity(pathMethod, rawParam)
let { specSelectors, pathMethod, rawParam, oas3Selectors } = this.props
let paramWithMeta = specSelectors.parameterWithMetaByIdentity(pathMethod, rawParam) || Map()
if (!paramWithMeta || paramWithMeta.get("value") !== undefined) {
return
@@ -100,20 +110,32 @@ export default class ParameterRow extends Component {
|| paramWithMeta.getIn(["schema", "example"])
|| paramWithMeta.getIn(["schema", "default"])
} else if (specSelectors.isOAS3()) {
newValue = paramWithMeta.get("example")
const currentExampleKey = oas3Selectors.activeExamplesMember(...pathMethod, "parameters", this.getParamKey())
newValue = paramWithMeta.getIn(["examples", currentExampleKey, "value"])
|| paramWithMeta.get("example")
|| paramWithMeta.getIn(["schema", "example"])
|| paramWithMeta.getIn(["schema", "default"])
}
if(newValue !== undefined) {
this.onChangeWrapper(numberToString(newValue))
this.onChangeWrapper(
List.isList(newValue) ? newValue : stringify(newValue)
)
}
}
}
render() {
let {param, rawParam, getComponent, getConfigs, isExecute, fn, onChangeConsumes, specSelectors, pathMethod, specPath} = this.props
getParamKey() {
const { param } = this.props
if(!param) return null
let { isOAS3 } = specSelectors
return `${param.get("name")}-${param.get("in")}`
}
render() {
let {param, rawParam, getComponent, getConfigs, isExecute, fn, onChangeConsumes, specSelectors, pathMethod, specPath, oas3Selectors} = this.props
let isOAS3 = specSelectors.isOAS3()
const { showExtensions, showCommonExtensions } = getConfigs()
@@ -121,6 +143,8 @@ export default class ParameterRow extends Component {
param = rawParam
}
if(!rawParam) return null
// const onChangeWrapper = (value) => onChange(param, value)
const JsonSchemaForm = getComponent("JsonSchemaForm")
const ParamBody = getComponent("ParamBody")
@@ -142,10 +166,12 @@ export default class ParameterRow extends Component {
const Markdown = getComponent("Markdown")
const ParameterExt = getComponent("ParameterExt")
const ParameterIncludeEmpty = getComponent("ParameterIncludeEmpty")
const ExamplesSelectValueRetainer = getComponent("ExamplesSelectValueRetainer")
const Example = getComponent("Example")
let paramWithMeta = specSelectors.parameterWithMetaByIdentity(pathMethod, rawParam)
let paramWithMeta = specSelectors.parameterWithMetaByIdentity(pathMethod, rawParam) || Map()
let format = param.get("format")
let schema = isOAS3 && isOAS3() ? param.get("schema") : param
let schema = isOAS3 ? param.get("schema") : param
let type = schema.get("type")
let isFormData = inType === "formData"
let isFormDataSupported = "FormData" in win
@@ -199,7 +225,7 @@ export default class ParameterRow extends Component {
{ format && <span className="prop-format">(${format})</span>}
</div>
<div className="parameter__deprecated">
{ isOAS3 && isOAS3() && param.get("deprecated") ? "deprecated": null }
{ isOAS3 && param.get("deprecated") ? "deprecated": null }
</div>
<div className="parameter__in">({ param.get("in") })</div>
{ !showCommonExtensions || !commonExt.size ? null : commonExt.map((v, key) => <ParameterExt key={`${key}-${v}`} xKey={key} xVal={v} /> )}
@@ -224,11 +250,28 @@ export default class ParameterRow extends Component {
{(isFormData && !isFormDataSupported) && <div>Error: your browser does not support FormData</div>}
{ bodyParam || !isExecute ? null
{
isOAS3 && param.get("examples") ? (
<section className="parameter-controls">
<ExamplesSelectValueRetainer
examples={param.get("examples")}
onSelect={this._onExampleSelect}
updateValue={this.onChangeWrapper}
getComponent={getComponent}
defaultToFirstExample={true}
currentKey={oas3Selectors.activeExamplesMember(...pathMethod, "parameters", this.getParamKey())}
currentUserInputValue={value}
/>
</section>
) : null
}
{ bodyParam ? null
: <JsonSchemaForm fn={fn}
getComponent={getComponent}
value={ value }
required={ required }
disabled={!isExecute}
description={param.get("description") ? `${param.get("name")} - ${param.get("description")}` : `${param.get("name")}`}
onChange={ this.onChangeWrapper }
errors={ paramWithMeta.get("errors") }
@@ -257,6 +300,18 @@ export default class ParameterRow extends Component {
: null
}
{
isOAS3 && param.get("examples") ? (
<Example
example={param.getIn([
"examples",
oas3Selectors.activeExamplesMember(...pathMethod, "parameters", this.getParamKey())
])}
getComponent={getComponent}
/>
) : null
}
</td>
</tr>
@@ -265,3 +320,4 @@ export default class ParameterRow extends Component {
}
}