Skip to content

Commit 47c24cb

Browse files
committed
fix(rest): allow . to be used in openapi path template
See #1866
1 parent e492e0f commit 47c24cb

File tree

5 files changed

+51
-15
lines changed

5 files changed

+51
-15
lines changed

packages/rest/src/router/openapi-path.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export function validateApiPath(path: string = '/') {
3434
);
3535
}
3636

37-
const regexpPath = path.replace(POSSIBLE_VARNAME_PATTERN, ':$1');
37+
const regexpPath = toExpressPath(path);
3838
tokens = pathToRegExp.parse(regexpPath);
3939
for (const token of tokens) {
4040
if (typeof token === 'string') continue;
@@ -62,5 +62,7 @@ export function getPathVariables(path: string) {
6262
* @param path OpenAPI path with optional variables as `{var}`
6363
*/
6464
export function toExpressPath(path: string) {
65-
return path.replace(POSSIBLE_VARNAME_PATTERN, ':$1');
65+
// Convert `.` to `\\.` so that path-to-regexp will treat it as the plain
66+
// `.` character
67+
return path.replace(POSSIBLE_VARNAME_PATTERN, ':$1').replace('.', '\\.');
6668
}

packages/rest/src/router/trie.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
// License text available at https://opensource.org/licenses/MIT
55

66
import {PathParameterValues} from '../types';
7+
import {toExpressPath} from './openapi-path';
8+
import pathToRegexp = require('path-to-regexp');
79

810
/**
911
* A Node in the trie
@@ -215,17 +217,13 @@ function createNode<T>(
215217
}
216218

217219
// Check if the key has variables such as `{var}`
218-
const pattern = /\{([^\{]*)\}/g;
219-
const names: string[] = [];
220-
let match;
221-
while ((match = pattern.exec(key))) {
222-
names.push(match[1]);
223-
}
220+
const path = toExpressPath(key);
221+
const params: pathToRegexp.Key[] = [];
222+
const re = pathToRegexp(path, params);
224223

225-
if (names.length) {
226-
child.names = names;
227-
const re = '^' + key.replace(/\{([^\}]+)\}/g, '(.+)') + '$';
228-
child.regexp = new RegExp(re);
224+
if (params.length) {
225+
child.names = params.map(p => `${p.name}`);
226+
child.regexp = re;
229227
}
230228

231229
// Add the node to the parent

packages/rest/test/unit/router/openapi-path.unit.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ describe('validateApiPath', () => {
2222
expect(path).to.eql('/{foo}/{bar}');
2323
});
2424

25+
it('allows /{foo}.{bar}', () => {
26+
const path = validateApiPath('/{foo}.{bar}');
27+
expect(path).to.eql('/{foo}.{bar}');
28+
});
29+
30+
it('allows /{foo}@{bar}', () => {
31+
const path = validateApiPath('/{foo}@{bar}');
32+
expect(path).to.eql('/{foo}@{bar}');
33+
});
34+
2535
it('allows /{_foo}/{bar}', () => {
2636
const path = validateApiPath('/{_foo}/{bar}');
2737
expect(path).to.eql('/{_foo}/{bar}');

packages/rest/test/unit/router/routing-table.unit.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,32 @@ function runTestsWithRouter(router: RestRouter) {
188188
expect(route.pathParams).to.containEql({arg1: '3', arg2: '2'});
189189
});
190190

191+
it('finds "GET /getProfile/{userId}.{format}" endpoint', () => {
192+
const spec = anOpenApiSpec()
193+
.withOperationReturningString(
194+
'get',
195+
'/getProfile/{userId}.{format}',
196+
'getProfile',
197+
)
198+
.build();
199+
200+
spec.basePath = '/my';
201+
202+
class TestController {}
203+
204+
const table = givenRoutingTable();
205+
table.registerController(spec, TestController);
206+
207+
let request = givenRequest({
208+
method: 'get',
209+
url: '/my/getProfile/1.json',
210+
});
211+
212+
let route = table.find(request);
213+
expect(route.path).to.eql('/my/getProfile/{userId}.{format}');
214+
expect(route.pathParams).to.containEql({userId: '1', format: 'json'});
215+
});
216+
191217
it('throws if router is not found', () => {
192218
const table = givenRoutingTable();
193219

packages/rest/test/unit/router/trie.unit.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ describe('Trie', () => {
5353
key: '{id}',
5454
value: getOrderById,
5555
names: ['id'],
56-
regexp: /^(.+)$/,
56+
regexp: /^([^\/]+?)(?:\/)?$/i,
5757
children: {},
5858
},
5959
},
@@ -89,7 +89,7 @@ describe('Trie', () => {
8989
key: '{id}',
9090
value: {verb: 'get', path: '/orders/{id}'},
9191
names: ['id'],
92-
regexp: /^(.+)$/,
92+
regexp: /^([^\/]+?)(?:\/)?$/i,
9393
},
9494
]);
9595
});
@@ -104,7 +104,7 @@ describe('Trie', () => {
104104
key: '{id}',
105105
value: {verb: 'get', path: '/orders/{id}'},
106106
names: ['id'],
107-
regexp: /^(.+)$/,
107+
regexp: /^([^\/]+?)(?:\/)?$/i,
108108
children: {},
109109
},
110110
]);

0 commit comments

Comments
 (0)