Skip to content

Commit d03f5cf

Browse files
authored
Merge pull request #3660 from swagger-api/char0n/3656
fix(execute): do not encode server variables by default
2 parents df83852 + 6a151d9 commit d03f5cf

File tree

5 files changed

+180
-5
lines changed

5 files changed

+180
-5
lines changed

docs/usage/http-client-for-oas-operations.md

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Property | Description
2828
`userFetch` | `Function=cross-fetch`. Custom **asynchronous** fetch function that accepts two arguments: the `url` and the `Request` object and must return a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) object. More info in [HTTP Client](http-client.md) documentation.
2929
`signal` | `AbortSignal=null`. AbortSignal object instance, which can be used to abort a request as desired.
3030
`server` | `String`. URL (`https://example.com`) or relative URI Reference (`/path/subpath`). Must match with of the defined `Server Objects`. If matched, it will be prepended to every requested path.
31+
`serverVariableEncoder` | `Function=identity`. An encoder function that is run on a server variable before substituted to the URL template.
3132
`contextUrl` | `String`. URL, e.g. `https://example.com`. Used in following situations: <br /><br />If `server` option is not matched and there is no `Server Object` defined in the definition, this URL will be prepended to every requested path.<br /><br />If matched `Server Object` is defined as relative URI Reference its `url` fixed field is resolved against `contenxtUrl`. Resolved URL will be prepended to every requested path.
3233

3334
For all later references, we will always use following OpenAPI 3.0.0 definition when referring

package-lock.json

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
"node-fetch-commonjs": "^3.3.2",
8787
"openapi-path-templating": "^1.5.1",
8888
"openapi-server-url-templating": "^1.0.0",
89+
"ramda": "^0.30.1",
8990
"ramda-adjunct": "^5.0.0",
9091
"neotraverse": "=0.6.18"
9192
},

src/execute/index.js

+20-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import cookie from 'cookie';
2+
import { identity } from 'ramda';
23
import { isPlainObject } from 'ramda-adjunct';
34
import {
45
test as testServerURLTemplate,
@@ -129,6 +130,7 @@ export function buildRequest(options) {
129130
serverVariables,
130131
http,
131132
signal,
133+
serverVariableEncoder,
132134
} = options;
133135

134136
let { parameters, parameterBuilders } = options;
@@ -183,6 +185,7 @@ export function buildRequest(options) {
183185
serverVariables,
184186
pathName,
185187
method,
188+
serverVariableEncoder,
186189
});
187190

