Skip to content

Commit a777e9b

Browse files
authored
Merge pull request #3669 from shockey/bug/oas3-accept-controls
OAS3: Accept header control
2 parents ef5904f + ae5ad48 commit a777e9b

File tree

12 files changed

+221
-15
lines changed

12 files changed

+221
-15
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"dependencies": {
4242
"base64-js": "^1.2.0",
4343
"brace": "0.7.0",
44+
"classnames": "^2.2.5",
4445
"css.escape": "1.5.1",
4546
"deep-extend": "0.4.1",
4647
"expect": "1.20.2",

src/core/components/operation.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export default class Operation extends PureComponent {
2828
authSelectors: PropTypes.object,
2929
specActions: PropTypes.object.isRequired,
3030
specSelectors: PropTypes.object.isRequired,
31+
oas3Actions: PropTypes.object.isRequired,
3132
layoutActions: PropTypes.object.isRequired,
3233
layoutSelectors: PropTypes.object.isRequired,
3334
fn: PropTypes.object.isRequired,
@@ -117,7 +118,8 @@ export default class Operation extends PureComponent {
117118
specSelectors,
118119
authActions,
119120
authSelectors,
120-
getConfigs
121+
getConfigs,
122+
oas3Actions
121123
} = this.props
122124

123125
let summary = operation.get("summary")
@@ -265,6 +267,7 @@ export default class Operation extends PureComponent {
265267
getComponent={ getComponent }
266268
getConfigs={ getConfigs }
267269
specSelectors={ specSelectors }
270+
oas3Actions={oas3Actions}
268271
specActions={ specActions }
269272
produces={ produces }
270273
producesValue={ operation.get("produces_value") }

src/core/components/operations.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export default class Operations extends React.Component {
99
static propTypes = {
1010
specSelectors: PropTypes.object.isRequired,
1111
specActions: PropTypes.object.isRequired,
12+
oas3Actions: PropTypes.object.isRequired,
1213
getComponent: PropTypes.func.isRequired,
1314
layoutSelectors: PropTypes.object.isRequired,
1415
layoutActions: PropTypes.object.isRequired,
@@ -21,6 +22,7 @@ export default class Operations extends React.Component {
2122
let {
2223
specSelectors,
2324
specActions,
25+
oas3Actions,
2426
getComponent,
2527
layoutSelectors,
2628
layoutActions,
@@ -147,6 +149,8 @@ export default class Operations extends React.Component {
147149
specActions={ specActions }
148150
specSelectors={ specSelectors }
149151

152+
oas3Actions={oas3Actions}
153+
150154
layoutActions={ layoutActions }
151155
layoutSelectors={ layoutSelectors }
152156

src/core/components/response.jsx

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from "react"
22
import PropTypes from "prop-types"
3+
import cx from "classnames"
34
import { fromJS, Seq } from "immutable"
45
import { getSampleSchema, fromJSOrdered } from "core/utils"
56

@@ -46,13 +47,25 @@ export default class Response extends React.Component {
4647
getComponent: PropTypes.func.isRequired,
4748
specSelectors: PropTypes.object.isRequired,
4849
fn: PropTypes.object.isRequired,
49-
contentType: PropTypes.string
50+
contentType: PropTypes.string,
51+
controlsAcceptHeader: PropTypes.bool,
52+
onContentTypeChange: PropTypes.func
5053
}
5154

5255
static defaultProps = {
5356
response: fromJS({}),
57+
onContentTypeChange: () => {}
5458
};
5559

60+
_onContentTypeChange = (value) => {
61+
const { onContentTypeChange, controlsAcceptHeader } = this.props
62+
this.setState({ responseContentType: value })
63+
onContentTypeChange({
64+
value: value,
65+
controlsAcceptHeader
66+
})
67+
}
68+
5669
render() {
5770
let {
5871
code,
@@ -61,7 +74,8 @@ export default class Response extends React.Component {
6174
fn,
6275
getComponent,
6376
specSelectors,
64-
contentType
77+
contentType,
78+
controlsAcceptHeader
6579
} = this.props
6680

6781
let { inferSchema } = fn
@@ -106,11 +120,18 @@ export default class Response extends React.Component {
106120
<Markdown source={ response.get( "description" ) } />
107121
</div>
108122

109-
{ isOAS3 ? <ContentType
110-
value={this.state.responseContentType}
111-
contentTypes={ response.get("content") ? response.get("content").keySeq() : Seq() }
112-
onChange={(val) => this.setState({ responseContentType: val })}
113-
className="response-content-type" /> : null }
123+
{ isOAS3 ?
124+
<div className={cx("response-content-type", {
125+
"controls-accept-header": controlsAcceptHeader
126+
})}>
127+
<ContentType
128+
value={this.state.responseContentType}
129+
contentTypes={ response.get("content") ? response.get("content").keySeq() : Seq() }
130+
onChange={this._onContentTypeChange}
131+
/>
132+
{ controlsAcceptHeader ? <small>Controls <code>Accept</code> header.</small> : null }
133+
</div>
134+
: null }
114135

115136
{ example ? (
116137
<ModelExample

src/core/components/responses.jsx

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from "react"
22
import PropTypes from "prop-types"
33
import { fromJS } from "immutable"
4-
import { defaultStatusCode } from "core/utils"
4+
import { defaultStatusCode, getAcceptControllingResponse } from "core/utils"
55

66
export default class Responses extends React.Component {
77

@@ -14,6 +14,7 @@ export default class Responses extends React.Component {
1414
getComponent: PropTypes.func.isRequired,
1515
specSelectors: PropTypes.object.isRequired,
1616
specActions: PropTypes.object.isRequired,
17+
oas3Actions: PropTypes.object.isRequired,
1718
pathMethod: PropTypes.array.isRequired,
1819
displayRequestDuration: PropTypes.bool.isRequired,
1920
fn: PropTypes.object.isRequired,
@@ -29,8 +30,28 @@ export default class Responses extends React.Component {
2930

3031
onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue(this.props.pathMethod, val)
3132

33+
onResponseContentTypeChange = ({ controlsAcceptHeader, value }) => {
34+
const { oas3Actions, pathMethod } = this.props
35+
if(controlsAcceptHeader) {
36+
oas3Actions.setResponseContentType({
37+
value,
38+
pathMethod
39+
})
40+
}
41+
}
42+
3243
render() {
33-
let { responses, request, tryItOutResponse, getComponent, getConfigs, specSelectors, fn, producesValue, displayRequestDuration } = this.props
44+
let {
45+
responses,
46+
request,
47+
tryItOutResponse,
48+
getComponent,
49+
getConfigs,
50+
specSelectors,
51+
fn,
52+
producesValue,
53+
displayRequestDuration
54+
} = this.props
3455
let defaultCode = defaultStatusCode( responses )
3556

3657
const ContentType = getComponent( "contentType" )
@@ -39,6 +60,11 @@ export default class Responses extends React.Component {
3960

4061
let produces = this.props.produces && this.props.produces.size ? this.props.produces : Responses.defaultProps.produces
4162

63+
const isSpecOAS3 = specSelectors.isOAS3()
64+
65+
const acceptControllingResponse = isSpecOAS3 ?
66+
getAcceptControllingResponse(responses) : null
67+
4268
return (
4369
<div className="responses-wrapper">
4470
<div className="opblock-section-header">
@@ -78,7 +104,6 @@ export default class Responses extends React.Component {
78104
<tbody>
79105
{
80106
responses.entrySeq().map( ([code, response]) => {
81-
82107
let className = tryItOutResponse && tryItOutResponse.get("status") == code ? "response_current" : ""
83108
return (
84109
<Response key={ code }
@@ -88,6 +113,8 @@ export default class Responses extends React.Component {
88113
code={ code }
89114
response={ response }
90115
specSelectors={ specSelectors }
116+
controlsAcceptHeader={response === acceptControllingResponse}
117+
onContentTypeChange={this.onResponseContentTypeChange}
91118
contentType={ producesValue }
92119
getComponent={ getComponent }/>
93120
)

src/core/plugins/oas3/actions.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
export const UPDATE_SELECTED_SERVER = "oas3_set_servers"
55
export const UPDATE_REQUEST_BODY_VALUE = "oas3_set_request_body_value"
66
export const UPDATE_REQUEST_CONTENT_TYPE = "oas3_set_request_content_type"
7+
export const UPDATE_RESPONSE_CONTENT_TYPE = "oas3_set_response_content_type"
78
export const UPDATE_SERVER_VARIABLE_VALUE = "oas3_set_server_variable_value"
89

910
export function setSelectedServer (selectedServerUrl) {
@@ -27,6 +28,13 @@ export function setRequestContentType ({ value, pathMethod }) {
2728
}
2829
}
2930

31+
export function setResponseContentType ({ value, pathMethod }) {
32+
return {
33+
type: UPDATE_RESPONSE_CONTENT_TYPE,
34+
payload: { value, pathMethod }
35+
}
36+
}
37+
3038
export function setServerVariableValue ({ server, key, val }) {
3139
return {
3240
type: UPDATE_SERVER_VARIABLE_VALUE,

src/core/plugins/oas3/reducers.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import {
22
UPDATE_SELECTED_SERVER,
33
UPDATE_REQUEST_BODY_VALUE,
44
UPDATE_REQUEST_CONTENT_TYPE,
5-
UPDATE_SERVER_VARIABLE_VALUE
5+
UPDATE_SERVER_VARIABLE_VALUE,
6+
UPDATE_RESPONSE_CONTENT_TYPE
67
} from "./actions"
78

89
export default {
@@ -17,6 +18,10 @@ export default {
1718
let [path, method] = pathMethod
1819
return state.setIn( [ "requestData", path, method, "requestContentType" ], value)
1920
},
21+
[UPDATE_RESPONSE_CONTENT_TYPE]: (state, { payload: { value, pathMethod } } ) =>{
22+
let [path, method] = pathMethod
23+
return state.setIn( [ "requestData", path, method, "responseContentType" ], value)
24+
},
2025
[UPDATE_SERVER_VARIABLE_VALUE]: (state, { payload: { server, key, val } } ) =>{
2126
return state.setIn( [ "serverVariableValues", server, key ], val)
2227
},

src/core/plugins/oas3/selectors.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ export const requestContentType = onlyOAS3((state, path, method) => {
3030
}
3131
)
3232

33+
export const responseContentType = onlyOAS3((state, path, method) => {
34+
return state.getIn(["requestData", path, method, "responseContentType"]) || null
35+
}
36+
)
37+
3338
export const serverVariableValue = onlyOAS3((state, server, key) => {
3439
return state.getIn(["serverVariableValues", server, key]) || null
3540
}

src/core/plugins/spec/actions.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ export const executeRequest = (req) =>
218218
req.server = oas3Selectors.selectedServer()
219219
req.serverVariables = oas3Selectors.serverVariables(req.server).toJS()
220220
req.requestContentType = oas3Selectors.requestContentType(pathName, method)
221+
req.responseContentType = oas3Selectors.responseContentType(pathName, method) || "*/*"
221222
const requestBody = oas3Selectors.requestBodyValue(pathName, method)
222223

223224
if(isJSONObject(requestBody)) {

src/core/utils.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,5 +652,28 @@ export const shallowEqualKeys = (a,b, keys) => {
652652
})
653653
}
654654

655+
export function getAcceptControllingResponse(responses) {
656+
if(!Im.OrderedMap.isOrderedMap(responses)) {
657+
// wrong type!
658+
return null
659+
}
660+
661+
if(!responses.size) {
662+
// responses is empty
663+
return null
664+
}
665+
666+
const suitable2xxResponse = responses.find((res, k) => {
667+
return k.startsWith("2") && Object.keys(res.get("content") || {}).length > 0
668+
})
669+
670+
// try to find a suitable `default` responses
671+
const defaultResponse = responses.get("default") || Im.OrderedMap()
672+
const defaultResponseMediaTypes = (defaultResponse.get("content") || Im.OrderedMap()).keySeq().toJS()
673+
const suitableDefaultResponse = defaultResponseMediaTypes.length ? defaultResponse : null
674+
675+
return suitable2xxResponse || suitableDefaultResponse
676+
}
677+
655678
export const createDeepLinkPath = (str) => typeof str == "string" || str instanceof String ? str.trim().replace(/\s/g, "_") : ""
656-
export const escapeDeepLinkPath = (str) => cssEscape( createDeepLinkPath(str) )
679+
export const escapeDeepLinkPath = (str) => cssEscape( createDeepLinkPath(str) )

0 commit comments

Comments
 (0)