Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OAS3 multi examples in requestbody #3616

Closed
wants to merge 37 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
635c32c
Merge remote-tracking branch 'refs/remotes/swagger-api/master'
biggates Jul 13, 2017
d9a1d88
Merge remote-tracking branch 'refs/remotes/swagger-api/master'
biggates Jul 31, 2017
d9ec044
Merge remote-tracking branch 'refs/remotes/swagger-api/master'
biggates Aug 17, 2017
d01ccf1
Merge remote-tracking branch 'refs/remotes/swagger-api/master'
biggates Aug 25, 2017
38269dc
initial commit
biggates Aug 25, 2017
afa680a
add ExternalValue component
biggates Aug 25, 2017
1206ca5
ExternalValue renders as a link
biggates Aug 27, 2017
0ce6337
Render externalValue as plain <a> tag
biggates Aug 28, 2017
7a50bb5
Merge remote-tracking branch 'refs/remotes/swagger-api/master' into o…
biggates Aug 28, 2017
73e98e5
fixed display of example.value
biggates Aug 28, 2017
6123a84
fix lint errors
biggates Aug 28, 2017
a9a2445
Merge remote-tracking branch 'refs/remotes/swagger-api/master'
biggates Aug 28, 2017
67e6561
Merge branch 'master' into oas3/multi-examples-in-requestBody
biggates Aug 31, 2017
2c64f2c
Merge remote-tracking branch 'refs/remotes/swagger-api/master'
biggates Sep 1, 2017
48fd09e
Merge remote-tracking branch 'refs/remotes/swagger-api/master'
biggates Sep 28, 2017
5f033a2
Merge remote-tracking branch 'refs/remotes/swagger-api/master' into o…
biggates Sep 28, 2017
e61800c
Register commonent "Servers" in OAS3
biggates Sep 28, 2017
457486b
Merge remote-tracking branch 'refs/remotes/swagger-api/master' into o…
biggates Sep 29, 2017
0a49263
Fix typo
biggates Sep 29, 2017
f7ad8f5
Add support for single/multiple examples in responses
biggates Sep 30, 2017
de5b7e0
Merge remote-tracking branch 'refs/remotes/swagger-api/master'
biggates Sep 30, 2017
e6c78a4
Merge remote-tracking branch 'refs/remotes/swagger-api/master' into o…
biggates Sep 30, 2017
74e61ab
Fix eslint
biggates Sep 30, 2017
e2f352a
Merge branch 'master' into oas3/multi-examples-in-requestBody
biggates Oct 3, 2017
bd190a3
Merge remote-tracking branch 'refs/remotes/origin/master' into oas3/m…
biggates Oct 4, 2017
b8dc26c
Merge branch 'oas3/multi-examples-in-requestBody' of https://github.c…
biggates Oct 4, 2017
6798d93
Merge remote-tracking branch 'upstream/master' into oas3/multi-exampl…
biggates Oct 5, 2017
d6bb81b
Merge remote-tracking branch 'refs/remotes/swagger-api/master' into o…
biggates Oct 30, 2017
e2431b8
Merge remote-tracking branch 'refs/remotes/swagger-api/master' into o…
biggates Nov 1, 2017
fb26c5b
Example(s) in Parameters
biggates Nov 1, 2017
fbeb109
Fix lint errors
biggates Nov 1, 2017
d999392
merge from remote/master
biggates Nov 1, 2017
9465169
Merge remote-tracking branch 'refs/remotes/swagger-api/master' into o…
biggates Nov 2, 2017
1a0baff
Fixed example value in inputs in "Try it out" mode
biggates Nov 2, 2017
361f003
Convert previously JS object to immutable.js version
biggates Nov 2, 2017
a23c74f
Fix eslint
biggates Nov 2, 2017
f1f383e
Merge branch 'master' into oas3/multi-examples-in-requestBody
shockey Nov 11, 2017
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
53 changes: 45 additions & 8 deletions src/core/components/model-example.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
import React from "react"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"

