Skip to content

Commit ada894f

Browse files
authored
Merge branch 'master' into bug/opid-safe-access
2 parents c910317 + a46c4fa commit ada894f

File tree

11 files changed

+847
-22
lines changed

11 files changed

+847
-22
lines changed

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "swagger-client",
3-
"version": "3.2.2",
3+
"version": "3.3.0",
44
"description": "SwaggerJS - a collection of interfaces for OAI specs",
55
"main": "dist/index.js",
66
"repository": "[email protected]:swagger-api/swagger-js.git",
@@ -50,7 +50,7 @@
5050
"eslint-config-airbnb-base": "^11.1.1",
5151
"eslint-plugin-import": "^2.2.0",
5252
"expect": "^1.20.2",
53-
"fetch-mock": "~5.12.0",
53+
"fetch-mock": "^5.12.0",
5454
"glob": "^7.1.1",
5555
"json-loader": "^0.5.4",
5656
"license-checker": "^8.0.3",
@@ -63,6 +63,7 @@
6363
"dependencies": {
6464
"babel-runtime": "^6.23.0",
6565
"btoa": "1.1.2",
66+
"cookie": "^0.3.1",
6667
"deep-extend": "^0.4.1",
6768
"fast-json-patch": "1.1.8",
6869
"isomorphic-fetch": "2.2.1",

src/execute/index.js

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import isPlainObject from 'lodash/isPlainObject'
44
import isArray from 'lodash/isArray'
55
import btoa from 'btoa'
66
import url from 'url'
7+
import cookie from 'cookie'
78
import http, {mergeInQueryOrForm} from '../http'
89
import createError from '../specmap/lib/create-error'
910

@@ -109,7 +110,8 @@ export function buildRequest(options) {
109110
// This breaks CORSs... removing this line... probably breaks oAuth. Need to address that
110111
// This also breaks tests
111112
// 'access-control-allow-origin': '*'
112-
}
113+
},
114+
cookies: {}
113115
}
114116

