Skip to content

Commit 2a3f113

Browse files
committed
Create version-specific buildRequest hooks
1 parent a9b1831 commit 2a3f113

File tree

9 files changed

+370
-310
lines changed

9 files changed

+370
-310
lines changed

src/execute/index.js

Lines changed: 34 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ import btoa from 'btoa'
66
import url from 'url'
77
import http, {mergeInQueryOrForm} from '../http'
88
import createError from '../specmap/lib/create-error'
9-
import SWAGGER2_PARAMETER_BUILDERS from './swagger2-parameter-builders'
10-
import OAS3_PARAMETER_BUILDERS from './oas3-parameter-builders'
9+
10+
import SWAGGER2_PARAMETER_BUILDERS from './swagger2/parameter-builders'
11+
import OAS3_PARAMETER_BUILDERS from './oas3/parameter-builders'
12+
import oas3BuildRequest from './oas3/build-request'
13+
import swagger2BuildRequest from './swagger2/build-request'
1114
import {
1215
getOperationRaw,
1316
idFromPathMethod,
@@ -64,12 +67,28 @@ export function execute({
6467
}
6568

6669
// Build a request, which can be handled by the `http.js` implementation.
67-
export function buildRequest({
68-
spec, operationId, parameters, securities, requestContentType,
69-
responseContentType, parameterBuilders, scheme,
70-
requestInterceptor, responseInterceptor, contextUrl, userFetch,
71-
requestBody, server, serverVariables
72-
}) {
70+
export function buildRequest(options) {
71+
const {
72+
spec,
73+
operationId,
74+
securities,
75+
requestContentType,
76+
responseContentType,
77+
scheme,
78+
requestInterceptor,
79+
responseInterceptor,
80+
contextUrl,
81+
userFetch,
82+
requestBody,
83+
server,
84+
serverVariables
85+
} = options
86+
87+
let {
88+
parameters,
89+
parameterBuilders
90+
} = options
91+
7392
const specIsOAS3 = isOAS3(spec)
7493

7594
if (!parameterBuilders) {
@@ -83,7 +102,7 @@ export function buildRequest({
83102
}
84103

85104
// Base Template
86-
let req = {
105+
const req = {
87106
url: baseUrl({spec, scheme, contextUrl, server, serverVariables}),
88107
credentials: 'same-origin',
89108
headers: {
@@ -168,85 +187,15 @@ export function buildRequest({
168187
}
169188
})
170189

171-
const requestBodyDef = operation.requestBody || {}
172-
const requestBodyMediaTypes = Object.keys(requestBodyDef.content || {})
173-
174-
// for OAS3: set the Content-Type
175-
if (specIsOAS3 && requestBody) {
176-
// does the passed requestContentType appear in the requestBody definition?
177-
const isExplicitContentTypeValid = requestContentType
178-
&& requestBodyMediaTypes.indexOf(requestContentType) > -1
179-
180-
if (requestContentType && isExplicitContentTypeValid) {
181-
req.headers['Content-Type'] = requestContentType
182-
}
183-
else if (!requestContentType) {
184-
const firstMediaType = requestBodyMediaTypes[0]
185-
if (firstMediaType) {
186-
req.headers['Content-Type'] = firstMediaType
187-
requestContentType = firstMediaType
188-
}
189-
}
190-
}
190+
// Do version-specific tasks, then return those results.
191+
const versionSpecificOptions = {...options, operation}
191192

192-
// for OAS3: add requestBody to request
193-
if (specIsOAS3 && requestBody) {
194-
if (requestContentType) {
195-
if (requestBodyMediaTypes.indexOf(requestContentType) > -1) {
196-
// only attach body if the requestBody has a definition for the
197-
// contentType that has been explicitly set
198-
if (requestContentType === 'application/x-www-form-urlencoded') {
199-
if (typeof requestBody === 'object') {
200-
req.form = {}
201-
Object.keys(requestBody).forEach((k) => {
202-
const val = requestBody[k]
203-
req.form[k] = {
204-
value: val
205-
}
206-
})
207-
}
208-
else {
209-
req.form = requestBody
210-
}
211-
}
212-
else {
213-
req.body = requestBody
214-
}
215-
}
216-
}
217-
else {
218-
req.body = requestBody
219-
}
193+
if (specIsOAS3) {
194+
return oas3BuildRequest(versionSpecificOptions, req)
220195
}
221196

222-
// Add securities, which are applicable
223-
// REVIEW: OAS3: what changed in securities?
224-
req = applySecurities({request: req, securities, operation, spec})
225-
226-
if (!specIsOAS3 && (req.body || req.form)) {
227-
// all following conditionals are Swagger2 only
228-
if (requestContentType) {
229-
req.headers['Content-Type'] = requestContentType
230-
}
231-
else if (Array.isArray(operation.consumes)) {
232-
req.headers['Content-Type'] = operation.consumes[0]
233-
}
234-
else if (Array.isArray(spec.consumes)) {
235-
req.headers['Content-Type'] = spec.consumes[0]
236-
}
237-
else if (operation.parameters && operation.parameters.filter(p => p.type === 'file').length) {
238-
req.headers['Content-Type'] = 'multipart/form-data'
239-
}
240-
else if (operation.parameters && operation.parameters.filter(p => p.in === 'formData').length) {
241-
req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
242-
}
243-
}
244-
245-
// Will add the query object into the URL, if it exists
246-
// ... will also create a FormData instance, if multipart/form-data (eg: a file)
247-
mergeInQueryOrForm(req)
248-
249-
return req
197+
// If not OAS3, then treat as Swagger2.
198+
return swagger2BuildRequest(versionSpecificOptions, req)
250199
}
251200

252201
const stripNonAlpha = str => (str ? str.replace(/\W/g, '') : null)
@@ -330,59 +279,3 @@ function swagger2BaseUrl({spec, scheme, contextUrl = ''}) {
330279

331280
return ''
332281
}
333-
334-
335-
// Add security values, to operations - that declare their need on them
336-
export function applySecurities({request, securities = {}, operation = {}, spec}) {
337-
const result = assign({}, request)
338-
const {authorized = {}, specSecurity = []} = securities
339-
const security = operation.security || specSecurity
340-
const isAuthorized = authorized && !!Object.keys(authorized).length
341-
const securityDef = spec.securityDefinitions
342-
343-
result.headers = result.headers || {}
344-
result.query = result.query || {}
345-
346-
if (!Object.keys(securities).length || !isAuthorized || !security ||
347-
(Array.isArray(operation.security) && !operation.security.length)) {
348-
return request
349-
}
350-
351-
security.forEach((securityObj, index) => {
352-
for (const key in securityObj) {
353-
const auth = authorized[key]
354-
if (!auth) {
355-
continue
356-
}
357-
358-
const token = auth.token
359-
const value = auth.value || auth
360-
const schema = securityDef[key]
361-
const {type} = schema
362-
const accessToken = token && token.access_token
363-
const tokenType = token && token.token_type
364-
365-
if (auth) {
366-
if (type === 'apiKey') {
367-
const inType = schema.in === 'query' ? 'query' : 'headers'
368-
result[inType] = result[inType] || {}
369-
result[inType][schema.name] = value
370-
}
371-
else if (type === 'basic') {
372-
if (value.header) {
373-
result.headers.authorization = value.header
374-
}
375-
else {
376-
value.base64 = btoa(`${value.username}:${value.password}`)
377-
result.headers.authorization = `Basic ${value.base64}`
378-
}
379-
}
380-
else if (type === 'oauth2' && accessToken) {
381-
result.headers.authorization = `${tokenType || 'Bearer'} ${accessToken}`
382-
}
383-
}
384-
}
385-
})
386-
387-
return result
388-
}

src/execute/oas3/build-request.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// This function runs after the common function,
2+
// `src/execute/index.js#buildRequest`
3+
4+
export default function (options, req) {
5+
const {
6+
operation,
7+
requestBody
8+
} = options
9+
10+
let {
11+
requestContentType
12+
} = options
13+
14+
const requestBodyDef = operation.requestBody || {}
15+
const requestBodyMediaTypes = Object.keys(requestBodyDef.content || {})
16+
17+
// for OAS3: set the Content-Type
18+
if (requestBody) {
19+
// does the passed requestContentType appear in the requestBody definition?
20+
const isExplicitContentTypeValid = requestContentType
21+
&& requestBodyMediaTypes.indexOf(requestContentType) > -1
22+
23+
if (requestContentType && isExplicitContentTypeValid) {
24+
req.headers['Content-Type'] = requestContentType
25+
}
26+
else if (!requestContentType) {
27+
const firstMediaType = requestBodyMediaTypes[0]
28+
if (firstMediaType) {
29+
req.headers['Content-Type'] = firstMediaType
30+
requestContentType = firstMediaType
31+
}
32+
}
33+
}
34+
35+
// for OAS3: add requestBody to request
36+
if (requestBody) {
37+
if (requestContentType) {
38+
if (requestBodyMediaTypes.indexOf(requestContentType) > -1) {
39+
// only attach body if the requestBody has a definition for the
40+
// contentType that has been explicitly set
41+
if (requestContentType === 'application/x-www-form-urlencoded') {
42+
if (typeof requestBody === 'object') {
43+
req.form = {}
44+
Object.keys(requestBody).forEach((k) => {
45+
const val = requestBody[k]
46+
req.form[k] = {
47+
value: val
48+
}
49+
})
50+
}
51+
else {
52+
req.form = requestBody
53+
}
54+
}
55+
else {
56+
req.body = requestBody
57+
}
58+
}
59+
}
60+
else {
61+
req.body = requestBody
62+
}
63+
}
64+
65+
return req
66+
}

src/execute/oas3-parameter-builders.js renamed to src/execute/oas3/parameter-builders.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import stylize from './oas3-style-serializer'
1+
import stylize from './style-serializer'
2+
23
export default {
34
path,
45
query
File renamed without changes.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// This function runs after the common function,
2+
// `src/execute/index.js#buildRequest`
3+
4+
import btoa from 'btoa'
5+
import assign from 'lodash/assign'
6+
import http, {mergeInQueryOrForm} from '../../http'
7+
8+
9+
export default function (options, req) {
10+
const {
11+
spec,
12+
operation,
13+
securities,
14+
requestContentType
15+
} = options
16+
// Add securities, which are applicable
17+
req = applySecurities({request: req, securities, operation, spec})
18+
19+
if (req.body || req.form) {
20+
// all following conditionals are Swagger2 only
21+
if (requestContentType) {
22+
req.headers['Content-Type'] = requestContentType
23+
}
24+
else if (Array.isArray(operation.consumes)) {
25+
req.headers['Content-Type'] = operation.consumes[0]
26+
}
27+
else if (Array.isArray(spec.consumes)) {
28+
req.headers['Content-Type'] = spec.consumes[0]
29+
}
30+
else if (operation.parameters && operation.parameters.filter(p => p.type === 'file').length) {
31+
req.headers['Content-Type'] = 'multipart/form-data'
32+
}
33+
else if (operation.parameters && operation.parameters.filter(p => p.in === 'formData').length) {
34+
req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
35+
}
36+
}
37+
38+
// Will add the query object into the URL, if it exists
39+
// ... will also create a FormData instance, if multipart/form-data (eg: a file)
40+
mergeInQueryOrForm(req)
41+
42+
return req
43+
}
44+
45+
// Add security values, to operations - that declare their need on them
46+
export function applySecurities({request, securities = {}, operation = {}, spec}) {
47+
const result = assign({}, request)
48+
const {authorized = {}, specSecurity = []} = securities
49+
const security = operation.security || specSecurity
50+
const isAuthorized = authorized && !!Object.keys(authorized).length
51+
const securityDef = spec.securityDefinitions
52+
53+
result.headers = result.headers || {}
54+
result.query = result.query || {}
55+
56+
if (!Object.keys(securities).length || !isAuthorized || !security ||
57+
(Array.isArray(operation.security) && !operation.security.length)) {
58+
return request
59+
}
60+
61+
security.forEach((securityObj, index) => {
62+
for (const key in securityObj) {
63+
const auth = authorized[key]
64+
if (!auth) {
65+
continue
66+
}
67+
68+
const token = auth.token
69+
const value = auth.value || auth
70+
const schema = securityDef[key]
71+
const {type} = schema
72+
const accessToken = token && token.access_token
73+
const tokenType = token && token.token_type
74+
75+
if (auth) {
76+
if (type === 'apiKey') {
77+
const inType = schema.in === 'query' ? 'query' : 'headers'
78+
result[inType] = result[inType] || {}
79+
result[inType][schema.name] = value
80+
}
81+
else if (type === 'basic') {
82+
if (value.header) {
83+
result.headers.authorization = value.header
84+
}
85+
else {
86+
value.base64 = btoa(`${value.username}:${value.password}`)
87+
result.headers.authorization = `Basic ${value.base64}`
88+
}
89+
}
90+
else if (type === 'oauth2' && accessToken) {
91+
result.headers.authorization = `${tokenType || 'Bearer'} ${accessToken}`
92+
}
93+
}
94+
}
95+
})
96+
97+
return result
98+
}

0 commit comments

Comments
 (0)