Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
261 changes: 51 additions & 210 deletions src/execute.js → src/execute/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,19 @@ import isPlainObject from 'lodash/isPlainObject'
import isArray from 'lodash/isArray'
import btoa from 'btoa'
import url from 'url'
import http, {mergeInQueryOrForm} from './http'
import {getOperationRaw, idFromPathMethod, legacyIdFromPathMethod, isOAS3} from './helpers'
import createError from './specmap/lib/create-error'
import http, {mergeInQueryOrForm} from '../http'
import createError from '../specmap/lib/create-error'

import SWAGGER2_PARAMETER_BUILDERS from './swagger2/parameter-builders'
import OAS3_PARAMETER_BUILDERS from './oas3/parameter-builders'
import oas3BuildRequest from './oas3/build-request'
import swagger2BuildRequest from './swagger2/build-request'
import {
getOperationRaw,
idFromPathMethod,
legacyIdFromPathMethod,
isOAS3
} from '../helpers'

const arrayOrEmpty = (ar) => {
return Array.isArray(ar) ? ar : []
Expand All @@ -26,18 +36,6 @@ export const self = {
buildRequest
}

// These functions will update the request.
// They'll be given {req, value, paramter, spec, operation}.


export const PARAMETER_BUILDERS = {
body: bodyBuilder,
header: headerBuilder,
query: queryBuilder,
path: pathBuilder,
formData: formDataBuilder
}

// Execute request, with the given operationId and parameters
// pathName/method or operationId is optional
export function execute({
Expand Down Expand Up @@ -69,15 +67,39 @@ export function execute({
}

// Build a request, which can be handled by the `http.js` implementation.
export function buildRequest({
spec, operationId, parameters, securities, requestContentType,
responseContentType, parameterBuilders, scheme,
requestInterceptor, responseInterceptor, contextUrl, userFetch,
requestBody, server, serverVariables
}) {
export function buildRequest(options) {
const {
spec,
operationId,
securities,
requestContentType,
responseContentType,
scheme,
requestInterceptor,
responseInterceptor,
contextUrl,
userFetch,
requestBody,
server,
serverVariables
} = options

let {
parameters,
parameterBuilders
} = options

const specIsOAS3 = isOAS3(spec)

parameterBuilders = parameterBuilders || PARAMETER_BUILDERS
if (!parameterBuilders) {
// user did not provide custom parameter builders
if (specIsOAS3) {
parameterBuilders = OAS3_PARAMETER_BUILDERS
}
else {
parameterBuilders = SWAGGER2_PARAMETER_BUILDERS
}
}

// Base Template
let req = {
Expand Down Expand Up @@ -165,78 +187,15 @@ export function buildRequest({
}
})

const requestBodyDef = operation.requestBody || {}
const requestBodyMediaTypes = Object.keys(requestBodyDef.content || {})

// for OAS3: set the Content-Type
if (specIsOAS3 && requestBody) {
// does the passed requestContentType appear in the requestBody definition?
const isExplicitContentTypeValid = requestContentType
&& requestBodyMediaTypes.indexOf(requestContentType) > -1

if (requestContentType && isExplicitContentTypeValid) {
req.headers['Content-Type'] = requestContentType
}
else if (!requestContentType) {
const firstMediaType = requestBodyMediaTypes[0]
if (firstMediaType) {
req.headers['Content-Type'] = firstMediaType
requestContentType = firstMediaType
}
}
}
// Do version-specific tasks, then return those results.
const versionSpecificOptions = {...options, operation}

// for OAS3: add requestBody to request
if (specIsOAS3 && requestBody) {
if (requestContentType) {
if (requestBodyMediaTypes.indexOf(requestContentType) > -1) {
// only attach body if the requestBody has a definition for the
// contentType that has been explicitly set
if (requestContentType === 'application/x-www-form-urlencoded') {
if (typeof requestBody === 'object') {
req.form = {}
Object.keys(requestBody).forEach((k) => {
const val = requestBody[k]
req.form[k] = {
value: val
}
})
}
else {
req.form = requestBody
}
}
else {
req.body = requestBody
}
}
}
else {
req.body = requestBody
}
if (specIsOAS3) {
req = oas3BuildRequest(versionSpecificOptions, req)
}

// Add securities, which are applicable
// REVIEW: OAS3: what changed in securities?
req = applySecurities({request: req, securities, operation, spec})

if (!specIsOAS3 && (req.body || req.form)) {
// all following conditionals are Swagger2 only
if (requestContentType) {
req.headers['Content-Type'] = requestContentType
}
else if (Array.isArray(operation.consumes)) {
req.headers['Content-Type'] = operation.consumes[0]
}
else if (Array.isArray(spec.consumes)) {
req.headers['Content-Type'] = spec.consumes[0]
}
else if (operation.parameters && operation.parameters.filter(p => p.type === 'file').length) {
req.headers['Content-Type'] = 'multipart/form-data'
}
else if (operation.parameters && operation.parameters.filter(p => p.in === 'formData').length) {
req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
}
else {
// If not OAS3, then treat as Swagger2.
req = swagger2BuildRequest(versionSpecificOptions, req)
}

// Will add the query object into the URL, if it exists
Expand All @@ -246,68 +205,6 @@ export function buildRequest({
return req
}

// Add the body to the request
export function bodyBuilder({req, value, specIsOAS3}) {
if (specIsOAS3) {
return
}
req.body = value
}

// Add a form data object.
export function formDataBuilder({req, value, parameter}) {
// REVIEW: OAS3: check for any parameter changes that affect the builder
req.form = req.form || {}
if (value || parameter.allowEmptyValue) {
req.form[parameter.name] = {
value,
allowEmptyValue: parameter.allowEmptyValue,
collectionFormat: parameter.collectionFormat,
}
}
}

// Add a header to the request
export function headerBuilder({req, parameter, value}) {
// REVIEW: OAS3: check for any parameter changes that affect the builder
req.headers = req.headers || {}
if (typeof value !== 'undefined') {
req.headers[parameter.name] = value
}
}

// Replace path paramters, with values ( ie: the URL )
export function pathBuilder({req, value, parameter}) {
// REVIEW: OAS3: check for any parameter changes that affect the builder
req.url = req.url.replace(`{${parameter.name}}`, encodeURIComponent(value))
}

// Add a query to the `query` object, which will later be stringified into the URL's search
export function queryBuilder({req, value, parameter}) {
// REVIEW: OAS3: check for any parameter changes that affect the builder
req.query = req.query || {}

if (value === false && parameter.type === 'boolean') {
value = 'false'
}

if (value === 0 && ['number', 'integer'].indexOf(parameter.type) > -1) {
value = '0'
}

if (value) {
req.query[parameter.name] = {
collectionFormat: parameter.collectionFormat,
value
}
}
else if (parameter.allowEmptyValue) {
const paramName = parameter.name
req.query[paramName] = req.query[paramName] || {}
req.query[paramName].allowEmptyValue = true
}
}

const stripNonAlpha = str => (str ? str.replace(/\W/g, '') : null)

export function baseUrl(obj) {
Expand Down Expand Up @@ -389,59 +286,3 @@ function swagger2BaseUrl({spec, scheme, contextUrl = ''}) {

return ''
}


// Add security values, to operations - that declare their need on them
export function applySecurities({request, securities = {}, operation = {}, spec}) {
const result = assign({}, request)
const {authorized = {}, specSecurity = []} = securities
const security = operation.security || specSecurity
const isAuthorized = authorized && !!Object.keys(authorized).length
const securityDef = spec.securityDefinitions

result.headers = result.headers || {}
result.query = result.query || {}

if (!Object.keys(securities).length || !isAuthorized || !security ||
(Array.isArray(operation.security) && !operation.security.length)) {
return request
}

security.forEach((securityObj, index) => {
for (const key in securityObj) {
const auth = authorized[key]
if (!auth) {
continue
}

const token = auth.token
const value = auth.value || auth
const schema = securityDef[key]
const {type} = schema
const accessToken = token && token.access_token
const tokenType = token && token.token_type

if (auth) {
if (type === 'apiKey') {
const inType = schema.in === 'query' ? 'query' : 'headers'
result[inType] = result[inType] || {}
result[inType][schema.name] = value
}
else if (type === 'basic') {
if (value.header) {
result.headers.authorization = value.header
}
else {
value.base64 = btoa(`${value.username}:${value.password}`)
result.headers.authorization = `Basic ${value.base64}`
}
}
else if (type === 'oauth2' && accessToken) {
result.headers.authorization = `${tokenType || 'Bearer'} ${accessToken}`
}
}
}
})

return result
}
66 changes: 66 additions & 0 deletions src/execute/oas3/build-request.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// This function runs after the common function,
// `src/execute/index.js#buildRequest`

export default function (options, req) {
const {
operation,
requestBody
} = options

let {
requestContentType
} = options

const requestBodyDef = operation.requestBody || {}
const requestBodyMediaTypes = Object.keys(requestBodyDef.content || {})

// for OAS3: set the Content-Type
if (requestBody) {
// does the passed requestContentType appear in the requestBody definition?
const isExplicitContentTypeValid = requestContentType
&& requestBodyMediaTypes.indexOf(requestContentType) > -1

if (requestContentType && isExplicitContentTypeValid) {
req.headers['Content-Type'] = requestContentType
}
else if (!requestContentType) {
const firstMediaType = requestBodyMediaTypes[0]
if (firstMediaType) {
req.headers['Content-Type'] = firstMediaType
requestContentType = firstMediaType
}
}
}

// for OAS3: add requestBody to request
if (requestBody) {
if (requestContentType) {
if (requestBodyMediaTypes.indexOf(requestContentType) > -1) {
// only attach body if the requestBody has a definition for the
// contentType that has been explicitly set
if (requestContentType === 'application/x-www-form-urlencoded') {
if (typeof requestBody === 'object') {
req.form = {}
Object.keys(requestBody).forEach((k) => {
const val = requestBody[k]
req.form[k] = {
value: val
}
})
}
else {
req.form = requestBody
}
}
else {
req.body = requestBody
}
}
}
else {
req.body = requestBody
}
}

return req
}
Loading