fix: path-item $ref produces/consumes inheritance (via #5049)
* implement a selector for consumes options * fix incorrect comment, test names * add `consumesOptionsFor` selector * use `consumesOptionsFor` and drop `operationConsumes`
This commit is contained in:
@@ -83,7 +83,6 @@ export default class Operation extends PureComponent {
|
|||||||
|
|
||||||
let operation = operationProps.getIn(["op"])
|
let operation = operationProps.getIn(["op"])
|
||||||
let responses = operation.get("responses")
|
let responses = operation.get("responses")
|
||||||
let produces = operation.get("produces")
|
|
||||||
let parameters = getList(operation, ["parameters"])
|
let parameters = getList(operation, ["parameters"])
|
||||||
let operationScheme = specSelectors.operationScheme(path, method)
|
let operationScheme = specSelectors.operationScheme(path, method)
|
||||||
let isShownKey = ["operations", tag, operationId]
|
let isShownKey = ["operations", tag, operationId]
|
||||||
@@ -216,7 +215,7 @@ export default class Operation extends PureComponent {
|
|||||||
specSelectors={ specSelectors }
|
specSelectors={ specSelectors }
|
||||||
oas3Actions={oas3Actions}
|
oas3Actions={oas3Actions}
|
||||||
specActions={ specActions }
|
specActions={ specActions }
|
||||||
produces={ produces }
|
produces={specSelectors.producesOptionsFor([path, method]) }
|
||||||
producesValue={ specSelectors.currentProducesFor([path, method]) }
|
producesValue={ specSelectors.currentProducesFor([path, method]) }
|
||||||
specPath={specPath.push("responses")}
|
specPath={specPath.push("responses")}
|
||||||
path={ path }
|
path={ path }
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ export default class ParameterRow extends Component {
|
|||||||
: <ParamBody getComponent={getComponent}
|
: <ParamBody getComponent={getComponent}
|
||||||
fn={fn}
|
fn={fn}
|
||||||
param={param}
|
param={param}
|
||||||
consumes={ specSelectors.operationConsumes(pathMethod) }
|
consumes={ specSelectors.consumesOptionsFor(pathMethod) }
|
||||||
consumesValue={ specSelectors.contentTypeValues(pathMethod).get("requestContentType") }
|
consumesValue={ specSelectors.contentTypeValues(pathMethod).get("requestContentType") }
|
||||||
onChange={this.onChangeWrapper}
|
onChange={this.onChangeWrapper}
|
||||||
onChangeConsumes={onChangeConsumes}
|
onChangeConsumes={onChangeConsumes}
|
||||||
|
|||||||
@@ -401,12 +401,6 @@ export function contentTypeValues(state, pathMethod) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the consumes/produces by path
|
|
||||||
export function operationConsumes(state, pathMethod) {
|
|
||||||
pathMethod = pathMethod || []
|
|
||||||
return specJsonWithResolvedSubtrees(state).getIn(["paths", ...pathMethod, "consumes"], fromJS({}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the currently selected produces value for an operation
|
// Get the currently selected produces value for an operation
|
||||||
export function currentProducesFor(state, pathMethod) {
|
export function currentProducesFor(state, pathMethod) {
|
||||||
pathMethod = pathMethod || []
|
pathMethod = pathMethod || []
|
||||||
@@ -425,6 +419,48 @@ export function currentProducesFor(state, pathMethod) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the produces options for an operation
|
||||||
|
export function producesOptionsFor(state, pathMethod) {
|
||||||
|
pathMethod = pathMethod || []
|
||||||
|
|
||||||
|
const spec = specJsonWithResolvedSubtrees(state)
|
||||||
|
const operation = spec.getIn([ "paths", ...pathMethod], null)
|
||||||
|
|
||||||
|
if(operation === null) {
|
||||||
|
// return nothing if the operation does not exist
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const [path] = pathMethod
|
||||||
|
|
||||||
|
const operationProduces = operation.get("produces", null)
|
||||||
|
const pathItemProduces = spec.getIn(["paths", path, "produces"], null)
|
||||||
|
const globalProduces = spec.getIn(["produces"], null)
|
||||||
|
|
||||||
|
return operationProduces || pathItemProduces || globalProduces
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the consumes options for an operation
|
||||||
|
export function consumesOptionsFor(state, pathMethod) {
|
||||||
|
pathMethod = pathMethod || []
|
||||||
|
|
||||||
|
const spec = specJsonWithResolvedSubtrees(state)
|
||||||
|
const operation = spec.getIn(["paths", ...pathMethod], null)
|
||||||
|
|
||||||
|
if (operation === null) {
|
||||||
|
// return nothing if the operation does not exist
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const [path] = pathMethod
|
||||||
|
|
||||||
|
const operationConsumes = operation.get("consumes", null)
|
||||||
|
const pathItemConsumes = spec.getIn(["paths", path, "consumes"], null)
|
||||||
|
const globalConsumes = spec.getIn(["consumes"], null)
|
||||||
|
|
||||||
|
return operationConsumes || pathItemConsumes || globalConsumes
|
||||||
|
}
|
||||||
|
|
||||||
export const operationScheme = ( state, path, method ) => {
|
export const operationScheme = ( state, path, method ) => {
|
||||||
let url = state.get("url")
|
let url = state.get("url")
|
||||||
let matchResult = url.match(/^([a-z][a-z0-9+\-.]*):/)
|
let matchResult = url.match(/^([a-z][a-z0-9+\-.]*):/)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
contentTypeValues,
|
contentTypeValues,
|
||||||
operationScheme,
|
operationScheme,
|
||||||
specJsonWithResolvedSubtrees,
|
specJsonWithResolvedSubtrees,
|
||||||
operationConsumes
|
producesOptionsFor,
|
||||||
} from "corePlugins/spec/selectors"
|
} from "corePlugins/spec/selectors"
|
||||||
|
|
||||||
import Petstore from "./assets/petstore.json"
|
import Petstore from "./assets/petstore.json"
|
||||||
@@ -15,7 +15,8 @@ import {
|
|||||||
operationWithMeta,
|
operationWithMeta,
|
||||||
parameterWithMeta,
|
parameterWithMeta,
|
||||||
parameterWithMetaByIdentity,
|
parameterWithMetaByIdentity,
|
||||||
parameterInclusionSettingFor
|
parameterInclusionSettingFor,
|
||||||
|
consumesOptionsFor
|
||||||
} from "../../../../src/core/plugins/spec/selectors"
|
} from "../../../../src/core/plugins/spec/selectors"
|
||||||
|
|
||||||
describe("spec plugin - selectors", function(){
|
describe("spec plugin - selectors", function(){
|
||||||
@@ -253,34 +254,6 @@ describe("spec plugin - selectors", function(){
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("operationConsumes", function(){
|
|
||||||
it("should return the operationConsumes for an operation", function(){
|
|
||||||
// Given
|
|
||||||
let state = fromJS({
|
|
||||||
json: {
|
|
||||||
paths: {
|
|
||||||
"/one": {
|
|
||||||
get: {
|
|
||||||
consumes: [
|
|
||||||
"application/xml",
|
|
||||||
"application/something-else"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// When
|
|
||||||
let contentTypes = operationConsumes(state, [ "/one", "get" ])
|
|
||||||
// Then
|
|
||||||
expect(contentTypes.toJS()).toEqual([
|
|
||||||
"application/xml",
|
|
||||||
"application/something-else"
|
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("operationScheme", function(){
|
describe("operationScheme", function(){
|
||||||
|
|
||||||
it("should return the correct scheme for a remote spec that doesn't specify a scheme", function(){
|
it("should return the correct scheme for a remote spec that doesn't specify a scheme", function(){
|
||||||
@@ -751,4 +724,264 @@ describe("spec plugin - selectors", function(){
|
|||||||
expect(result).toEqual(true)
|
expect(result).toEqual(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
describe("producesOptionsFor", function() {
|
||||||
|
it("should return an operation produces value", function () {
|
||||||
|
const state = fromJS({
|
||||||
|
json: {
|
||||||
|
paths: {
|
||||||
|
"/": {
|
||||||
|
"get": {
|
||||||
|
description: "my operation",
|
||||||
|
produces: [
|
||||||
|
"operation/one",
|
||||||
|
"operation/two",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = producesOptionsFor(state, ["/", "get"])
|
||||||
|
|
||||||
|
expect(result.toJS()).toEqual([
|
||||||
|
"operation/one",
|
||||||
|
"operation/two",
|
||||||
|
])
|
||||||
|
})
|
||||||
|
it("should return a path item produces value", function () {
|
||||||
|
const state = fromJS({
|
||||||
|
json: {
|
||||||
|
paths: {
|
||||||
|
"/": {
|
||||||
|
"get": {
|
||||||
|
description: "my operation",
|
||||||
|
produces: [
|
||||||
|
"path-item/one",
|
||||||
|
"path-item/two",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = producesOptionsFor(state, ["/", "get"])
|
||||||
|
|
||||||
|
expect(result.toJS()).toEqual([
|
||||||
|
"path-item/one",
|
||||||
|
"path-item/two",
|
||||||
|
])
|
||||||
|
})
|
||||||
|
it("should return a global produces value", function () {
|
||||||
|
const state = fromJS({
|
||||||
|
json: {
|
||||||
|
produces: [
|
||||||
|
"global/one",
|
||||||
|
"global/two",
|
||||||
|
],
|
||||||
|
paths: {
|
||||||
|
"/": {
|
||||||
|
"get": {
|
||||||
|
description: "my operation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = producesOptionsFor(state, ["/", "get"])
|
||||||
|
|
||||||
|
expect(result.toJS()).toEqual([
|
||||||
|
"global/one",
|
||||||
|
"global/two",
|
||||||
|
])
|
||||||
|
})
|
||||||
|
it("should favor an operation produces value over a path-item value", function () {
|
||||||
|
const state = fromJS({
|
||||||
|
json: {
|
||||||
|
paths: {
|
||||||
|
"/": {
|
||||||
|
produces: [
|
||||||
|
"path-item/one",
|
||||||
|
"path-item/two",
|
||||||
|
],
|
||||||
|
"get": {
|
||||||
|
description: "my operation",
|
||||||
|
produces: [
|
||||||
|
"operation/one",
|
||||||
|
"operation/two",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = producesOptionsFor(state, ["/", "get"])
|
||||||
|
|
||||||
|
expect(result.toJS()).toEqual([
|
||||||
|
"operation/one",
|
||||||
|
"operation/two",
|
||||||
|
])
|
||||||
|
})
|
||||||
|
it("should favor a path-item produces value over a global value", function () {
|
||||||
|
const state = fromJS({
|
||||||
|
json: {
|
||||||
|
produces: [
|
||||||
|
"global/one",
|
||||||
|
"global/two",
|
||||||
|
],
|
||||||
|
paths: {
|
||||||
|
"/": {
|
||||||
|
produces: [
|
||||||
|
"path-item/one",
|
||||||
|
"path-item/two",
|
||||||
|
],
|
||||||
|
"get": {
|
||||||
|
description: "my operation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = producesOptionsFor(state, ["/", "get"])
|
||||||
|
|
||||||
|
expect(result.toJS()).toEqual([
|
||||||
|
"path-item/one",
|
||||||
|
"path-item/two",
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe("consumesOptionsFor", function() {
|
||||||
|
it("should return an operation consumes value", function () {
|
||||||
|
const state = fromJS({
|
||||||
|
json: {
|
||||||
|
paths: {
|
||||||
|
"/": {
|
||||||
|
"get": {
|
||||||
|
description: "my operation",
|
||||||
|
consumes: [
|
||||||
|
"operation/one",
|
||||||
|
"operation/two",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = consumesOptionsFor(state, ["/", "get"])
|
||||||
|
|
||||||
|
expect(result.toJS()).toEqual([
|
||||||
|
"operation/one",
|
||||||
|
"operation/two",
|
||||||
|
])
|
||||||
|
})
|
||||||
|
it("should return a path item consumes value", function () {
|
||||||
|
const state = fromJS({
|
||||||
|
json: {
|
||||||
|
paths: {
|
||||||
|
"/": {
|
||||||
|
"get": {
|
||||||
|
description: "my operation",
|
||||||
|
consumes: [
|
||||||
|
"path-item/one",
|
||||||
|
"path-item/two",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = consumesOptionsFor(state, ["/", "get"])
|
||||||
|
|
||||||
|
expect(result.toJS()).toEqual([
|
||||||
|
"path-item/one",
|
||||||
|
"path-item/two",
|
||||||
|
])
|
||||||
|
})
|
||||||
|
it("should return a global consumes value", function () {
|
||||||
|
const state = fromJS({
|
||||||
|
json: {
|
||||||
|
consumes: [
|
||||||
|
"global/one",
|
||||||
|
"global/two",
|
||||||
|
],
|
||||||
|
paths: {
|
||||||
|
"/": {
|
||||||
|
"get": {
|
||||||
|
description: "my operation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = consumesOptionsFor(state, ["/", "get"])
|
||||||
|
|
||||||
|
expect(result.toJS()).toEqual([
|
||||||
|
"global/one",
|
||||||
|
"global/two",
|
||||||
|
])
|
||||||
|
})
|
||||||
|
it("should favor an operation consumes value over a path-item value", function () {
|
||||||
|
const state = fromJS({
|
||||||
|
json: {
|
||||||
|
paths: {
|
||||||
|
"/": {
|
||||||
|
consumes: [
|
||||||
|
"path-item/one",
|
||||||
|
"path-item/two",
|
||||||
|
],
|
||||||
|
"get": {
|
||||||
|
description: "my operation",
|
||||||
|
consumes: [
|
||||||
|
"operation/one",
|
||||||
|
"operation/two",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = consumesOptionsFor(state, ["/", "get"])
|
||||||
|
|
||||||
|
expect(result.toJS()).toEqual([
|
||||||
|
"operation/one",
|
||||||
|
"operation/two",
|
||||||
|
])
|
||||||
|
})
|
||||||
|
it("should favor a path-item consumes value over a global value", function () {
|
||||||
|
const state = fromJS({
|
||||||
|
json: {
|
||||||
|
consumes: [
|
||||||
|
"global/one",
|
||||||
|
"global/two",
|
||||||
|
],
|
||||||
|
paths: {
|
||||||
|
"/": {
|
||||||
|
consumes: [
|
||||||
|
"path-item/one",
|
||||||
|
"path-item/two",
|
||||||
|
],
|
||||||
|
"get": {
|
||||||
|
description: "my operation"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const result = consumesOptionsFor(state, ["/", "get"])
|
||||||
|
|
||||||
|
expect(result.toJS()).toEqual([
|
||||||
|
"path-item/one",
|
||||||
|
"path-item/two",
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
17
test/e2e-cypress/static/documents/bugs/5043/status.yaml
Normal file
17
test/e2e-cypress/static/documents/bugs/5043/status.yaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
paths:
|
||||||
|
findByStatus:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- "pet"
|
||||||
|
summary: "Finds Pets by status"
|
||||||
|
description: "Multiple status values can be provided with comma separated strings"
|
||||||
|
operationId: "findPetsByStatus"
|
||||||
|
parameters:
|
||||||
|
- name: "status"
|
||||||
|
in: "body"
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: ok
|
||||||
44
test/e2e-cypress/static/documents/bugs/5043/swagger.yaml
Normal file
44
test/e2e-cypress/static/documents/bugs/5043/swagger.yaml
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
swagger: "2.0"
|
||||||
|
info:
|
||||||
|
description: "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters."
|
||||||
|
version: "1.0.0"
|
||||||
|
title: "Swagger Petstore"
|
||||||
|
termsOfService: "http://swagger.io/terms/"
|
||||||
|
contact:
|
||||||
|
email: "apiteam@swagger.io"
|
||||||
|
license:
|
||||||
|
name: "Apache 2.0"
|
||||||
|
url: "http://www.apache.org/licenses/LICENSE-2.0.html"
|
||||||
|
host: "petstore.swagger.io"
|
||||||
|
basePath: "/v2"
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
- application/xml
|
||||||
|
- text/csv
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
- application/xml
|
||||||
|
- text/csv
|
||||||
|
schemes:
|
||||||
|
- "https"
|
||||||
|
- "http"
|
||||||
|
paths:
|
||||||
|
/pet:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- "pet"
|
||||||
|
summary: "Add a new pet to the store"
|
||||||
|
description: ""
|
||||||
|
operationId: "addPet"
|
||||||
|
parameters:
|
||||||
|
- in: "body"
|
||||||
|
name: "body"
|
||||||
|
description: "Pet object that needs to be added to the store"
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/Pet"
|
||||||
|
responses:
|
||||||
|
405:
|
||||||
|
description: "Invalid input"
|
||||||
|
/pet/findByStatus:
|
||||||
|
$ref: "status.yaml#/paths/findByStatus"
|
||||||
32
test/e2e-cypress/tests/bugs/5043.js
Normal file
32
test/e2e-cypress/tests/bugs/5043.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import repeat from "lodash/repeat"
|
||||||
|
|
||||||
|
describe("#5043: path-level $ref path items should inherit global consumes/produces", () => {
|
||||||
|
it("should render consumes options correctly", () => {
|
||||||
|
cy
|
||||||
|
.visit("/?url=/documents/bugs/5043/swagger.yaml")
|
||||||
|
.get("#operations-pet-findPetsByStatus")
|
||||||
|
.click()
|
||||||
|
.get(".try-out__btn")
|
||||||
|
.click()
|
||||||
|
.get(".content-type")
|
||||||
|
.contains("application/json")
|
||||||
|
.get(".content-type")
|
||||||
|
.contains("application/xml")
|
||||||
|
.get(".content-type")
|
||||||
|
.contains("text/csv")
|
||||||
|
})
|
||||||
|
it("should render produces options correctly", () => {
|
||||||
|
cy
|
||||||
|
.visit("/?url=/documents/bugs/5043/swagger.yaml")
|
||||||
|
.get("#operations-pet-findPetsByStatus")
|
||||||
|
.click()
|
||||||
|
.get(".try-out__btn")
|
||||||
|
.click()
|
||||||
|
.get(".body-param-content-type select")
|
||||||
|
.contains("application/json")
|
||||||
|
.get(".body-param-content-type select")
|
||||||
|
.contains("application/xml")
|
||||||
|
.get(".body-param-content-type select")
|
||||||
|
.contains("text/csv")
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user