Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(execute): do not encode server variables by default #3660

Merged
merged 1 commit into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/usage/http-client-for-oas-operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Property | Description
`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.
`signal` | `AbortSignal=null`. AbortSignal object instance, which can be used to abort a request as desired.
`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.
`serverVariableEncoder` | `Function=identity`. An encoder function that is run on a server variable before substituted to the URL template.
`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.

For all later references, we will always use following OpenAPI 3.0.0 definition when referring
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
"node-fetch-commonjs": "^3.3.2",
"openapi-path-templating": "^1.5.1",
"openapi-server-url-templating": "^1.0.0",
"ramda": "^0.30.1",
"ramda-adjunct": "^5.0.0",
"neotraverse": "=0.6.18"
},
Expand Down
25 changes: 20 additions & 5 deletions src/execute/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import cookie from 'cookie';
import { identity } from 'ramda';
import { isPlainObject } from 'ramda-adjunct';
import {
test as testServerURLTemplate,
Expand Down Expand Up @@ -129,6 +130,7 @@ export function buildRequest(options) {
serverVariables,
http,
signal,
serverVariableEncoder,
} = options;

let { parameters, parameterBuilders } = options;
Expand Down Expand Up @@ -183,6 +185,7 @@ export function buildRequest(options) {
serverVariables,
pathName,
method,
serverVariableEncoder,
});

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

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

function oas3BaseUrl({ spec, pathName, method, server, contextUrl, serverVariables = {} }) {
function oas3BaseUrl({
spec,
pathName,
method,
server,
contextUrl,
serverVariables = {},
serverVariableEncoder,
}) {
let servers = [];
let selectedServerUrl = '';
let selectedServerObj;
Expand Down Expand Up @@ -359,10 +370,14 @@ function oas3BaseUrl({ spec, pathName, method, server, contextUrl, serverVariabl
{}
);

selectedServerUrl = substituteServerURLTemplate(selectedServerUrl, {
...selectedServerVariables,
...serverVariables,
});
selectedServerUrl = substituteServerURLTemplate(
selectedServerUrl,
{
...selectedServerVariables,
...serverVariables,
},
{ encoder: typeof serverVariableEncoder === 'function' ? serverVariableEncoder : identity }
);
}

return buildOas3UrlWithContext(selectedServerUrl, contextUrl);
Expand Down
157 changes: 157 additions & 0 deletions test/execute/server-variable-encoder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { execute, buildRequest } from '../../src/execute/index.js';

describe('execute/serverVariableEncoder', () => {
test('should encode when encoder provided', () => {
const spec = {
openapi: '3.0.3',
servers: [
{
url: '{server}/v1',
variables: {
server: {
default: 'https://swagger.io',
},
},
},
],
paths: {
'/one': {
get: {
operationId: 'getMe',
parameters: [{ name: 'petId', in: 'query' }],
},
},
},
};

const spy = jest.fn().mockImplementation(() => Promise.resolve());

execute({
fetch: spy,
spec,
operationId: 'getMe',
serverVariableEncoder: encodeURIComponent,
});

expect(spy.mock.calls.length).toEqual(1);
expect(spy.mock.calls[0][0]).toEqual({
method: 'GET',
url: 'https%3A%2F%2Fswagger.io/v1/one',
credentials: 'same-origin',
headers: {},
});
});

test('should not encode when encoder not provided', () => {
const spec = {
openapi: '3.0.3',
servers: [
{
url: '{server}/v1',
variables: {
server: {
default: 'https://swagger.io',
},
},
},
],
paths: {
'/one': {
get: {
operationId: 'getMe',
parameters: [{ name: 'petId', in: 'query' }],
},
},
},
};

const spy = jest.fn().mockImplementation(() => Promise.resolve());

execute({
fetch: spy,
spec,
operationId: 'getMe',
});

expect(spy.mock.calls.length).toEqual(1);
expect(spy.mock.calls[0][0]).toEqual({
method: 'GET',
url: 'https://swagger.io/v1/one',
credentials: 'same-origin',
headers: {},
});
});
});

describe('buildRequest/serverVariableEncoder', () => {
test('should encode when encoder provided', () => {
const spec = {
openapi: '3.0.3',
servers: [
{
url: '{server}/v1',
variables: {
server: {
default: 'https://swagger.io',
},
},
},
],
paths: {
'/one': {
get: {
operationId: 'getMe',
parameters: [{ name: 'petId', in: 'query' }],
},
},
},
};

const req = buildRequest({
spec,
operationId: 'getMe',
parameters: { petId: 123 },
serverVariableEncoder: encodeURIComponent,
});

expect(req).toEqual({
url: 'https%3A%2F%2Fswagger.io/v1/one?petId=123',
method: 'GET',
credentials: 'same-origin',
headers: {},
});
});

test('should not encode when encoder not provided', () => {
const spec = {
openapi: '3.0.3',
servers: [
{
url: '{server}/v1',
variables: {
server: {
default: 'https://swagger.io',
},
},
},
],
paths: {
'/one': {
get: {
operationId: 'getMe',
parameters: [{ name: 'petId', in: 'query' }],
},
},
},
};

const req = buildRequest({ spec, operationId: 'getMe', parameters: { petId: 123 } });

expect(req).toEqual({
url: 'https://swagger.io/v1/one?petId=123',
method: 'GET',
credentials: 'same-origin',
headers: {},
});
});
});