Skip to content

Commit 8609dbb

Browse files
authored
Merge pull request #13 from AndrewWalsh/refactor
refactor: RequestStore now determines valid requests
2 parents 37addbc + 8b524b3 commit 8609dbb

8 files changed

Lines changed: 89 additions & 101 deletions

File tree

LICENSE.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2023 Andrew Walsh
3+
Copyright (c) 2024 Andrew Walsh
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

src/lib/RequestStore.test.ts

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,21 @@ vi.mock("./store-helpers/persist-options", async () => {
1717
const host = "test.com";
1818
const base = `https://${host}`;
1919
const POST = "POST";
20+
const testKey = "test";
21+
const testStrObj = { [testKey]: "string" };
22+
const contentStrTest = JSON.stringify(testStrObj);
23+
const testIntObj = { [testKey]: 1 };
24+
const contentIntTest = JSON.stringify(testIntObj);
25+
const testNullObj = { [testKey]: null };
26+
const contentNullTest = JSON.stringify(testNullObj);
27+
const testBoolObj = { [testKey]: true };
28+
const contentBoolTest = JSON.stringify(testBoolObj);
2029

2130
const getResBodyJSONTypes = (
2231
store: RequestStore,
2332
host: string,
2433
path: string,
25-
propName = "foo",
34+
propName = testKey
2635
) => {
2736
const match = store.get()[host]?.lookup(path);
2837
if (!match) throw new Error("Could not match path");
@@ -37,10 +46,10 @@ it("parameterises and merges paths", () => {
3746
const req1 = createSimpleRequest(`${base}/1/2/a`);
3847
const req2 = createSimpleRequest(`${base}/1/2/b`);
3948
const req3 = createSimpleRequest(`${base}/1/2/c`);
40-
store.insert(req1, { foo: "bar" });
41-
store.insert(req2, { foo: 1 });
49+
store.insert(req1, contentStrTest);
50+
store.insert(req2, contentIntTest);
4251
store.parameterise(2, "/1/2/a", host);
43-
store.insert(req3, { foo: null });
52+
store.insert(req3, contentNullTest);
4453
store.parameterise(1, "/1/2/:param2", host);
4554
const properties = getResBodyJSONTypes(store, host, "/1/zzz/asbds");
4655
expect(properties).toContain("string");
@@ -52,7 +61,7 @@ it("parameterises and merges paths", () => {
5261
it("inserts data and can retrieve it", () => {
5362
const store = new RequestStore();
5463
const req = createSimpleRequest(`${base}/1/2/a`);
55-
store.insert(req, { foo: 1 });
64+
store.insert(req, contentIntTest);
5665
const properties = getResBodyJSONTypes(store, host, "/1/2/a");
5766
expect(properties).toBe("integer");
5867
});
@@ -65,14 +74,14 @@ it("sets leafMap correctly after multiple add and parameterise operations", () =
6574
const req4 = createSimpleRequest(`${base}/1/2/c`);
6675
const req5 = createSimpleRequest(`${base}/dynamicPath/2/a`);
6776
const req6 = createSimpleRequest(`${base}/dynamicPath/2/b`);
68-
store.insert(req1, { foo: "bar" });
69-
store.insert(req2, { foo: "bar" });
70-
store.insert(req3, { foo: 1 });
77+
store.insert(req1, contentStrTest);
78+
store.insert(req2, contentStrTest);
79+
store.insert(req3, contentIntTest);
7180
store.parameterise(2, "/1/2/a", host);
72-
store.insert(req4, { foo: null });
81+
store.insert(req4, contentNullTest);
7382
store.parameterise(1, "/1/2/:param2", host);
74-
store.insert(req5, { foo: "bar" });
75-
store.insert(req6, { foo: 1 });
83+
store.insert(req5, contentStrTest);
84+
store.insert(req6, contentIntTest);
7685
store.parameterise(2, "/dynamicPath/2/b", host);
7786
store.parameterise(1, "/dynamicPath/2/:param2", host);
7887
const expected = {
@@ -94,7 +103,7 @@ it("sets leafMap correctly after multiple add and parameterise operations", () =
94103
"string",
95104
]);
96105
expect(getResBodyJSONTypes(store, host, "/staticPath/2/3/4/5")).toBe(
97-
"string",
106+
"string"
98107
);
99108
});
100109

@@ -104,10 +113,10 @@ it("sets leafMap correctly after many parameterise operations", () => {
104113
const req2 = createSimpleRequest(`${base}/1/2/3/z/b`);
105114
const req3 = createSimpleRequest(`${base}/1/x/y/z/b`);
106115
const req4 = createSimpleRequest(`${base}/1/2/b`);
107-
store.insert(req1, { foo: "bar" });
108-
store.insert(req2, { foo: null });
109-
store.insert(req3, { foo: 1 });
110-
store.insert(req4, { foo: true });
116+
store.insert(req1, contentStrTest);
117+
store.insert(req2, contentNullTest);
118+
store.insert(req3, contentIntTest);
119+
store.insert(req4, contentBoolTest);
111120
store.parameterise(4, "/1/2/3/4/a", host);
112121
store.parameterise(3, "/1/x/y/z/b", host);
113122
store.parameterise(3, "/1/2/3/4/:param4", host);
@@ -133,9 +142,9 @@ it("collapses into a single route when paramaterised", () => {
133142
const req1 = createSimpleRequest(`${base}/1/2/3/4/a`);
134143
const req2 = createSimpleRequest(`${base}/1/2/3/4/b`);
135144
const req3 = createSimpleRequest(`${base}/1/2/3/4/c`);
136-
store.insert(req1, { foo: "bar" });
137-
store.insert(req2, { foo: null });
138-
store.insert(req3, { foo: 1 });
145+
store.insert(req1, contentStrTest);
146+
store.insert(req2, contentNullTest);
147+
store.insert(req3, contentIntTest);
139148
store.parameterise(3, "/1/2/3/4/a", host);
140149
store.parameterise(4, "/1/2/3/:param3/a", host);
141150
const expected = {
@@ -156,8 +165,8 @@ it("can parameterise paths that are subsets of another path", () => {
156165
const store = new RequestStore();
157166
const req1 = createSimpleRequest(`${base}/1/2/a`);
158167
const req2 = createSimpleRequest(`${base}/1/2`);
159-
store.insert(req1, { foo: "bar" });
160-
store.insert(req2, { foo: 1 });
168+
store.insert(req1, contentStrTest);
169+
store.insert(req2, contentIntTest);
161170
store.parameterise(1, "/1/2", host);
162171
const expected = {
163172
[host]: {
@@ -177,10 +186,10 @@ it("can parameterise paths that exist along the same segment", () => {
177186
const req2 = createSimpleRequest(`${base}/1/2`);
178187
const req3 = createSimpleRequest(`${base}/1`);
179188
const req4 = createSimpleRequest(`${base}/1/2/3/4`);
180-
store.insert(req1, { foo: "bar" });
181-
store.insert(req2, { foo: 1 });
182-
store.insert(req3, { foo: null });
183-
store.insert(req4, { foo: null });
189+
store.insert(req1, contentStrTest);
190+
store.insert(req2, contentIntTest);
191+
store.insert(req3, contentNullTest);
192+
store.insert(req4, contentNullTest);
184193
store.parameterise(1, "/1/2/a", host);
185194
// Bug happens below. When /1/2 is parameterised, router.remove removes /1/2/3/4
186195
store.parameterise(1, "/1/2", host);
@@ -202,11 +211,11 @@ it("parameterising a path catches future requests to the same path", () => {
202211
const store = new RequestStore();
203212
const req1 = createSimpleRequest(`${base}/1/2/a`);
204213
const req2 = createSimpleRequest(`${base}/1/2/b`);
205-
store.insert(req1, { foo: "bar" });
206-
store.insert(req2, { foo: "bar" });
214+
store.insert(req1, contentStrTest);
215+
store.insert(req2, contentStrTest);
207216
store.parameterise(1, "/1/2/a", host);
208-
store.insert(req1, { foo: 1 });
209-
store.insert(req2, { foo: 1 });
217+
store.insert(req1, contentIntTest);
218+
store.insert(req2, contentIntTest);
210219
const expected = {
211220
[host]: {
212221
"/1/:param1/a": expect.any(Object),
@@ -222,12 +231,12 @@ it("parameterisation works after export and import", () => {
222231
const req = createSimpleRequest(`${base}/1/2/a`);
223232
const options = { enableMoreInfo: true };
224233
store.options(options);
225-
store.insert(req, { foo: 1 });
234+
store.insert(req, contentIntTest);
226235
store.parameterise(2, "/1/2/a", host);
227236
const exported = store.export();
228237
store.clear();
229238
store.import(exported);
230-
store.insert(req, { foo: 1 });
239+
store.insert(req, contentIntTest);
231240
const expectedLeafMap = {
232241
[host]: {
233242
"/1/2/:param2": expect.any(Object),

src/lib/RequestStore.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import {
99
import { omit, unset } from "lodash";
1010
import leafMapToEndpoints from "./leafmap-to-endpoints";
1111
import stringify from "json-stable-stringify";
12-
import type { Entry } from 'har-format';
12+
import type { Entry } from "har-format";
13+
import decodeUriComponent from "decode-uri-component";
14+
import { isValidRequest, parseJSON } from "../utils/helpers";
1315

1416
export type Options = {
1517
// Includes additional data such as response samples
@@ -27,8 +29,8 @@ export default class RequestStore {
2729
private storeOptions: Options;
2830

2931
constructor(storeOptions = persistOptions.get()) {
30-
this.leafMap = {}; // persist.get() || {};
31-
this.store = {}; // leafMapToRouterMap(this.leafMap);
32+
this.leafMap = {};
33+
this.store = {};
3234
this.disabledHosts = new Set();
3335
this.storeOptions = storeOptions;
3436
}
@@ -63,7 +65,6 @@ export default class RequestStore {
6365
this.store = {};
6466
this.leafMap = {};
6567
this.disabledHosts = new Set();
66-
// persist.clear();
6768
}
6869

6970
public endpoints(): Array<Endpoint> {
@@ -84,25 +85,25 @@ export default class RequestStore {
8485
return Object.keys(this.store);
8586
}
8687

87-
public insert(
88-
harRequest: Entry,
89-
responseBody: JSONType
90-
) {
88+
public insert(harRequest: Entry, content: string): boolean {
89+
if (!isValidRequest(harRequest, content)) return false;
90+
harRequest.request.url = decodeUriComponent(harRequest.request.url);
91+
const responseBody: JSONType = parseJSON(content);
9192
const result = upsert({
9293
harRequest,
9394
responseBody,
9495
store: this.store,
9596
options: this.storeOptions,
9697
});
97-
if (!result) return;
98+
if (!result) return false;
9899
const { insertedPath, insertedLeaf, insertedHost } = result;
99100
insertLeafMap({
100101
leafMap: this.leafMap,
101102
host: insertedHost,
102103
leaf: insertedLeaf,
103104
path: insertedPath,
104105
});
105-
// persist.set(this.leafMap);
106+
return true;
106107
}
107108

108109
public parameterise(index: number, path: string, host: string): void {
@@ -117,7 +118,6 @@ export default class RequestStore {
117118
leaf: insertedLeaf,
118119
path: insertedPath,
119120
});
120-
// persist.set(this.leafMap);
121121
}
122122

123123
public setDisabledHosts(disabledHosts: Set<string>): void {

src/lib/endpoints-to-oai31.test.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,17 @@ import RequestStore, { Options } from "./RequestStore.js";
1313
import { defaultOptions } from "./store-helpers/persist-options.js";
1414
import { AuthType } from "../utils/types.js";
1515

16+
const testStrObj = { test: "string" };
17+
const contentStrTest = JSON.stringify(testStrObj);
18+
1619
const createRequestStoreWithDefaults = () => {
1720
const store = new RequestStore();
1821
const req1 = cloneDeep(postJson);
1922
req1.request.url = "https://example.com/api/v1/1a";
2023
const req2 = cloneDeep(postJson);
2124
req2.request.url = "https://example.com/api/v1/2 . ";
22-
store.insert(req1, { test: "string" });
23-
store.insert(req2, { test: "string" });
25+
store.insert(req1, contentStrTest);
26+
store.insert(req2, contentStrTest);
2427
return store;
2528
};
2629

@@ -38,8 +41,8 @@ it("handles multiple status codes in responses", () => {
3841
const req1 = cloneDeep(postJson);
3942
const req2 = cloneDeep(postJson);
4043
req2.response.status = 304;
41-
store.insert(req1, { test: "string" });
42-
store.insert(req2, { test: "string" });
44+
store.insert(req1, contentStrTest);
45+
store.insert(req2, contentStrTest);
4346
const endpoints = store.endpoints();
4447
const oai31 = endpointsToOAI31(endpoints, options);
4548
const result = oai31.rootDoc.paths?.["/v1/track"].post?.responses;
@@ -50,7 +53,7 @@ it("handles multiple status codes in responses", () => {
5053
it("sets most recent request when enabled", () => {
5154
const options: Options = { enableMoreInfo: true };
5255
const store = new RequestStore(options);
53-
store.insert(postJson, { test: "string" });
56+
store.insert(postJson, contentStrTest);
5457
const endpoints = store.endpoints();
5558
const oai31 = endpointsToOAI31(endpoints, options);
5659
const result =
@@ -64,19 +67,19 @@ it("sets most recent request when enabled", () => {
6467
it("sets most recent response when enabled", () => {
6568
const options: Options = { enableMoreInfo: true };
6669
const store = new RequestStore(options);
67-
store.insert(postJson, { test: "string" });
70+
store.insert(postJson, contentStrTest);
6871
const endpoints = store.endpoints();
6972
const oai31 = endpointsToOAI31(endpoints, options);
7073
const result =
7174
oai31.rootDoc.paths?.["/v1/track"].post?.responses["200"].content[
7275
"application/json"
7376
].example;
74-
expect(result).toEqual({ test: "string" });
77+
expect(result).toEqual(testStrObj);
7578
});
7679

7780
it("sets bearer auth security schema when available", () => {
7881
const store = new RequestStore();
79-
store.insert(bearer, { test: "string" });
82+
store.insert(bearer, contentStrTest);
8083
const endpoints = store.endpoints();
8184
const oai31 = endpointsToOAI31(endpoints, defaultOptions);
8285
expect(oai31.rootDoc.components?.securitySchemes).toEqual({
@@ -95,7 +98,7 @@ it("sets bearer auth security schema when available", () => {
9598

9699
it("sets basic auth security schema when available", () => {
97100
const store = new RequestStore();
98-
store.insert(basic, { test: "string" });
101+
store.insert(basic, contentStrTest);
99102
const endpoints = store.endpoints();
100103
const oai31 = endpointsToOAI31(endpoints, defaultOptions);
101104
expect(oai31.rootDoc.components?.securitySchemes).toEqual({
@@ -114,7 +117,7 @@ it("sets basic auth security schema when available", () => {
114117

115118
it("sets digest auth security schema when available", () => {
116119
const store = new RequestStore();
117-
store.insert(digest, { test: "string" });
120+
store.insert(digest, contentStrTest);
118121
const endpoints = store.endpoints();
119122
const oai31 = endpointsToOAI31(endpoints, defaultOptions);
120123
expect(oai31.rootDoc.components?.securitySchemes).toEqual({
@@ -133,7 +136,7 @@ it("sets digest auth security schema when available", () => {
133136

134137
it("sets api keys from headers", () => {
135138
const store = new RequestStore();
136-
store.insert(apikey, { test: "string" });
139+
store.insert(apikey, contentStrTest);
137140
const endpoints = store.endpoints();
138141
const oai31 = endpointsToOAI31(endpoints, defaultOptions);
139142
expect(oai31.rootDoc.components?.securitySchemes).toEqual({

src/lib/store-helpers/authentication-http.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,7 @@ export interface DigestAuthHeader extends HTTPAuth {
2424
in: "header";
2525
}
2626

27-
export type HTTP =
28-
| DigestAuthHeader
29-
| BasicAuthHeader
30-
| BearerAuthHeader;
27+
export type HTTP = DigestAuthHeader | BasicAuthHeader | BearerAuthHeader;
3128

3229
export const parseHTTPAuthHeader = (value: string): HTTP | undefined => {
3330
const authType = getAuthType(value);

src/lib/store-helpers/persist-store.ts

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)