@@ -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 ) => {
@@ -24,6 +24,8 @@ export const self = {
2424
2525// These functions will update the request.
2626// They'll be given {req, value, paramter, spec, operation}.
27+
28+
2729export const PARAMETER_BUILDERS = {
2830 body : bodyBuilder ,
2931 header : headerBuilder ,
@@ -66,13 +68,16 @@ export function execute({
6668export function buildRequest ( {
6769 spec, operationId, parameters, securities, requestContentType,
6870 responseContentType, parameterBuilders, scheme,
69- requestInterceptor, responseInterceptor, contextUrl, userFetch
71+ requestInterceptor, responseInterceptor, contextUrl, userFetch,
72+ requestBody, server, serverVariables
7073} ) {
74+ const specIsOAS3 = isOAS3 ( spec )
75+
7176 parameterBuilders = parameterBuilders || PARAMETER_BUILDERS
7277
7378 // Base Template
7479 let req = {
75- url : baseUrl ( { spec, scheme, contextUrl} ) ,
80+ url : baseUrl ( { spec, scheme, contextUrl, server , serverVariables } ) ,
7681 credentials : 'same-origin' ,
7782 headers : {
7883 // This breaks CORSs... removing this line... probably breaks oAuth. Need to address that
@@ -120,6 +125,9 @@ export function buildRequest({
120125 const builder = parameterBuilders [ parameter . in ]
121126 let value
122127
128+ // REVIEW: OAS3: have any key names or parameter shapes changed?
129+ // Any new features that need to be plugged in here?
130+
123131 if ( parameter . in === 'body' && parameter . schema && parameter . schema . properties ) {
124132 value = parameters
125133 }
@@ -135,28 +143,82 @@ export function buildRequest({
135143 }
136144
137145 if ( builder ) {
138- builder ( { req, parameter, value, operation, spec} )
146+ builder ( { req, parameter, value, operation, spec, specIsOAS3 } )
139147 }
140148 } )
141149
150+ const requestBodyDef = operation . requestBody || { }
151+ const requestBodyMediaTypes = Object . keys ( requestBodyDef . content || { } )
152+
153+ // for OAS3: set the Content-Type
154+ if ( specIsOAS3 && requestBody ) {
155+ // does the passed requestContentType appear in the requestBody definition?
156+ const isExplicitContentTypeValid = requestContentType
157+ && requestBodyMediaTypes . indexOf ( requestContentType ) > - 1
158+
159+ if ( requestContentType && isExplicitContentTypeValid ) {
160+ req . headers [ 'Content-Type' ] = requestContentType
161+ }
162+ else if ( ! requestContentType ) {
163+ const firstMediaType = requestBodyMediaTypes [ 0 ]
164+ if ( firstMediaType ) {
165+ req . headers [ 'Content-Type' ] = firstMediaType
166+ requestContentType = firstMediaType
167+ }
168+ }
169+ }
170+
171+ // for OAS3: add requestBody to request
172+ if ( specIsOAS3 && requestBody ) {
173+ if ( requestContentType ) {
174+ if ( requestBodyMediaTypes . indexOf ( requestContentType ) > - 1 ) {
175+ // only attach body if the requestBody has a definition for the
176+ // contentType that has been explicitly set
177+ if ( requestContentType === 'application/x-www-form-urlencoded' ) {
178+ if ( typeof requestBody === 'object' ) {
179+ req . form = { }
180+ Object . keys ( requestBody ) . forEach ( ( k ) => {
181+ const val = requestBody [ k ]
182+ req . form [ k ] = {
183+ value : val
184+ }
185+ } )
186+ }
187+ else {
188+ req . form = requestBody
189+ }
190+ }
191+ else {
192+ req . body = requestBody
193+ }
194+ }
195+ }
196+ else {
197+ req . body = requestBody
198+ }
199+ }
200+
201+
142202 // Add securities, which are applicable
203+ // REVIEW: OAS3: what changed in securities?
143204 req = applySecurities ( { request : req , securities, operation, spec} )
144205
145- if ( req . body || req . form ) {
206+ if ( ! specIsOAS3 && ( req . body || req . form ) ) {
207+ // all following conditionals are Swagger2 only
146208 if ( requestContentType ) {
147- req . headers [ 'content-type ' ] = requestContentType
209+ req . headers [ 'Content-Type ' ] = requestContentType
148210 }
149211 else if ( Array . isArray ( operation . consumes ) ) {
150- req . headers [ 'content-type ' ] = operation . consumes [ 0 ]
212+ req . headers [ 'Content-Type ' ] = operation . consumes [ 0 ]
151213 }
152214 else if ( Array . isArray ( spec . consumes ) ) {
153- req . headers [ 'content-type ' ] = spec . consumes [ 0 ]
215+ req . headers [ 'Content-Type ' ] = spec . consumes [ 0 ]
154216 }
155- else if ( operation . parameters . filter ( p => p . type === 'file' ) . length ) {
156- req . headers [ 'content-type ' ] = 'multipart/form-data'
217+ else if ( operation . parameters && operation . parameters . filter ( p => p . type === 'file' ) . length ) {
218+ req . headers [ 'Content-Type ' ] = 'multipart/form-data'
157219 }
158- else if ( operation . parameters . filter ( p => p . in === 'formData' ) . length ) {
159- req . headers [ 'content-type ' ] = 'application/x-www-form-urlencoded'
220+ else if ( operation . parameters && operation . parameters . filter ( p => p . in === 'formData' ) . length ) {
221+ req . headers [ 'Content-Type ' ] = 'application/x-www-form-urlencoded'
160222 }
161223 }
162224
@@ -168,12 +230,16 @@ export function buildRequest({
168230}
169231
170232// Add the body to the request
171- export function bodyBuilder ( { req, value} ) {
233+ export function bodyBuilder ( { req, value, specIsOAS3} ) {
234+ if ( specIsOAS3 ) {
235+ return
236+ }
172237 req . body = value
173238}
174239
175240// Add a form data object.
176241export function formDataBuilder ( { req, value, parameter} ) {
242+ // REVIEW: OAS3: check for any parameter changes that affect the builder
177243 req . form = req . form || { }
178244 if ( value || parameter . allowEmptyValue ) {
179245 req . form [ parameter . name ] = {
@@ -186,6 +252,7 @@ export function formDataBuilder({req, value, parameter}) {
186252
187253// Add a header to the request
188254export function headerBuilder ( { req, parameter, value} ) {
255+ // REVIEW: OAS3: check for any parameter changes that affect the builder
189256 req . headers = req . headers || { }
190257 if ( typeof value !== 'undefined' ) {
191258 req . headers [ parameter . name ] = value
@@ -194,11 +261,13 @@ export function headerBuilder({req, parameter, value}) {
194261
195262// Replace path paramters, with values ( ie: the URL )
196263export function pathBuilder ( { req, value, parameter} ) {
264+ // REVIEW: OAS3: check for any parameter changes that affect the builder
197265 req . url = req . url . replace ( `{${ parameter . name } }` , encodeURIComponent ( value ) )
198266}
199267
200268// Add a query to the `query` object, which will later be stringified into the URL's search
201269export function queryBuilder ( { req, value, parameter} ) {
270+ // REVIEW: OAS3: check for any parameter changes that affect the builder
202271 req . query = req . query || { }
203272
204273 if ( value === false && parameter . type === 'boolean' ) {
@@ -224,8 +293,69 @@ export function queryBuilder({req, value, parameter}) {
224293
225294const stripNonAlpha = str => ( str ? str . replace ( / \W / g, '' ) : null )
226295
296+ export function baseUrl ( obj ) {
297+ const specIsOAS3 = isOAS3 ( obj . spec )
298+
299+ return specIsOAS3 ? oas3BaseUrl ( obj ) : swagger2BaseUrl ( obj )
300+ }
301+
302+ function oas3BaseUrl ( { spec, server, serverVariables = { } } ) {
303+ const servers = spec . servers
304+
305+ let selectedServerUrl = ''
306+ let selectedServerObj = null
307+
308+ if ( ! servers || ! Array . isArray ( servers ) ) {
309+ return ''
310+ }
311+
312+ if ( server ) {
313+ const serverUrls = servers . map ( srv => srv . url )
314+
315+ if ( serverUrls . indexOf ( server ) > - 1 ) {
316+ selectedServerUrl = server
317+ selectedServerObj = servers [ serverUrls . indexOf ( server ) ]
318+ }
319+ }
320+
321+ if ( ! selectedServerUrl ) {
322+ // default to the first server if we don't have one by now
323+ selectedServerUrl = servers [ 0 ] . url
324+ selectedServerObj = servers [ 0 ]
325+ }
326+
327+ if ( selectedServerUrl . indexOf ( '{' ) > - 1 ) {
328+ // do variable substitution
329+ const varNames = getVariableTemplateNames ( selectedServerUrl )
330+ varNames . forEach ( ( vari ) => {
331+ if ( selectedServerObj . variables && selectedServerObj . variables [ vari ] ) {
332+ // variable is defined in server
333+ const variableDefinition = selectedServerObj . variables [ vari ]
334+ const variableValue = serverVariables [ vari ] || variableDefinition . default
335+
336+ const re = new RegExp ( `{${ vari } }` , 'g' )
337+ selectedServerUrl = selectedServerUrl . replace ( re , variableValue )
338+ }
339+ } )
340+ }
341+
342+ return selectedServerUrl
343+ }
344+
345+ function getVariableTemplateNames ( str ) {
346+ const results = [ ]
347+ const re = / { ( [ ^ } ] + ) } / g
348+ let text
349+
350+ // eslint-disable-next-line no-cond-assign
351+ while ( text = re . exec ( str ) ) {
352+ results . push ( text [ 1 ] )
353+ }
354+ return results
355+ }
356+
227357// Compose the baseUrl ( scheme + host + basePath )
228- export function baseUrl ( { spec, scheme, contextUrl = '' } ) {
358+ function swagger2BaseUrl ( { spec, scheme, contextUrl = '' } ) {
229359 const parsedContextUrl = url . parse ( contextUrl )
230360 const firstSchemeInSpec = Array . isArray ( spec . schemes ) ? spec . schemes [ 0 ] : null
231361
0 commit comments