Skip to content

Commit 7af7121

Browse files
authored
fix: streamline management of user-selected produces and consumes values (#4137)
* Remove produces/consumes setter from OperationContainer * Store consumes/produces information in `meta` key * Migrate produces value state usage to `meta` key * use meta consumes data for isXml check * Fix failing tests * normalize action name casing * restore correct produces fallback value logic
1 parent ec48902 commit 7af7121

File tree

7 files changed

+135
-39
lines changed

7 files changed

+135
-39
lines changed

src/core/components/operation.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ export default class Operation extends PureComponent {
248248
oas3Actions={oas3Actions}
249249
specActions={ specActions }
250250
produces={ produces }
251-
producesValue={ operation.get("produces_value") }
251+
producesValue={ specSelectors.currentProducesFor([path, method]) }
252252
specPath={specPath.push("responses")}
253253
path={ path }
254254
method={ method }

src/core/containers/OperationContainer.jsx

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -82,27 +82,9 @@ export default class OperationContainer extends PureComponent {
8282
}
8383

8484
componentWillReceiveProps(nextProps) {
85-
const defaultContentType = "application/json"
86-
let { specActions, path, method, op } = nextProps
87-
let operation = op.get("operation")
88-
let producesValue = operation.get("produces_value")
89-
let produces = operation.get("produces")
90-
let consumes = operation.get("consumes")
91-
let consumesValue = operation.get("consumes_value")
92-
9385
if(nextProps.response !== this.props.response) {
9486
this.setState({ executeInProgress: false })
9587
}
96-
97-
if (producesValue === undefined) {
98-
producesValue = produces && produces.size ? produces.first() : defaultContentType
99-
specActions.changeProducesValue([path, method], producesValue)
100-
}
101-
102-
if (consumesValue === undefined) {
103-
consumesValue = consumes && consumes.size ? consumes.first() : defaultContentType
104-
specActions.changeConsumesValue([path, method], consumesValue)
105-
}
10688
}
10789

10890
toggleShown =() => {

src/core/plugins/spec/actions.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export const LOG_REQUEST = "spec_log_request"
1919
export const CLEAR_RESPONSE = "spec_clear_response"
2020
export const CLEAR_REQUEST = "spec_clear_request"
2121
export const CLEAR_VALIDATE_PARAMS = "spec_clear_validate_param"
22-
export const UPDATE_OPERATION_VALUE = "spec_update_operation_value"
22+
export const UPDATE_OPERATION_META_VALUE = "spec_update_operation_meta_value"
2323
export const UPDATE_RESOLVED = "spec_update_resolved"
2424
export const SET_SCHEME = "set_scheme"
2525

@@ -150,14 +150,14 @@ export function clearValidateParams( payload ){
150150

151151
export function changeConsumesValue(path, value) {
152152
return {
153-
type: UPDATE_OPERATION_VALUE,
153+
type: UPDATE_OPERATION_META_VALUE,
154154
payload:{ path, value, key: "consumes_value" }
155155
}
156156
}
157157

158158
export function changeProducesValue(path, value) {
159159
return {
160-
type: UPDATE_OPERATION_VALUE,
160+
type: UPDATE_OPERATION_META_VALUE,
161161
payload:{ path, value, key: "produces_value" }
162162
}
163163
}

src/core/plugins/spec/reducers.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
SET_REQUEST,
1313
SET_MUTATED_REQUEST,
1414
UPDATE_RESOLVED,
15-
UPDATE_OPERATION_VALUE,
15+
UPDATE_OPERATION_META_VALUE,
1616
CLEAR_RESPONSE,
1717
CLEAR_REQUEST,
1818
CLEAR_VALIDATE_PARAMS,
@@ -52,8 +52,8 @@ export default {
5252
},
5353

5454
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod, isOAS3 } } ) => {
55-
let operation = state.getIn( [ "resolved", "paths", ...pathMethod ] )
56-
let isXml = /xml/i.test(operation.get("consumes_value"))
55+
let meta = state.getIn( [ "meta", "paths", ...pathMethod ] )
56+
let isXml = /xml/i.test(meta.get("consumes_value"))
5757

5858
return state.updateIn( [ "resolved", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
5959
return parameters.withMutations( parameters => {
@@ -107,12 +107,17 @@ export default {
107107
return state.setIn( [ "mutatedRequests", path, method ], fromJSOrdered(req))
108108
},
109109

110-
[UPDATE_OPERATION_VALUE]: (state, { payload: { path, value, key } }) => {
110+
[UPDATE_OPERATION_META_VALUE]: (state, { payload: { path, value, key } }) => {
111+
// path is a pathMethod tuple... can't change the name now.
111112
let operationPath = ["resolved", "paths", ...path]
113+
let metaPath = ["meta", "paths", ...path]
114+
112115
if(!state.getIn(operationPath)) {
116+
// do nothing if the operation does not exist
113117
return state
114118
}
115-
return state.setIn([...operationPath, key], fromJS(value))
119+
120+
return state.setIn([...metaPath, key], fromJS(value))
116121
},
117122

118123
[CLEAR_RESPONSE]: (state, { payload: { path, method } } ) =>{

src/core/plugins/spec/selectors.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -306,18 +306,21 @@ export function parametersIncludeType(parameters, typeValue="") {
306306
export function contentTypeValues(state, pathMethod) {
307307
pathMethod = pathMethod || []
308308
let op = spec(state).getIn(["paths", ...pathMethod], fromJS({}))
309+
let meta = state.getIn(["meta", "paths", ...pathMethod], fromJS({}))
310+
let producesValue = currentProducesFor(state, pathMethod)
311+
309312
const parameters = op.get("parameters") || new List()
310313

311314
const requestContentType = (
312-
op.get("consumes_value") ? op.get("consumes_value")
315+
meta.get("consumes_value") ? meta.get("consumes_value")
313316
: parametersIncludeType(parameters, "file") ? "multipart/form-data"
314317
: parametersIncludeType(parameters, "formData") ? "application/x-www-form-urlencoded"
315318
: undefined
316319
)
317320

318321
return fromJS({
319322
requestContentType,
320-
responseContentType: op.get("produces_value")
323+
responseContentType: producesValue
321324
})
322325
}
323326

@@ -327,6 +330,24 @@ export function operationConsumes(state, pathMethod) {
327330
return spec(state).getIn(["paths", ...pathMethod, "consumes"], fromJS({}))
328331
}
329332

333+
// Get the currently selected produces value for an operation
334+
export function currentProducesFor(state, pathMethod) {
335+
pathMethod = pathMethod || []
336+
337+
const operation = spec(state).getIn(["paths", ...pathMethod], null)
338+
339+
if(operation === null) {
340+
// return nothing if the operation does not exist
341+
return
342+
}
343+
344+
const currentProducesValue = state.getIn(["meta", "paths", ...pathMethod, "produces_value"], null)
345+
const firstProducesArrayItem = operation.getIn(["produces", 0], null)
346+
347+
return currentProducesValue || firstProducesArrayItem || "application/json"
348+
349+
}
350+
330351
export const operationScheme = ( state, path, method ) => {
331352
let url = state.get("url")
332353
let matchResult = url.match(/^([a-z][a-z0-9+\-.]*):/)

test/core/plugins/spec-reducer.js renamed to test/core/plugins/spec/reducer.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import reducer from "corePlugins/spec/reducers"
55

66
describe("spec plugin - reducer", function(){
77

8-
describe("update operation value", function() {
9-
it("should update the operation at the specified key", () => {
10-
const updateOperationValue = reducer["spec_update_operation_value"]
8+
describe("update operation meta value", function() {
9+
it("should update the operation metadata at the specified key", () => {
10+
const updateOperationValue = reducer["spec_update_operation_meta_value"]
1111

1212
const state = fromJS({
1313
resolved: {
@@ -34,7 +34,15 @@ describe("spec plugin - reducer", function(){
3434
"paths": {
3535
"/pet": {
3636
"post": {
37-
"description": "my operation",
37+
"description": "my operation"
38+
}
39+
}
40+
}
41+
},
42+
meta: {
43+
paths: {
44+
"/pet": {
45+
post: {
3846
"consumes_value": "application/json"
3947
}
4048
}
@@ -46,7 +54,7 @@ describe("spec plugin - reducer", function(){
4654
})
4755

4856
it("shouldn't throw an error if we try to update the consumes_value of a null operation", () => {
49-
const updateOperationValue = reducer["spec_update_operation_value"]
57+
const updateOperationValue = reducer["spec_update_operation_meta_value"]
5058

5159
const state = fromJS({
5260
resolved: {

test/core/plugins/spec/selectors.js

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ describe("spec plugin - selectors", function(){
5656
// Given
5757
let state = fromJS({
5858
resolved: {
59+
paths: {
60+
"/one": {
61+
get: {}
62+
}
63+
}
64+
},
65+
meta: {
5966
paths: {
6067
"/one": {
6168
get: {
@@ -76,20 +83,93 @@ describe("spec plugin - selectors", function(){
7683
})
7784
})
7885

86+
it("should default to the first `produces` array value if current is not set", function(){
87+
// Given
88+
let state = fromJS({
89+
resolved: {
90+
paths: {
91+
"/one": {
92+
get: {
93+
produces: [
94+
"application/xml",
95+
"application/whatever"
96+
]
97+
}
98+
}
99+
}
100+
},
101+
meta: {
102+
paths: {
103+
"/one": {
104+
get: {
105+
"consumes_value": "one"
106+
}
107+
}
108+
}
109+
}
110+
})
111+
112+
// When
113+
let contentTypes = contentTypeValues(state, [ "/one", "get" ])
114+
// Then
115+
expect(contentTypes.toJS()).toEqual({
116+
requestContentType: "one",
117+
responseContentType: "application/xml"
118+
})
119+
})
120+
121+
it("should default to `application/json` if a default produces value is not available", function(){
122+
// Given
123+
let state = fromJS({
124+
resolved: {
125+
paths: {
126+
"/one": {
127+
get: {}
128+
}
129+
}
130+
},
131+
meta: {
132+
paths: {
133+
"/one": {
134+
get: {
135+
"consumes_value": "one"
136+
}
137+
}
138+
}
139+
}
140+
})
141+
142+
// When
143+
let contentTypes = contentTypeValues(state, [ "/one", "get" ])
144+
// Then
145+
expect(contentTypes.toJS()).toEqual({
146+
requestContentType: "one",
147+
responseContentType: "application/json"
148+
})
149+
})
150+
79151
it("should prioritize consumes value first from an operation", function(){
80152
// Given
81153
let state = fromJS({
82154
resolved: {
83155
paths: {
84156
"/one": {
85157
get: {
86-
"consumes_value": "one",
87-
"parameters": [{
158+
"parameters": [{
88159
"type": "file"
89160
}],
90161
}
91162
}
92163
}
164+
},
165+
meta: {
166+
paths: {
167+
"/one": {
168+
get: {
169+
"consumes_value": "one",
170+
}
171+
}
172+
}
93173
}
94174
})
95175

@@ -106,7 +186,7 @@ describe("spec plugin - selectors", function(){
106186
paths: {
107187
"/one": {
108188
get: {
109-
"parameters": [{
189+
"parameters": [{
110190
"type": "file"
111191
}],
112192
}
@@ -128,7 +208,7 @@ describe("spec plugin - selectors", function(){
128208
paths: {
129209
"/one": {
130210
get: {
131-
"parameters": [{
211+
"parameters": [{
132212
"type": "formData"
133213
}],
134214
}
@@ -143,7 +223,7 @@ describe("spec plugin - selectors", function(){
143223
expect(contentTypes.toJS().requestContentType).toEqual("application/x-www-form-urlencoded")
144224
})
145225

146-
it("should be ok, if no operation found", function(){
226+
it("should return nothing, if the operation does not exist", function(){
147227
// Given
148228
let state = fromJS({ })
149229

0 commit comments

Comments
 (0)