188191
req.url += baseURL;
@@ -321,7 +324,15 @@ export function baseUrl(obj) {
321324

322325
const isNonEmptyServerList = (value) => Array.isArray(value) && value.length > 0;
323326

324-
function oas3BaseUrl({ spec, pathName, method, server, contextUrl, serverVariables = {} }) {
327+
function oas3BaseUrl({
328+
spec,
329+
pathName,
330+
method,
331+
server,
332+
contextUrl,
333+
serverVariables = {},
334+
serverVariableEncoder,
335+
}) {
325336
let servers = [];
326337
let selectedServerUrl = '';
327338
let selectedServerObj;
@@ -359,10 +370,14 @@ function oas3BaseUrl({ spec, pathName, method, server, contextUrl, serverVariabl
359370
{}
360371
);
361372

362-
selectedServerUrl = substituteServerURLTemplate(selectedServerUrl, {
363-
...selectedServerVariables,
364-
...serverVariables,
365-
});
373+
selectedServerUrl = substituteServerURLTemplate(
374+
selectedServerUrl,
375+
{
376+
...selectedServerVariables,
377+
...serverVariables,
378+
},
379+
{ encoder: typeof serverVariableEncoder === 'function' ? serverVariableEncoder : identity }
380+
);
366381
}
367382

368383
return buildOas3UrlWithContext(selectedServerUrl, contextUrl);
+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import { execute, buildRequest } from '../../src/execute/index.js';
2+
3+
describe('execute/serverVariableEncoder', () => {
4+
test('should encode when encoder provided', () => {
5+
const spec = {
6+
openapi: '3.0.3',
7+
servers: [
8+
{
9+
url: '{server}/v1',
10+
variables: {
11+
server: {
12+
default: 'https://swagger.io',
13+
},
14+
},
15+
},
16+
],
17+
paths: {
18+
'/one': {
19+
get: {
20+
operationId: 'getMe',
21+
parameters: [{ name: 'petId', in: 'query' }],
22+
},
23+
},
24+
},
25+
};
26+
27+
const spy = jest.fn().mockImplementation(() => Promise.resolve());
28+
29+
execute({
30+
fetch: spy,
31+
spec,
32+
operationId: 'getMe',
33+
serverVariableEncoder: encodeURIComponent,
34+
});
35+
36+
expect(spy.mock.calls.length).toEqual(1);
37+
expect(spy.mock.calls[0][0]).toEqual({
38+
method: 'GET',
39+
url: 'https%3A%2F%2Fswagger.io/v1/one',
40+
credentials: 'same-origin',
41+
headers: {},
42+
});
43+
});
44+
45+
test('should not encode when encoder not provided', () => {
46+
const spec = {
47+
openapi: '3.0.3',
48+
servers: [
49+
{
50+
url: '{server}/v1',
51+
variables: {
52+
server: {
53+
default: 'https://swagger.io',
54+
},
55+
},
56+
},
57+
],
58+
paths: {
59+
'/one': {
60+
get: {
61+
operationId: 'getMe',
62+
parameters: [{ name: 'petId', in: 'query' }],
63+
},
64+
},
65+
},
66+
};
67+
68+
const spy = jest.fn().mockImplementation(() => Promise.resolve());
69+
70+
execute({
71+
fetch: spy,
72+
spec,
73+
operationId: 'getMe',
74+
});
75+
76+
expect(spy.mock.calls.length).toEqual(1);
77+
expect(spy.mock.calls[0][0]).toEqual({
78+
method: 'GET',
79+
url: 'https://swagger.io/v1/one',
80+
credentials: 'same-origin',
81+
headers: {},
82+
});
83+
});
84+
});
85+
86+
describe('buildRequest/serverVariableEncoder', () => {
87+
test('should encode when encoder provided', () => {
88+
const spec = {
89+
openapi: '3.0.3',
90+
servers: [
91+
{
92+
url: '{server}/v1',
93+
variables: {
94+
server: {
95+
default: 'https://swagger.io',
96+
},
97+
},
98+
},
99+
],
100+
paths: {
101+
'/one': {
102+
get: {
103+
operationId: 'getMe',
104+
parameters: [{ name: 'petId', in: 'query' }],
105+
},
106+
},
107+
},
108+
};
109+
110+
const req = buildRequest({
111+
spec,
112+
operationId: 'getMe',
113+
parameters: { petId: 123 },
114+
serverVariableEncoder: encodeURIComponent,
115+
});
116+
117+
expect(req).toEqual({
118+
url: 'https%3A%2F%2Fswagger.io/v1/one?petId=123',
119+
method: 'GET',
120+
credentials: 'same-origin',
121+
headers: {},
122+
});
123+
});
124+
125+
test('should not encode when encoder not provided', () => {
126+
const spec = {
127+
openapi: '3.0.3',
128+
servers: [
129+
{
130+
url: '{server}/v1',
131+
variables: {
132+
server: {
133+
default: 'https://swagger.io',
134+
},
135+
},
136+
},
137+
],
138+
paths: {
139+
'/one': {
140+
get: {
141+
operationId: 'getMe',
142+
parameters: [{ name: 'petId', in: 'query' }],
143+
},
144+
},
145+
},
146+
};
147+
148+
const req = buildRequest({ spec, operationId: 'getMe', parameters: { petId: 123 } });
149+
150+
expect(req).toEqual({
151+
url: 'https://swagger.io/v1/one?petId=123',
152+
method: 'GET',
153+
credentials: 'same-origin',
154+
headers: {},
155+
});
156+
});
157+
});

0 commit comments

Comments
 (0)