-
Notifications
You must be signed in to change notification settings - Fork 38
Expand file tree
/
Copy pathcommon.utils.ts
More file actions
87 lines (78 loc) · 3 KB
/
common.utils.ts
File metadata and controls
87 lines (78 loc) · 3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import { inspect } from 'util';
import { Path } from 'path-parser';
import url from 'url';
export const stringify = (obj) => inspect(obj, { depth: null });
// excludes the query because path = pathname + query
export const getPathname = (request) => url.parse(request.path).pathname;
// converts all {foo} to :foo
const convertOpenApiPathToColonForm = (openApiPath) =>
openApiPath.replace(/{/g, ':').replace(/}/g, '');
const doesColonPathMatchPathname = (pathInColonForm, pathname) => {
const pathParamsInPathname = new Path(pathInColonForm).test(pathname); // => one of: null, {}, {exampleParam: 'foo'}
return Boolean(pathParamsInPathname);
};
const doesOpenApiPathMatchPathname = (openApiPath, pathname) => {
const pathInColonForm = convertOpenApiPathToColonForm(openApiPath);
return doesColonPathMatchPathname(pathInColonForm, pathname);
};
const compareOpenApiPathsByGenericity = (openApiPath1, openApiPath2) => {
// genericity is a based on the idea that a path with a template parameters is more generic
// than a path without any template parameter
// simple examples:
// "/a" == "/b"
// "/{foo}" == "/{bar}"
// "/{foo}" > "/a"
// "/a" < "/{foo}"
// examples with templated prefix:
// "/{hello}/a" == "/{bye}/b"
// "/{hello}/{foo}" == "/{bye}/{bar}"
// "/{hello}/{foo}" > "/{bye}/a"
// "/{hello}/a" < "/{bye}/{foo}"
// examples with hardcoded prefix:
// "/hello/a" == "/bye/b"
// "/hello/{foo}" == "/bye/{bar}"
// "/hello/{foo}" > "/bye/a"
// "/hello/a" < "/bye/{foo}"
const pathElements1 = openApiPath1.substring(1).split(/\//);
const pathElements2 = openApiPath2.substring(1).split(/\//);
for (let i = 0; i < pathElements1.length && i < pathElements2.length; i++) {
const isTemplateElement1 = pathElements1[i][0] == '{';
const isTemplateElement2 = pathElements2[i][0] == '{';
if (isTemplateElement1 && !isTemplateElement2) {
return 1;
} else if (!isTemplateElement1 && isTemplateElement2) {
return -1;
}
}
// returning 0 is valid because this function is called with paths of the same length,
// so we don't have to compare "/{foo}/a" and "/{bar}" for instance.
return 0;
};
export const findOpenApiPathMatchingPossiblePathnames = (
possiblePathnames,
OAPaths,
) => {
let openApiPath;
// eslint-disable-next-line no-restricted-syntax
for (const pathname of possiblePathnames) {
// eslint-disable-next-line no-restricted-syntax
for (const OAPath of OAPaths) {
if (OAPath === pathname) {
return OAPath;
}
if (doesOpenApiPathMatchPathname(OAPath, pathname)) {
// favor OAPath if it is least generic than openApiPath
if (
!openApiPath ||
compareOpenApiPathsByGenericity(OAPath, openApiPath) < 0
) {
openApiPath = OAPath;
}
}
}
}
return openApiPath;
};
export const defaultBasePath = '/';
export const getPathnameWithoutBasePath = (basePath, pathname) =>
basePath === defaultBasePath ? pathname : pathname.replace(basePath, '');