Files
swagger-ui/test/core/plugins/spec/selectors.js
kyle b716ed2515 fix: gracefully handle malformed global tags array in taggedOperations selector (via #5159)
* fix: handle malformed global tags array in taggedOperations

* handle non-array global tags as well

* update test imports

* remove stray brackets
2019-02-05 20:10:18 -06:00

1215 lines
28 KiB
JavaScript

/* eslint-env mocha */
import expect from "expect"
import { fromJS } from "immutable"
import { fromJSOrdered } from "core/utils"
import {
definitions,
parameterValues,
contentTypeValues,
operationScheme,
specJsonWithResolvedSubtrees,
producesOptionsFor,
operationWithMeta,
parameterWithMeta,
parameterWithMetaByIdentity,
parameterInclusionSettingFor,
consumesOptionsFor,
taggedOperations
} from "corePlugins/spec/selectors"
import Petstore from "./assets/petstore.json"
describe("definitions", function(){
it("should return definitions by default", function(){
// Given
const spec = fromJS({
json: {
swagger: "2.0",
definitions: {
a: {
type: "string"
},
b: {
type: "string"
}
}
}
})
// When
let res = definitions(spec)
// Then
expect(res.toJS()).toEqual({
a: {
type: "string"
},
b: {
type: "string"
}
})
})
it("should return an empty Map when missing definitions", function(){
// Given
const spec = fromJS({
json: {
swagger: "2.0"
}
})
// When
let res = definitions(spec)
// Then
expect(res.toJS()).toEqual({})
})
it("should return an empty Map when given non-object definitions", function(){
// Given
const spec = fromJS({
json: {
swagger: "2.0",
definitions: "..."
}
})
// When
let res = definitions(spec)
// Then
expect(res.toJS()).toEqual({})
})
})
describe("parameterValue", function(){
it("should return Map({}) if no path found", function(){
// Given
const spec = fromJS({ })
// When
let paramValues = parameterValues(spec, ["/one", "get"])
// Then
expect(paramValues.toJS()).toEqual({})
})
it("should return a hash of [parameterName]: value", function(){
// Given
const spec = fromJS({
json: {
paths: {
"/one": {
get: {
parameters: [
{ name: "one", in: "query", value: 1},
{ name: "two", in: "query", value: "duos"}
]
}
}
}
}
})
// When
let paramValues = parameterValues(spec, ["/one", "get"])
// Then
expect(paramValues.toJS()).toEqual({
"query.one": 1,
"query.two": "duos"
})
})
})
describe("contentTypeValues", function(){
it("should return { requestContentType, responseContentType } from an operation", function(){
// Given
let state = fromJS({
json: {
paths: {
"/one": {
get: {}
}
}
},
meta: {
paths: {
"/one": {
get: {
"consumes_value": "one",
"produces_value": "two"
}
}
}
}
})
// When
let contentTypes = contentTypeValues(state, [ "/one", "get" ])
// Then
expect(contentTypes.toJS()).toEqual({
requestContentType: "one",
responseContentType: "two"
})
})
it("should default to the first `produces` array value if current is not set", function(){
// Given
let state = fromJS({
json: {
paths: {
"/one": {
get: {
produces: [
"application/xml",
"application/whatever"
]
}
}
}
},
meta: {
paths: {
"/one": {
get: {
"consumes_value": "one"
}
}
}
}
})
// When
let contentTypes = contentTypeValues(state, [ "/one", "get" ])
// Then
expect(contentTypes.toJS()).toEqual({
requestContentType: "one",
responseContentType: "application/xml"
})
})
it("should default to `application/json` if a default produces value is not available", function(){
// Given
let state = fromJS({
json: {
paths: {
"/one": {
get: {}
}
}
},
meta: {
paths: {
"/one": {
get: {
"consumes_value": "one"
}
}
}
}
})
// When
let contentTypes = contentTypeValues(state, [ "/one", "get" ])
// Then
expect(contentTypes.toJS()).toEqual({
requestContentType: "one",
responseContentType: "application/json"
})
})
it("should prioritize consumes value first from an operation", function(){
// Given
let state = fromJS({
json: {
paths: {
"/one": {
get: {
"parameters": [{
"type": "file"
}],
}
}
}
},
meta: {
paths: {
"/one": {
get: {
"consumes_value": "one",
}
}
}
}
})
// When
let contentTypes = contentTypeValues(state, [ "/one", "get" ])
// Then
expect(contentTypes.toJS().requestContentType).toEqual("one")
})
it("should fallback to multipart/form-data if there is no consumes value but there is a file parameter", function(){
// Given
let state = fromJS({
json: {
paths: {
"/one": {
get: {
"parameters": [{
"type": "file"
}],
}
}
}
}
})
// When
let contentTypes = contentTypeValues(state, [ "/one", "get" ])
// Then
expect(contentTypes.toJS().requestContentType).toEqual("multipart/form-data")
})
it("should fallback to application/x-www-form-urlencoded if there is no consumes value, no file parameter, but there is a formData parameter", function(){
// Given
let state = fromJS({
json: {
paths: {
"/one": {
get: {
"parameters": [{
"type": "formData"
}],
}
}
}
}
})
// When
let contentTypes = contentTypeValues(state, [ "/one", "get" ])
// Then
expect(contentTypes.toJS().requestContentType).toEqual("application/x-www-form-urlencoded")
})
it("should return nothing, if the operation does not exist", function(){
// Given
let state = fromJS({ })
// When
let contentTypes = contentTypeValues(state, [ "/one", "get" ])
// Then
expect(contentTypes.toJS()).toEqual({
requestContentType: undefined,
responseContentType: undefined
})
})
})
describe("operationScheme", function(){
it("should return the correct scheme for a remote spec that doesn't specify a scheme", function(){
// Given
let state = fromJS({
url: "https://generator.swagger.io/api/swagger.json",
json: {
paths: {
"/one": {
get: {
"consumes_value": "one",
"produces_value": "two"
}
}
}
}
})
// When
let scheme = operationScheme(state, ["/one"], "get")
// Then
expect(scheme).toEqual("https")
})
// it("should be ok, if no operation found", function(){
// // Given
// let state = fromJS({ })
//
// // When
// let contentTypes = contentTypeValues(state, [ "/one", "get" ])
// // Then
// expect(contentTypes.toJS()).toEqual({
// requestContentType: undefined,
// responseContentType: undefined
// })
// })
})
describe("specJsonWithResolvedSubtrees", function(){
it("should return a correctly merged tree", function(){
// Given
let state = fromJS({
json: {
definitions: {
Asdf: {
$ref: "#/some/path",
randomKey: "this should be removed b/c siblings of $refs must be removed, per the specification",
description: "same for this key"
},
Fgsfds: {
$ref: "#/another/path"
},
OtherDef: {
description: "has no refs"
}
}
},
resolvedSubtrees: {
definitions: {
Asdf: {
type: "object",
$$ref: "#/some/path"
}
}
}
})
// When
let result = specJsonWithResolvedSubtrees(state)
// Then
expect(result.toJS()).toEqual({
definitions: {
Asdf: {
type: "object",
$$ref: "#/some/path"
},
Fgsfds: {
$ref: "#/another/path"
},
OtherDef: {
description: "has no refs"
}
}
})
})
it("should preserve initial map key ordering", function(){
// Given
let state = fromJSOrdered({
json: Petstore,
resolvedSubtrees: {
paths: {
"/pet/{petId}": {
post: {
tags: [
"pet"
],
summary: "Updates a pet in the store with form data",
description: "",
operationId: "updatePetWithForm",
consumes: [
"application/x-www-form-urlencoded"
],
produces: [
"application/xml",
"application/json"
],
parameters: [
{
name: "petId",
"in": "path",
description: "ID of pet that needs to be updated",
required: true,
type: "integer",
format: "int64"
},
{
name: "name",
"in": "formData",
description: "Updated name of the pet",
required: false,
type: "string"
},
{
name: "status",
"in": "formData",
description: "Updated status of the pet",
required: false,
type: "string"
}
],
responses: {
"405": {
description: "Invalid input"
}
},
security: [
{
petstore_auth: [
"write:pets",
"read:pets"
]
}
],
__originalOperationId: "updatePetWithForm"
}
}
}
}
})
// When
let result = specJsonWithResolvedSubtrees(state)
// Then
const correctOrder = [
"/pet",
"/pet/findByStatus",
"/pet/findByTags",
"/pet/{petId}",
"/pet/{petId}/uploadImage",
"/store/inventory",
"/store/order",
"/store/order/{orderId}",
"/user",
"/user/createWithArray",
"/user/createWithList",
"/user/login",
"/user/logout",
"/user/{username}"
]
expect(state.getIn(["json", "paths"]).keySeq().toJS()).toEqual(correctOrder)
expect(result.getIn(["paths"]).keySeq().toJS()).toEqual(correctOrder)
})
})
describe("operationWithMeta", function() {
it("should support merging in {in}.{name} keyed param metadata", function () {
const state = fromJS({
json: {
paths: {
"/": {
"get": {
parameters: [
{
name: "myBody",
in: "body"
}
]
}
}
}
},
meta: {
paths: {
"/": {
"get": {
parameters: {
"body.myBody": {
value: "abc123"
}
}
}
}
}
}
})
const result = operationWithMeta(state, "/", "get")
expect(result.toJS()).toEqual({
parameters: [
{
name: "myBody",
in: "body",
value: "abc123"
}
]
})
})
it("should support merging in hash-keyed param metadata", function () {
const bodyParam = fromJS({
name: "myBody",
in: "body"
})
const state = fromJS({
json: {
paths: {
"/": {
"get": {
parameters: [
bodyParam
]
}
}
}
},
meta: {
paths: {
"/": {
"get": {
parameters: {
[`body.myBody.hash-${bodyParam.hashCode()}`]: {
value: "abc123"
}
}
}
}
}
}
})
const result = operationWithMeta(state, "/", "get")
expect(result.toJS()).toEqual({
parameters: [
{
name: "myBody",
in: "body",
value: "abc123"
}
]
})
})
})
describe("parameterWithMeta", function() {
it("should support merging in {in}.{name} keyed param metadata", function () {
const state = fromJS({
json: {
paths: {
"/": {
"get": {
parameters: [
{
name: "myBody",
in: "body"
}
]
}
}
}
},
meta: {
paths: {
"/": {
"get": {
parameters: {
"body.myBody": {
value: "abc123"
}
}
}
}
}
}
})
const result = parameterWithMeta(state, ["/", "get"], "myBody", "body")
expect(result.toJS()).toEqual({
name: "myBody",
in: "body",
value: "abc123"
})
})
it("should give best-effort when encountering hash-keyed param metadata", function () {
const bodyParam = fromJS({
name: "myBody",
in: "body"
})
const state = fromJS({
json: {
paths: {
"/": {
"get": {
parameters: [
bodyParam
]
}
}
}
},
meta: {
paths: {
"/": {
"get": {
parameters: {
[`body.myBody.hash-${bodyParam.hashCode()}`]: {
value: "abc123"
}
}
}
}
}
}
})
const result = parameterWithMeta(state, ["/", "get"], "myBody", "body")
expect(result.toJS()).toEqual({
name: "myBody",
in: "body",
value: "abc123"
})
})
})
describe("parameterWithMetaByIdentity", function() {
it("should support merging in {in}.{name} keyed param metadata", function () {
const bodyParam = fromJS({
name: "myBody",
in: "body"
})
const state = fromJS({
json: {
paths: {
"/": {
"get": {
parameters: [bodyParam]
}
}
}
},
meta: {
paths: {
"/": {
"get": {
parameters: {
"body.myBody": {
value: "abc123"
}
}
}
}
}
}
})
const result = parameterWithMetaByIdentity(state, ["/", "get"], bodyParam)
expect(result.toJS()).toEqual({
name: "myBody",
in: "body",
value: "abc123"
})
})
it("should support merging in hash-keyed param metadata", function () {
const bodyParam = fromJS({
name: "myBody",
in: "body"
})
const state = fromJS({
json: {
paths: {
"/": {
"get": {
parameters: [
bodyParam
]
}
}
}
},
meta: {
paths: {
"/": {
"get": {
parameters: {
[`body.myBody.hash-${bodyParam.hashCode()}`]: {
value: "abc123"
}
}
}
}
}
}
})
const result = parameterWithMetaByIdentity(state, ["/", "get"], bodyParam)
expect(result.toJS()).toEqual({
name: "myBody",
in: "body",
value: "abc123"
})
})
})
describe("parameterInclusionSettingFor", function() {
it("should support getting {in}.{name} param inclusion settings", function () {
const param = fromJS({
name: "param",
in: "query",
allowEmptyValue: true
})
const state = fromJS({
json: {
paths: {
"/": {
"get": {
parameters: [
param
]
}
}
}
},
meta: {
paths: {
"/": {
"get": {
"parameter_inclusions": {
[`query.param`]: true
}
}
}
}
}
})
const result = parameterInclusionSettingFor(state, ["/", "get"], "param", "query")
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",
])
})
})
describe("taggedOperations", function () {
it("should return a List of ad-hoc tagged operations", function () {
const system = {
getConfigs: () => ({})
}
const state = fromJS({
json: {
// tags: [
// "myTag"
// ],
paths: {
"/": {
"get": {
produces: [],
tags: ["myTag"],
description: "my operation",
consumes: [
"operation/one",
"operation/two",
]
}
}
}
}
})
const result = taggedOperations(state)(system)
const op = state.getIn(["json", "paths", "/", "get"]).toJS()
expect(result.toJS()).toEqual({
myTag: {
tagDetails: undefined,
operations: [{
id: "get-/",
method: "get",
path: "/",
operation: op
}]
}
})
})
it("should return a List of defined tagged operations", function () {
const system = {
getConfigs: () => ({})
}
const state = fromJS({
json: {
tags: [
{
name: "myTag"
}
],
paths: {
"/": {
"get": {
produces: [],
tags: ["myTag"],
description: "my operation",
consumes: [
"operation/one",
"operation/two",
]
}
}
}
}
})
const result = taggedOperations(state)(system)
const op = state.getIn(["json", "paths", "/", "get"]).toJS()
expect(result.toJS()).toEqual({
myTag: {
tagDetails: {
name: "myTag"
},
operations: [{
id: "get-/",
method: "get",
path: "/",
operation: op
}]
}
})
})
it("should gracefully handle a malformed global tags array", function () {
const system = {
getConfigs: () => ({})
}
const state = fromJS({
json: {
tags: [null],
paths: {
"/": {
"get": {
produces: [],
tags: ["myTag"],
description: "my operation",
consumes: [
"operation/one",
"operation/two",
]
}
}
}
}
})
const result = taggedOperations(state)(system)
const op = state.getIn(["json", "paths", "/", "get"]).toJS()
expect(result.toJS()).toEqual({
myTag: {
tagDetails: undefined,
operations: [{
id: "get-/",
method: "get",
path: "/",
operation: op
}]
}
})
})
it("should gracefully handle a non-array global tags entry", function () {
const system = {
getConfigs: () => ({})
}
const state = fromJS({
json: {
tags: "asdf",
paths: {
"/": {
"get": {
produces: [],
tags: ["myTag"],
description: "my operation",
consumes: [
"operation/one",
"operation/two",
]
}
}
}
}
})
const result = taggedOperations(state)(system)
const op = state.getIn(["json", "paths", "/", "get"]).toJS()
expect(result.toJS()).toEqual({
myTag: {
tagDetails: undefined,
operations: [{
id: "get-/",
method: "get",
path: "/",
operation: op
}]
}
})
})
})