115117
if (requestInterceptor) {
@@ -124,6 +126,11 @@ export function buildRequest(options) {
124126

125127
// Mostly for testing
126128
if (!operationId) {
129+
// Not removing req.cookies causes testing issues and would
130+
// change our interface, so we're always sure to remove it.
131+
// See the same statement lower down in this function for
132+
// more context.
133+
delete req.cookies
127134
return req
128135
}
129136

@@ -198,6 +205,26 @@ export function buildRequest(options) {
198205
req = swagger2BuildRequest(versionSpecificOptions, req)
199206
}
200207

208+
209+
// If the cookie convenience object exists in our request,
210+
// serialize its content and then delete the cookie object.
211+
if (req.cookies && Object.keys(req.cookies).length) {
212+
const cookieString = Object.keys(req.cookies).reduce((prev, cookieName) => {
213+
const cookieValue = req.cookies[cookieName]
214+
const prefix = prev ? '&' : ''
215+
const stringified = cookie.serialize(cookieName, cookieValue)
216+
return prev + prefix + stringified
217+
}, '')
218+
req.headers.Cookie = cookieString
219+
}
220+
221+
if (req.cookies) {
222+
// even if no cookies were defined, we need to remove
223+
// the cookies key from our request, or many many legacy
224+
// tests will break.
225+
delete req.cookies
226+
}
227+
201228
// Will add the query object into the URL, if it exists
202229
// ... will also create a FormData instance, if multipart/form-data (eg: a file)
203230
mergeInQueryOrForm(req)
@@ -219,7 +246,7 @@ function oas3BaseUrl({spec, server, contextUrl, serverVariables = {}}) {
219246
let selectedServerUrl = ''
220247
let selectedServerObj = null
221248

222-
if (server) {
249+
if (server && servers) {
223250
const serverUrls = servers.map(srv => srv.url)
224251

225252
if (serverUrls.indexOf(server) > -1) {
@@ -259,15 +286,18 @@ function buildOas3UrlWithContext(ourUrl = '', contextUrl = '') {
259286
const computedScheme = stripNonAlpha(parsedUrl.protocol) || stripNonAlpha(parsedContextUrl.protocol) || ''
260287
const computedHost = parsedUrl.host || parsedContextUrl.host
261288
const computedPath = parsedUrl.pathname || ''
289+
let res
262290

263291
if (computedScheme && computedHost) {
264-
const res = `${computedScheme}://${computedHost + computedPath}`
292+
res = `${computedScheme}://${computedHost + computedPath}`
265293

266294
// If last character is '/', trim it off
267-
return res[res.length - 1] === '/' ? res.slice(0, -1) : res
295+
}
296+
else {
297+
res = computedPath
268298
}
269299

270-
return ''
300+
return res[res.length - 1] === '/' ? res.slice(0, -1) : res
271301
}
272302

273303
function getVariableTemplateNames(str) {
@@ -290,13 +320,17 @@ function swagger2BaseUrl({spec, scheme, contextUrl = ''}) {
290320
const computedScheme = scheme || firstSchemeInSpec || stripNonAlpha(parsedContextUrl.protocol) || 'http'
291321
const computedHost = spec.host || parsedContextUrl.host || ''
292322
const computedPath = spec.basePath || ''
323+
let res
293324

294325
if (computedScheme && computedHost) {
295-
const res = `${computedScheme}://${computedHost + computedPath}`
296-
297-
// If last character is '/', trim it off
298-
return res[res.length - 1] === '/' ? res.slice(0, -1) : res
326+
// we have what we need for an absolute URL
327+
res = `${computedScheme}://${computedHost + computedPath}`
328+
}
329+
else {
330+
// if not, a relative URL will have to do
331+
res = computedPath
299332
}
300333

301-
return ''
334+
// If last character is '/', trim it off
335+
return res[res.length - 1] === '/' ? res.slice(0, -1) : res
302336
}

src/execute/oas3/build-request.js

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
11
// This function runs after the common function,
22
// `src/execute/index.js#buildRequest`
3+
import assign from 'lodash/assign'
4+
import get from 'lodash/get'
5+
import btoa from 'btoa'
36

47
export default function (options, req) {
58
const {
69
operation,
7-
requestBody
10+
requestBody,
11+
securities,
12+
spec
813
} = options
914

1015
let {
1116
requestContentType
1217
} = options
1318

19+
req = applySecurities({request: req, securities, operation, spec})
20+
1421
const requestBodyDef = operation.requestBody || {}
1522
const requestBodyMediaTypes = Object.keys(requestBodyDef.content || {})
1623

@@ -64,3 +71,73 @@ export default function (options, req) {
6471

6572
return req
6673
}
74+
75+
// Add security values, to operations - that declare their need on them
76+
// Adapted from the Swagger2 implementation
77+
export function applySecurities({request, securities = {}, operation = {}, spec}) {
78+
const result = assign({}, request)
79+
const {authorized = {}} = securities
80+
const security = operation.security || spec.security || []
81+
const isAuthorized = authorized && !!Object.keys(authorized).length
82+
const securityDef = get(spec, ['components', 'securitySchemes']) || {}
83+
84+
result.headers = result.headers || {}
85+
result.query = result.query || {}
86+
87+
if (!Object.keys(securities).length || !isAuthorized || !security ||
88+
(Array.isArray(operation.security) && !operation.security.length)) {
89+
return request
90+
}
91+
92+
security.forEach((securityObj, index) => {
93+
for (const key in securityObj) {
94+
const auth = authorized[key]
95+
const schema = securityDef[key]
96+
97+
if (!auth) {
98+
continue
99+
}
100+
101+
const value = auth.value || auth
102+
const {type} = schema
103+
104+
if (auth) {
105+
if (type === 'apiKey') {
106+
if (schema.in === 'query') {
107+
result.query[schema.name] = value
108+
}
109+
if (schema.in === 'header') {
110+
result.headers[schema.name] = value
111+
}
112+
if (schema.in === 'cookie') {
113+
result.cookies[schema.name] = value
114+
}
115+
}
116+
else if (type === 'http') {
117+
if (schema.scheme === 'basic') {
118+
const {username, password} = value
119+
const encoded = btoa(`${username}:${password}`)
120+
result.headers.Authorization = `Basic ${encoded}`
121+
}
122+
123+
if (schema.scheme === 'bearer') {
124+
result.headers.Authorization = `Bearer ${value}`
125+
}
126+
}
127+
else if (type === 'oauth2') {
128+
const token = auth.token || {}
129+
const accessToken = token.access_token
130+
let tokenType = token.token_type
131+
132+
if (!tokenType || tokenType.toLowerCase() === 'bearer') {
133+
tokenType = 'Bearer'
134+
}
135+
136+
result.headers.Authorization = `${tokenType} ${accessToken}`
137+
}
138+
}
139+
}
140+
})
141+
142+
return result
143+
}

src/execute/oas3/parameter-builders.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,19 @@ function query({req, value, parameter}) {
9191
}
9292
}
9393

94+
const PARAMETER_HEADER_BLACKLIST = [
95+
'accept',
96+
'authorization',
97+
'content-type'
98+
]
99+
94100
function header({req, parameter, value}) {
95101
req.headers = req.headers || {}
102+
103+
if (PARAMETER_HEADER_BLACKLIST.indexOf(parameter.name.toLowerCase()) > -1) {
104+
return
105+
}
106+
96107
if (typeof value !== 'undefined') {
97108
req.headers[parameter.name] = stylize({
98109
key: parameter.name,

src/execute/swagger2/build-request.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import btoa from 'btoa'
55
import assign from 'lodash/assign'
6-
import http, {mergeInQueryOrForm} from '../../http'
6+
import http from '../../http'
77

88

99
export default function (options, req) {

src/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ Swagger.prototype = {
6969
return Swagger.resolve({
7070
spec: this.spec,
7171
url: this.url,
72-
allowMetaPatches: this.allowMetaPatches
72+
allowMetaPatches: this.allowMetaPatches,
73+
requestInterceptor: this.requestInterceptor || null,
74+
responseInterceptor: this.responseInterceptor || null
7375
}).then((obj) => {
7476
this.originalSpec = this.spec
7577
this.spec = obj.spec

src/resolver.js

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ import Http from './http'
22
import mapSpec, {plugins} from './specmap'
33
import {normalizeSwagger} from './helpers'
44

5-
export function makeFetchJSON(http) {
5+
export function makeFetchJSON(http, opts = {}) {
6+
const {requestInterceptor, responseInterceptor} = opts
67
return (docPath) => {
78
return http({
89
url: docPath,
910
loadSpec: true,
11+
requestInterceptor,
12+
responseInterceptor,
1013
headers: {
1114
Accept: 'application/json'
1215
},
@@ -23,10 +26,17 @@ export function clearCache() {
2326
plugins.refs.clearCache()
2427
}
2528

26-
export default function resolve({
27-
http, fetch, spec, url, baseDoc, mode, allowMetaPatches = true,
28-
modelPropertyMacro, parameterMacro
29-
}) {
29+
export default function resolve(obj) {
30+
const {
31+
fetch, spec, url, mode, allowMetaPatches = true,
32+
modelPropertyMacro, parameterMacro, requestInterceptor,
33+
responseInterceptor
34+
} = obj
35+
36+
let {http, baseDoc} = obj
37+
38+
// console.log(obj)
39+
3040
// @TODO Swagger-UI uses baseDoc instead of url, this is to allow both
3141
// need to fix and pick one.
3242
baseDoc = baseDoc || url
@@ -36,7 +46,7 @@ export default function resolve({
3646
http = fetch || http || Http
3747

3848
if (!spec) {
39-
return makeFetchJSON(http)(baseDoc).then(doResolve)
49+
return makeFetchJSON(http, {requestInterceptor, responseInterceptor})(baseDoc).then(doResolve)
4050
}
4151

4252
return doResolve(spec)
@@ -47,7 +57,7 @@ export default function resolve({
4757
}
4858

4959
// Build a json-fetcher ( ie: give it a URL and get json out )
50-
plugins.refs.fetchJSON = makeFetchJSON(http)
60+
plugins.refs.fetchJSON = makeFetchJSON(http, {requestInterceptor, responseInterceptor})
5161

5262
const plugs = [plugins.refs]
5363

test/execute/baseurl.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,15 @@ describe('baseUrl', () => {
129129

130130
expect(res).toEqual('https://example.com:9090')
131131
})
132+
133+
it('should include a basePath when no contextUrl is available', () => {
134+
const res = baseUrl({
135+
spec: {
136+
title: 'a spec',
137+
basePath: '/mybase'
138+
}
139+
})
140+
141+
expect(res).toEqual('/mybase')
142+
})
132143
})

0 commit comments

Comments
 (0)