Skip to content

Commit 7469d8d

Browse files
authored
Merge pull request #14 from AndrewWalsh/automatic-parameterisation
feat: rules-based automatic parameterisation from pathnames
2 parents 8609dbb + dd47463 commit 7469d8d

File tree

10 files changed

+88
-8
lines changed

10 files changed

+88
-8
lines changed

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 3,
33
"name": "OpenAPI DevTools",
4-
"version": "1.5.1",
4+
"version": "1.5.2",
55
"devtools_page": "index.html",
66
"permissions": [],
77
"icons": {

package-lock.json

Lines changed: 17 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"test": "vitest",
99
"dev": "tsc && vite build && web-ext run -s dist",
1010
"build": "tsc && vite build && web-ext build --overwrite-dest -s dist",
11-
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0 --fix"
11+
"lint": "eslint --ext ts,tsx --fix ."
1212
},
1313
"dependencies": {
1414
"@chakra-ui/icons": "^2.1.1",
@@ -33,7 +33,8 @@
3333
"react-window": "^1.8.9",
3434
"redoc": "^2.1.3",
3535
"store2": "^2.14.2",
36-
"truncate-middle": "^1.0.6"
36+
"truncate-middle": "^1.0.6",
37+
"validator": "^13.11.0"
3738
},
3839
"devDependencies": {
3940
"@crxjs/vite-plugin": "^2.0.0-beta.19",
@@ -47,6 +48,7 @@
4748
"@types/react-dom": "^18.2.13",
4849
"@types/react-window": "^1.8.7",
4950
"@types/truncate-middle": "^1.0.3",
51+
"@types/validator": "^13.11.9",
5052
"@typescript-eslint/eslint-plugin": "^6.8.0",
5153
"@vitejs/plugin-react": "^4.1.0",
5254
"eslint": "^8.51.0",

src/lib/RequestStore.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,3 +250,21 @@ it("parameterisation works after export and import", () => {
250250
// @ts-expect-error accessing private property
251251
expect(store.storeOptions).toEqual(expectedOptions);
252252
});
253+
254+
it("automatically parameterises pathnames containing known path parameters such as UUIDs", () => {
255+
const store = new RequestStore();
256+
const req = createSimpleRequest(`${base}/1/2/a`);
257+
const uuid = "7be85ff3-2785-45d4-81d0-b8b58f80dfdc";
258+
const host = "http://test.com";
259+
const pathname = `/v1/${uuid}/sites/${uuid}`;
260+
const url = `${host}${pathname}`;
261+
req.request.url = url;
262+
store.insert(req, contentIntTest);
263+
// The path parameters are the UUIDs in this example
264+
const splitUrl = pathname.split("/").slice(1);
265+
splitUrl[1] = ":param1";
266+
splitUrl[3] = ":param3";
267+
const expectUrl = `/${splitUrl.join("/")}`;
268+
// @ts-expect-error accessing private property
269+
expect(store.leafMap["test.com"]).toHaveProperty(expectUrl);
270+
});

src/lib/RequestStore.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
upsert,
66
persistOptions,
77
leafMapToRouterMap,
8+
fastPathParameterIndices,
89
} from "./store-helpers";
910
import { omit, unset } from "lodash";
1011
import leafMapToEndpoints from "./leafmap-to-endpoints";
@@ -103,12 +104,21 @@ export default class RequestStore {
103104
leaf: insertedLeaf,
104105
path: insertedPath,
105106
});
107+
let pathname = insertedPath;
108+
for (const idx of fastPathParameterIndices(pathname)) {
109+
const newPathname = this.parameterise(idx, pathname, insertedHost);
110+
if (newPathname) pathname = newPathname;
111+
}
106112
return true;
107113
}
108114

109-
public parameterise(index: number, path: string, host: string): void {
115+
public parameterise(
116+
index: number,
117+
path: string,
118+
host: string
119+
): string | null {
110120
const result = parameterise({ store: this.store, index, path, host });
111-
if (!result) return;
121+
if (!result) return null;
112122
const { removedPaths, insertedPath, insertedLeaf } = result;
113123
const unsetLeafMap = (path: string) => unset(this.leafMap[host], path);
114124
removedPaths.concat([path]).forEach(unsetLeafMap);
@@ -118,6 +128,7 @@ export default class RequestStore {
118128
leaf: insertedLeaf,
119129
path: insertedPath,
120130
});
131+
return insertedPath;
121132
}
122133

123134
public setDisabledHosts(disabledHosts: Set<string>): void {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { it, expect } from "vitest";
2+
import { fastPathParameterIndices } from "./automatic-parameterisation";
3+
4+
// [what is being tested, pathname, indices that should be parameterised]
5+
it.each([["UUIDs", "/path/7be85ff3-2785-45d4-81d0-b8b58f80dfdc", [1]]])(
6+
"returns indices of path parameters that are %s in pathnames",
7+
(_, path, expected) => {
8+
const result = fastPathParameterIndices(path);
9+
expect(result).toEqual(expected);
10+
}
11+
);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import validator from "validator";
2+
3+
const validationRules = [validator.isUUID];
4+
5+
/**
6+
* Fast and low-effort rules-based parameterisation of pathnames alone
7+
* Designed to accompany a more computationally intensive analysis
8+
* Returns an array of indices of path parameters in a given pathname that matches a validation rule
9+
*/
10+
export const fastPathParameterIndices = (pathname: string): Array<number> => {
11+
const indices = [];
12+
const parts = pathname.split("/").slice(1);
13+
for (let idx = 0; idx < parts.length; idx++) {
14+
const part = parts[idx];
15+
if (validationRules.some((rule) => rule(part))) {
16+
indices.push(idx);
17+
}
18+
}
19+
return indices;
20+
};

src/lib/store-helpers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ export { default as determineAuthFromHAR } from "./authentication";
99
export { default as remove } from "./remove";
1010
export * from "./merge";
1111
export * from "./helpers";
12+
export * from "./automatic-parameterisation";

src/ui/Main.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ function Main() {
102102
(index, path, host) => {
103103
requestStore.parameterise(index, path, host);
104104
setSpecEndpoints();
105+
return null;
105106
},
106107
[]
107108
);

src/ui/context.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ const defaultContextValue: ContextType = {
2525
setAllHosts: () => {},
2626
disabledHosts: new Set(),
2727
setDisabledHosts: () => {},
28-
parameterise: () => {},
28+
parameterise: () => null,
2929
import: () => false,
30-
export: () => '',
30+
export: () => "",
3131
options: () => defaultOptions,
3232
};
3333

0 commit comments

Comments
 (0)