export default class ModelExample extends React.Component {
static propTypes = {
getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
schema: PropTypes.object.isRequired,
example: PropTypes.any.isRequired,
schema: PropTypes.object,
example: PropTypes.any,
examples: ImPropTypes.orderedMap,
isExecute: PropTypes.bool,
getConfigs: PropTypes.func.isRequired
}

constructor(props, context) {
super(props, context)

let { getConfigs } = this.props
let { defaultModelRendering } = getConfigs()
if (defaultModelRendering !== "example" && defaultModelRendering !== "model") {
Expand All @@ -31,16 +34,36 @@ export default class ModelExample extends React.Component {
})
}

formatValue = (value) => {
if(typeof(value) === "string") {
return value
} else {
return JSON.stringify(value, null, 2)
}
}

render() {
let { getComponent, specSelectors, schema, example, isExecute, getConfigs } = this.props
let { getComponent, specSelectors, schema, example, examples, isExecute, getConfigs } = this.props
let { defaultModelExpandDepth } = getConfigs()
const ModelWrapper = getComponent("ModelWrapper")
const HighlightCode = getComponent("highlightCode")
const Markdown = getComponent("Markdown")
const ExternalValue = getComponent("ExternalValue")

// TODO fetch externalValue and display it on demand
return <div>
<ul className="tab">
<li className={ "tabitem" + ( isExecute || this.state.activeTab === "example" ? " active" : "") }>
<a className="tablinks" data-name="example" onClick={ this.activeTab }>Example Value</a>
</li>

{ examples && examples.map( (item, key) => {
return ( !isExecute && <li key={"examples_key_" + key} className={"tabitem" + ( isExecute || this.state.activeTab === "example_" + key ? " active" : "") }>
<a className="tablinks" data-name={"example_" + key} onClick={ this.activeTab }>Example: {key}</a>
</li> )
} ).toArray()
}

{ schema ? <li className={ "tabitem" + ( !isExecute && this.state.activeTab === "model" ? " active" : "") }>
<a className={ "tablinks" + ( isExecute ? " inactive" : "" )} data-name="model" onClick={ this.activeTab }>Model</a>
</li> : null }
Expand All @@ -49,13 +72,27 @@ export default class ModelExample extends React.Component {
{
(isExecute || this.state.activeTab === "example") && example
}
{
examples && examples.map( (item, key) => {
return ((!isExecute && this.state.activeTab === "example_" + key) && (
<div key={"example_div_key_" + key} className="example-wrapper">
{ item.has("summary") && <h6 className="example-summary">{ item.get("summary") }</h6> }
{
item.has("description") && <div className="example-description">
<Markdown source={ item.get("description") } />
</div>
}
{ item.has("value") && <HighlightCode value={ this.formatValue(item.get("value")) } /> }
{ item.has("externalValue") && <ExternalValue location={ item.get("externalValue") } getComponent={ getComponent } /> }
</div>
))
} ).toArray()
}
{
!isExecute && this.state.activeTab === "model" && <ModelWrapper schema={ schema }
getComponent={ getComponent }
specSelectors={ specSelectors }
expandDepth={ defaultModelExpandDepth } />


getComponent={ getComponent }
specSelectors={ specSelectors }
expandDepth={ defaultModelExpandDepth } />
}
</div>
</div>
Expand Down
45 changes: 41 additions & 4 deletions src/core/components/parameter-row.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React, { Component } from "react"
import { Map } from "immutable"
import PropTypes from "prop-types"
import win from "core/window"
import HighlightCode from "./highlight-code"
import { formatParamValue } from "core/plugins/oas3/utils"

export default class ParameterRow extends Component {
static propTypes = {
Expand Down Expand Up @@ -33,6 +35,7 @@ export default class ParameterRow extends Component {
let { isOAS3 } = specSelectors

let example = param.get("example")
let examples = param.get("examples")
let defaultValue = param.get("default")
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
let enumValue
Expand All @@ -49,12 +52,18 @@ export default class ParameterRow extends Component {

if ( paramValue !== undefined ) {
value = paramValue
} else if ( example !== undefined ) {
value = example
} else if ( defaultValue !== undefined) {
value = defaultValue
} else if ( example !== undefined ) {
value = example
} else if ( examples !== undefined && examples.size > 0 ) {
value = examples.first().value
} else if ( param.get("required") && enumValue && enumValue.size ) {
value = enumValue.first()
} else if ( example !== undefined ) {
value = example.toObject()
} else if ( examples !== undefined && examples.size > 0 ) {
value = examples.first().value
}

if ( value !== undefined ) {
Expand Down Expand Up @@ -100,6 +109,20 @@ export default class ParameterRow extends Component {
let itemType = param.getIn(isOAS3 && isOAS3() ? ["schema", "items", "type"] : ["items", "type"])
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
let value = parameter ? parameter.get("value") : ""
let examples = isOAS3 && isOAS3() && param.get("examples")
let example

if(isOAS3 && isOAS3() && !examples){
// in OAS3, "example" and "examples" are mutually exclusive.
// So "example" are handled only when "examples" is not present.
example = param.get("example")

if(example) {
example = <HighlightCode value={ formatParamValue(example, parameter) }/>
} else {
example = <HighlightCode value={ formatParamValue(value, parameter) }/>
}
}

return (
<tr>
Expand All @@ -122,7 +145,7 @@ export default class ParameterRow extends Component {
{ bodyParam || !isExecute ? null
: <JsonSchemaForm fn={fn}
getComponent={getComponent}
value={ value }
value={ formatParamValue(value, parameter) }
required={ required }
description={param.get("description") ? `${param.get("name")} - ${param.get("description")}` : `${param.get("name")}`}
onChange={ this.onChangeWrapper }
Expand All @@ -136,10 +159,24 @@ export default class ParameterRow extends Component {
isExecute={ isExecute }
specSelectors={ specSelectors }
schema={ schema }
example={ bodyParam }/>
example={ bodyParam }
examples={ examples }/>
: null
}

{/* for non-body params with example(s) */}
{
(!bodyParam && examples) && <ModelExample
getComponent={ getComponent }
example={ example }
examples={ examples }
getConfigs={ getConfigs }
isExecute={ isExecute }
specSelectors={ specSelectors }
schema={ schema } />
}


</td>

</tr>
Expand Down
54 changes: 33 additions & 21 deletions src/core/components/response.jsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
import React from "react"
import PropTypes from "prop-types"
import cx from "classnames"
import { fromJS, Seq } from "immutable"
import { getSampleSchema, fromJSOrdered } from "core/utils"
import { fromJS, OrderedMap, Seq } from "immutable"
import { fromJSOrdered, getSampleSchema } from "core/utils"

const getExampleComponent = ( sampleResponse, examples, HighlightCode ) => {
if ( sampleResponse ) { return <div>
<HighlightCode className="example" value={ sampleResponse } />
</div>
}
if ( examples && examples.size ) {
return examples.entrySeq().map( ([ key, example ]) => {
let exampleValue = example
if ( example.toJS ) {
try {
exampleValue = JSON.stringify(example.toJS(), null, 2)
}
catch(e) {
exampleValue = String(example)
}
let [key, example] = examples.entrySeq().first()

let exampleValue = example
if (example.toJS) {
try {
exampleValue = JSON.stringify(example.toJS(), null, 2)
}
catch (e) {
exampleValue = String(example)
}
}

return (<div key={ key }>
<h5>{ key }</h5>
<HighlightCode className="example" value={ exampleValue } />
</div>)
}).toArray()
return (<div key={key}>
<h5>{key}</h5>
<HighlightCode className="example" value={exampleValue}/>
</div>)
}

if ( sampleResponse ) { return <div>
<HighlightCode className="example" value={ sampleResponse } />
</div>
}
return null
}

Expand Down Expand Up @@ -102,6 +102,16 @@ export default class Response extends React.Component {
includeReadOnly: true
}) : null
schema = oas3SchemaForContentType ? inferSchema(oas3SchemaForContentType.toJS()) : null
examples = response.getIn(["content", this.state.responseContentType, "examples"])

if(!examples) {
// The example object is mutually exclusive of the examples object. Prefer `examples` here.
let example = response.getIn(["content", this.state.responseContentType, "example"])

if(example) {
examples = OrderedMap({"Example": example})
}
}
} else {
schema = inferSchema(response.toJS())
sampleResponse = schema ? getSampleSchema(schema, contentType, {
Expand Down Expand Up @@ -149,7 +159,9 @@ export default class Response extends React.Component {
getConfigs={ getConfigs }
specSelectors={ specSelectors }
schema={ fromJSOrdered(schema) }
example={ example }/>
example={ example }
examples={ examples }
/>
) : null}

{ headers ? (
Expand Down
33 changes: 33 additions & 0 deletions src/core/plugins/oas3/components/external-value.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from "react"
import PropTypes from "prop-types"

/**
* ExternalValue is a <HighlightCode> with content set to an external location.
* usage: <ExternalValue location="http://example.com/path/to/file" />
*/
export default class ExternalValue extends React.Component {
static propTypes = {
getComponent: PropTypes.func.isRequired,
location: PropTypes.string.isRequired
}

constructor(props, context) {
super(props, context)
}

render() {
let { location } = this.props
// const Markdown = getComponent("Markdown")

// TODO should externalValue be fetched and displayed inline?

return (
<div>
<a href={location} target="_blank" title={location} >Open ExternalValue</a>
</div>
)
}
}



7 changes: 5 additions & 2 deletions src/core/plugins/oas3/components/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

import Callbacks from "./callbacks"
import RequestBody from "./request-body"
import OperationLink from "./operation-link.jsx"
import ExternalValue from "./external-value.jsx"
import Servers from "./servers"
import RequestBodyEditor from "./request-body-editor"
import HttpAuth from "./http-auth"
Expand All @@ -9,7 +11,8 @@ export default {
Callbacks,
HttpAuth,
RequestBody,
operationLink: OperationLink,
ExternalValue,
Servers,
RequestBodyEditor,
operationLink: OperationLink
RequestBodyEditor
}
8 changes: 6 additions & 2 deletions src/core/plugins/oas3/components/request-body.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
import { OrderedMap } from "immutable"
import { memoizedGetExamples } from "core/plugins/oas3/utils"

const RequestBody = ({
requestBody,
Expand All @@ -22,6 +23,8 @@ const RequestBody = ({

const mediaTypeValue = requestBodyContent.get(contentType)

const examples = memoizedGetExamples(mediaTypeValue.get("examples"))

return <div>
{ requestBodyDescription &&
<Markdown source={requestBodyDescription} />
Expand All @@ -40,8 +43,9 @@ const RequestBody = ({
getComponent={getComponent}
isExecute={isExecute}
specSelectors={specSelectors}
/>}
/>
/>}
examples={examples}
/>
</div>
}

Expand Down
27 changes: 27 additions & 0 deletions src/core/plugins/oas3/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import memoizee from "memoizee"
import { fromJS } from "immutable"

/**
* Convert OAS3 "Examples" (in requestBody) section and convert to Map
* @param examplesInSpec {object} "examples" in OAS3 spec
* @returns {Map} Map {name: {summary, description, value, externalValue}}
*/
export const getExamples = (examplesInSpec) => {
return fromJS(examplesInSpec)
}

export const memoizedGetExamples = memoizee(getExamples)

/**
* Convert 'value' to serialized format, so can be handled correctly in <input>
* @param value value
* @param parameter {OrderedMap} parameter in spec
*/
export const formatParamValue = (value, parameter) => {
if (typeof ( value ) === "string") {
return value
} else {
// TODO apply 'style', 'explode', 'allowReserved' in OAS3
return JSON.stringify(value, null, 2)
}
}
Loading