From 9d6b30c55a3e9ac38e4076c8a55f1734bb1b674e Mon Sep 17 00:00:00 2001 From: Roman Hotsiy Date: Mon, 23 Jul 2018 12:00:29 +0300 Subject: [PATCH] feat: simple variable substitution support fixes #565 --- src/utils/__tests__/openapi.test.ts | 48 +++++++++++++++++++++++++++++ src/utils/helpers.ts | 28 +++++++++++++++++ src/utils/openapi.ts | 19 +++++++----- 3 files changed, 88 insertions(+), 7 deletions(-) diff --git a/src/utils/__tests__/openapi.test.ts b/src/utils/__tests__/openapi.test.ts index e1166c024a..9e61c9080d 100644 --- a/src/utils/__tests__/openapi.test.ts +++ b/src/utils/__tests__/openapi.test.ts @@ -234,6 +234,24 @@ describe('Utils', () => { expect(res).toEqual([{ url: 'http://base.com/sandbox/test', description: '' }]); }); + it('should correcly resolve url with server relative path', () => { + const res = normalizeServers('http://base.com/subpath/spec.yaml', [ + { + url: '/sandbox/test', + }, + ]); + expect(res).toEqual([{ url: 'http://base.com/sandbox/test', description: '' }]); + }); + + it('should correcly resolve url with relative path', () => { + const res = normalizeServers('http://base.com/subpath/spec.yaml', [ + { + url: 'sandbox/test', + }, + ]); + expect(res).toEqual([{ url: 'http://base.com/subpath/sandbox/test', description: '' }]); + }); + it('should prefer server host over spec`s one', () => { const res = normalizeServers('http://base.com/spec.yaml', [ { @@ -261,5 +279,35 @@ describe('Utils', () => { ]); expect(res).toEqual([{ url: 'https://base.com/sandbox/test', description: 'test' }]); }); + + it('should expand variables', () => { + const servers = normalizeServers('', [ + { + url: '{protocol}{host}{basePath}', + variables: { + protocol: { + default: 'http://', + }, + host: { + default: '127.0.0.1', + }, + basePath: { + default: '/path/to/endpoint', + }, + }, + }, + { + url: 'http://127.0.0.2:{port}', + variables: {}, + }, + { + url: 'http://127.0.0.3', + }, + ]); + + expect(servers[0].url).toEqual('http://127.0.0.1/path/to/endpoint'); + expect(servers[1].url).toEqual('http://127.0.0.2:{port}'); + expect(servers[2].url).toEqual('http://127.0.0.3'); + }); }); }); diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index 53703e1c94..116390633d 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -1,4 +1,5 @@ import slugify from 'slugify'; +import { format, parse } from 'url'; /** * Maps over array passing `isLast` bool to iterator as the second arguemnt @@ -133,3 +134,30 @@ export function safeSlugify(value: string): string { .replace(/-+$/, '') ); // Trim - from end of text } + +export function isAbsoluteUrl(url: string) { + return /(?:^[a-z][a-z0-9+.-]*:|\/\/)/i.test(url); +} + +/** + * simple resolve URL which doesn't break on strings with url fragments + * e.g. resolveUrl('http://test.com:{port}', 'path') results in http://test.com:{port}/path + */ +export function resolveUrl(url: string, to: string) { + let res; + if (to.startsWith('//')) { + const { protocol: specProtocol } = parse(url); + res = `${specProtocol}${to}`; + } else if (isAbsoluteUrl(to)) { + res = to; + } else if (!to.startsWith('/')) { + res = stripTrailingSlash(url) + '/' + to; + } else { + const urlObj = parse(url); + res = format({ + ...urlObj, + pathname: to, + }); + } + return stripTrailingSlash(res); +} diff --git a/src/utils/openapi.ts b/src/utils/openapi.ts index 0efa0d69cf..2aecb6459d 100644 --- a/src/utils/openapi.ts +++ b/src/utils/openapi.ts @@ -1,5 +1,4 @@ import { dirname } from 'path'; -import { parse as urlParse, resolve as resolveUrl } from 'url'; import { OpenAPIParser } from '../services/OpenAPIParser'; import { @@ -11,7 +10,7 @@ import { Referenced, } from '../types'; import { IS_BROWSER } from './dom'; -import { isNumeric, stripTrailingSlash } from './helpers'; +import { isNumeric, resolveUrl } from './helpers'; function isWildcardStatusCode(statusCode: string | number): statusCode is string { return typeof statusCode === 'string' && /\dxx/i.test(statusCode); @@ -240,6 +239,13 @@ export function mergeSimilarMediaTypes(types: Dict): Dict (variables[name] && variables[name].default) || match, + ); +} + export function normalizeServers( specUrl: string | undefined, servers: OpenAPIServer[], @@ -254,17 +260,16 @@ export function normalizeServers( }, ]; } - const { protocol: specProtocol } = urlParse(baseUrl); - function normalizeUrl(url: string): string { - url = resolveUrl(baseUrl, url); - return stripTrailingSlash(url.startsWith('//') ? `${specProtocol}${url}` : url); + function normalizeUrl(url: string, variables: object | undefined): string { + url = expandVariables(url, variables); + return resolveUrl(baseUrl, url); } return servers.map(server => { return { ...server, - url: normalizeUrl(server.url), + url: normalizeUrl(server.url, server.variables), description: server.description || '', }; });