Skip to content

Commit

Permalink
feat: addition of a splitVariables method (#432)
Browse files Browse the repository at this point in the history
  • Loading branch information
erunion authored May 11, 2021
1 parent b477aaa commit 76bb481
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 1 deletion.
72 changes: 72 additions & 0 deletions __tests__/tooling/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,78 @@ describe('#splitUrl()', () => {
});
});

describe('#splitVariables()', () => {
it('should return false if no match was found', () => {
expect(new Oas().splitVariables('https://local.dev')).toBe(false);
});

it('should not return any variables for a server url that has none', () => {
expect(new Oas({ servers: [{ url: 'https://example.com' }] }).splitVariables('https://example.com')).toStrictEqual({
selected: 0,
variables: {},
});
});

it('should find and return variables', () => {
const oas = new Oas({
servers: [
{
url: 'http://{name}.local/{basePath}',
variables: {
name: { default: 'demo' },
basePath: { default: 'v2' },
},
},
{
url: 'https://{name}.example.com:{port}/{basePath}',
variables: {
name: { default: 'demo' },
port: { default: '443' },
basePath: { default: 'v2' },
},
},
],
});

const url = 'https://buster.example.com:3000/pet';
const split = oas.splitVariables(url);

expect(split).toStrictEqual({
selected: 1,
variables: {
name: 'buster',
port: '3000',
basePath: 'pet',
},
});

expect(oas.url(split.selected, split.variables)).toBe(url);
});

// Surprisingly this is valid by the spec. :cowboy-sweat:
it('should handle if a variable is duped in the server url', () => {
const oas = new Oas({
servers: [
{
url: 'http://{region}.api.example.com/region/{region}/{lang}',
variables: {
region: { default: 'us' },
lang: { default: 'en-US' },
},
},
],
});

expect(oas.splitVariables('http://eu.api.example.com/region/eu/fr-CH')).toStrictEqual({
selected: 0,
variables: {
region: 'eu',
lang: 'fr-CH',
},
});
});
});

describe('#variables([selected])', () => {
it('should return with list of variables', () => {
const variables = { path: { description: 'path description' } };
Expand Down
45 changes: 44 additions & 1 deletion tooling/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ function normalizedUrl(oas, selected) {
* @returns {String}
*/
function transformUrlIntoRegex(url) {
return stripTrailingSlash(url.replace(/{([-_a-zA-Z0-9[\]]+)}/g, () => '([-_a-zA-Z0-9[\\]]+)'));
return stripTrailingSlash(url.replace(/{([-_a-zA-Z0-9[\]]+)}/g, '([-_a-zA-Z0-9[\\]]+)'));
}

function normalizePath(path) {
Expand Down Expand Up @@ -209,6 +209,49 @@ class Oas {
});
}

/**
* With a fully composed server URL, run through our list of known OAS servers and return back which server URL was
* selected along with any contained server variables split out.
*
* For example, if you have an OAS server URL of `https://{name}.example.com:{port}/{basePath}`, and pass in
* `https://buster.example.com:3000/pet` to this function, you'll get back the following:
*
* { selected: 0, variables: { name: 'buster', port: 3000, basePath: 'pet' } }
*
* Re-supplying this data to `oas.url()` should return the same URL you passed into this method.
*
* @param {String} baseUrl
* @returns {Object|Boolean}
*/
splitVariables(baseUrl) {
const matchedServer = (this.servers || [])
.map((server, i) => {
const rgx = transformUrlIntoRegex(server.url);
const found = new RegExp(rgx).exec(baseUrl);
if (!found) {
return false;
}

// While it'd be nice to use named regex groups to extract path parameters from the URL and match them up with
// the variables that we have present in it, JS unfortunately doesn't support having the groups duplicated. So
// instead of doing that we need to re-regex the server URL, this time splitting on the path parameters -- this
// way we'll be able to extract the parameter names and match them up with the matched server that we obtained
// above.
const variables = {};
[...server.url.matchAll(/{([-_a-zA-Z0-9[\]]+)}/g)].forEach((variable, y) => {
variables[variable[1]] = found[y + 1];
});

return {
selected: i,
variables,
};
})
.filter(Boolean);

return matchedServer.length ? matchedServer[0] : false;
}

/**
* Replace templated variables with supplied data in a given URL.
*
Expand Down

0 comments on commit 76bb481

Please sign in to comment.