@@ -5,7 +5,7 @@ import isArray from 'lodash/isArray'
55import btoa from 'btoa'
66import url from 'url'
77import http , { mergeInQueryOrForm } from './http'
8- import { getOperationRaw , idFromPathMethod , legacyIdFromPathMethod } from './helpers'
8+ import { getOperationRaw , idFromPathMethod , legacyIdFromPathMethod , isOAS3 } from './helpers'
99import createError from './specmap/lib/create-error'
1010
1111const arrayOrEmpty = ( ar ) => {
@@ -28,6 +28,8 @@ export const self = {
2828
2929// These functions will update the request.
3030// They'll be given {req, value, paramter, spec, operation}.
31+
32+
3133export const PARAMETER_BUILDERS = {
3234 body : bodyBuilder ,
3335 header : headerBuilder ,
@@ -70,13 +72,16 @@ export function execute({
7072export function buildRequest ( {
7173 spec, operationId, parameters, securities, requestContentType,
7274 responseContentType, parameterBuilders, scheme,
73- requestInterceptor, responseInterceptor, contextUrl
75+ requestInterceptor, responseInterceptor, contextUrl, userFetch,
76+ requestBody, server, serverVariables
7477} ) {
78+ const specIsOAS3 = isOAS3 ( spec )
79+
7580 parameterBuilders = parameterBuilders || PARAMETER_BUILDERS
7681
7782 // Base Template
7883 let req = {
79- url : baseUrl ( { spec, scheme, contextUrl} ) ,
84+ url : baseUrl ( { spec, scheme, contextUrl, server , serverVariables } ) ,
8085 credentials : 'same-origin' ,
8186 headers : {
8287 // This breaks CORSs... removing this line... probably breaks oAuth. Need to address that
@@ -91,6 +96,9 @@ export function buildRequest({
9196 if ( responseInterceptor ) {
9297 req . responseInterceptor = responseInterceptor
9398 }
99+ if ( userFetch ) {
100+ req . userFetch = userFetch
101+ }
94102
95103 // Mostly for testing
96104 if ( ! operationId ) {
@@ -118,6 +126,10 @@ export function buildRequest({
118126 . concat ( arrayOrEmpty ( operation . parameters ) ) // operation parameters
119127 . concat ( arrayOrEmpty ( path . parameters ) ) // path parameters
120128
129+ // REVIEW: OAS3: have any key names or parameter shapes changed?
130+ // Any new features that need to be plugged in here?
131+
132+
121133 // Add values to request
122134 combinedParameters . forEach ( ( parameter ) => {
123135 const builder = parameterBuilders [ parameter . in ]
@@ -152,24 +164,77 @@ export function buildRequest({
152164 }
153165 } )
154166
167+ const requestBodyDef = operation . requestBody || { }
168+ const requestBodyMediaTypes = Object . keys ( requestBodyDef . content || { } )
169+
170+ // for OAS3: set the Content-Type
171+ if ( specIsOAS3 && requestBody ) {
172+ // does the passed requestContentType appear in the requestBody definition?
173+ const isExplicitContentTypeValid = requestContentType
174+ && requestBodyMediaTypes . indexOf ( requestContentType ) > - 1
175+
176+ if ( requestContentType && isExplicitContentTypeValid ) {
177+ req . headers [ 'Content-Type' ] = requestContentType
178+ }
179+ else if ( ! requestContentType ) {
180+ const firstMediaType = requestBodyMediaTypes [ 0 ]
181+ if ( firstMediaType ) {
182+ req . headers [ 'Content-Type' ] = firstMediaType
183+ requestContentType = firstMediaType
184+ }
185+ }
186+ }
187+
188+ // for OAS3: add requestBody to request
189+ if ( specIsOAS3 && requestBody ) {
190+ if ( requestContentType ) {
191+ if ( requestBodyMediaTypes . indexOf ( requestContentType ) > - 1 ) {
192+ // only attach body if the requestBody has a definition for the
193+ // contentType that has been explicitly set
194+ if ( requestContentType === 'application/x-www-form-urlencoded' ) {
195+ if ( typeof requestBody === 'object' ) {
196+ req . form = { }
197+ Object . keys ( requestBody ) . forEach ( ( k ) => {
198+ const val = requestBody [ k ]
199+ req . form [ k ] = {
200+ value : val
201+ }
202+ } )
203+ }
204+ else {
205+ req . form = requestBody
206+ }
207+ }
208+ else {
209+ req . body = requestBody
210+ }
211+ }
212+ }
213+ else {
214+ req . body = requestBody
215+ }
216+ }
217+
155218 // Add securities, which are applicable
219+ // REVIEW: OAS3: what changed in securities?
156220 req = applySecurities ( { request : req , securities, operation, spec} )
157221
158- if ( req . body || req . form ) {
222+ if ( ! specIsOAS3 && ( req . body || req . form ) ) {
223+ // all following conditionals are Swagger2 only
159224 if ( requestContentType ) {
160- req . headers [ 'content-type ' ] = requestContentType
225+ req . headers [ 'Content-Type ' ] = requestContentType
161226 }
162227 else if ( Array . isArray ( operation . consumes ) ) {
163- req . headers [ 'content-type ' ] = operation . consumes [ 0 ]
228+ req . headers [ 'Content-Type ' ] = operation . consumes [ 0 ]
164229 }
165230 else if ( Array . isArray ( spec . consumes ) ) {
166- req . headers [ 'content-type ' ] = spec . consumes [ 0 ]
231+ req . headers [ 'Content-Type ' ] = spec . consumes [ 0 ]
167232 }
168- else if ( operation . parameters . filter ( p => p . type === 'file' ) . length ) {
169- req . headers [ 'content-type ' ] = 'multipart/form-data'
233+ else if ( operation . parameters && operation . parameters . filter ( p => p . type === 'file' ) . length ) {
234+ req . headers [ 'Content-Type ' ] = 'multipart/form-data'
170235 }
171- else if ( operation . parameters . filter ( p => p . in === 'formData' ) . length ) {
172- req . headers [ 'content-type ' ] = 'application/x-www-form-urlencoded'
236+ else if ( operation . parameters && operation . parameters . filter ( p => p . in === 'formData' ) . length ) {
237+ req . headers [ 'Content-Type ' ] = 'application/x-www-form-urlencoded'
173238 }
174239 }
175240
@@ -181,12 +246,16 @@ export function buildRequest({
181246}
182247
183248// Add the body to the request
184- export function bodyBuilder ( { req, value} ) {
249+ export function bodyBuilder ( { req, value, specIsOAS3} ) {
250+ if ( specIsOAS3 ) {
251+ return
252+ }
185253 req . body = value
186254}
187255
188256// Add a form data object.
189257export function formDataBuilder ( { req, value, parameter} ) {
258+ // REVIEW: OAS3: check for any parameter changes that affect the builder
190259 req . form = req . form || { }
191260 if ( value || parameter . allowEmptyValue ) {
192261 req . form [ parameter . name ] = {
@@ -199,6 +268,7 @@ export function formDataBuilder({req, value, parameter}) {
199268
200269// Add a header to the request
201270export function headerBuilder ( { req, parameter, value} ) {
271+ // REVIEW: OAS3: check for any parameter changes that affect the builder
202272 req . headers = req . headers || { }
203273 if ( typeof value !== 'undefined' ) {
204274 req . headers [ parameter . name ] = value
@@ -207,11 +277,13 @@ export function headerBuilder({req, parameter, value}) {
207277
208278// Replace path paramters, with values ( ie: the URL )
209279export function pathBuilder ( { req, value, parameter} ) {
280+ // REVIEW: OAS3: check for any parameter changes that affect the builder
210281 req . url = req . url . replace ( `{${ parameter . name } }` , encodeURIComponent ( value ) )
211282}
212283
213284// Add a query to the `query` object, which will later be stringified into the URL's search
214285export function queryBuilder ( { req, value, parameter} ) {
286+ // REVIEW: OAS3: check for any parameter changes that affect the builder
215287 req . query = req . query || { }
216288
217289 if ( value === false && parameter . type === 'boolean' ) {
@@ -237,8 +309,69 @@ export function queryBuilder({req, value, parameter}) {
237309
238310const stripNonAlpha = str => ( str ? str . replace ( / \W / g, '' ) : null )
239311
312+ export function baseUrl ( obj ) {
313+ const specIsOAS3 = isOAS3 ( obj . spec )
314+
315+ return specIsOAS3 ? oas3BaseUrl ( obj ) : swagger2BaseUrl ( obj )
316+ }
317+
318+ function oas3BaseUrl ( { spec, server, serverVariables = { } } ) {
319+ const servers = spec . servers
320+
321+ let selectedServerUrl = ''
322+ let selectedServerObj = null
323+
324+ if ( ! servers || ! Array . isArray ( servers ) ) {
325+ return ''
326+ }
327+
328+ if ( server ) {
329+ const serverUrls = servers . map ( srv => srv . url )
330+
331+ if ( serverUrls . indexOf ( server ) > - 1 ) {
332+ selectedServerUrl = server
333+ selectedServerObj = servers [ serverUrls . indexOf ( server ) ]
334+ }
335+ }
336+
337+ if ( ! selectedServerUrl ) {
338+ // default to the first server if we don't have one by now
339+ selectedServerUrl = servers [ 0 ] . url
340+ selectedServerObj = servers [ 0 ]
341+ }
342+
343+ if ( selectedServerUrl . indexOf ( '{' ) > - 1 ) {
344+ // do variable substitution
345+ const varNames = getVariableTemplateNames ( selectedServerUrl )
346+ varNames . forEach ( ( vari ) => {
347+ if ( selectedServerObj . variables && selectedServerObj . variables [ vari ] ) {
348+ // variable is defined in server
349+ const variableDefinition = selectedServerObj . variables [ vari ]
350+ const variableValue = serverVariables [ vari ] || variableDefinition . default
351+
352+ const re = new RegExp ( `{${ vari } }` , 'g' )
353+ selectedServerUrl = selectedServerUrl . replace ( re , variableValue )
354+ }
355+ } )
356+ }
357+
358+ return selectedServerUrl
359+ }
360+
361+ function getVariableTemplateNames ( str ) {
362+ const results = [ ]
363+ const re = / { ( [ ^ } ] + ) } / g
364+ let text
365+
366+ // eslint-disable-next-line no-cond-assign
367+ while ( text = re . exec ( str ) ) {
368+ results . push ( text [ 1 ] )
369+ }
370+ return results
371+ }
372+
240373// Compose the baseUrl ( scheme + host + basePath )
241- export function baseUrl ( { spec, scheme, contextUrl = '' } ) {
374+ function swagger2BaseUrl ( { spec, scheme, contextUrl = '' } ) {
242375 const parsedContextUrl = url . parse ( contextUrl )
243376 const firstSchemeInSpec = Array . isArray ( spec . schemes ) ? spec . schemes [ 0 ] : null
244377
@@ -302,7 +435,7 @@ export function applySecurities({request, securities = {}, operation = {}, spec}
302435 result . headers . authorization = `Basic ${ value . base64 } `
303436 }
304437 }
305- else if ( type === 'oauth2' ) {
438+ else if ( type === 'oauth2' && accessToken ) {
306439 result . headers . authorization = `${ tokenType || 'Bearer' } ${ accessToken } `
307440 }
308441 }
0 commit comments