Skip to content

Commit 98c2c9c

Browse files
committed
v1.3.0
- feat: api keys in auth headers are now parsed and will be associated with endpoints - feat: api keys in cookies are also parsed, keys that look like they relate to auth will be associated with endpoints - feat: new Actions menu that presents opportunities to users. Currently includes guidance on how to use the app to generate code for 20+ languages easily. This can be achieved by pasting generated schemas into https://app.quicktype.io/ - feat: long parts in pathnames are now truncated in the middle. Hovering on the part shows a tooltip of the full name. This should resolve UI issues with long pathnames - fix: the import modal is now cleared after import - general: code cleanup in some areas, and added comments to clarify core types that power the algorithm - general: improved some tooltips & clarity in text content
1 parent d350fcb commit 98c2c9c

28 files changed

Lines changed: 586 additions & 152 deletions

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.2.4",
4+
"version": "1.3.0",
55
"devtools_page": "index.html",
66
"permissions": [],
77
"icons": {

package-lock.json

Lines changed: 30 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: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@
88
"test": "vitest",
99
"dev": "vite",
1010
"build": "rimraf dist && tsc && vite build && npm run zip",
11-
"zip": "rimraf resources/dist.zip && zip -r resources/dist.zip dist",
11+
"zip": "rimraf resources/dist.zip && zip -rj resources/dist.zip dist/*",
1212
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0 --fix"
1313
},
1414
"dependencies": {
1515
"@chakra-ui/icons": "^2.1.1",
1616
"@chakra-ui/react": "^2.8.1",
1717
"@emotion/react": "^11.11.1",
1818
"@emotion/styled": "^11.11.0",
19+
"cookie": "^0.6.0",
1920
"copy-to-clipboard": "^3.3.3",
2021
"decode-uri-component": "^0.4.1",
2122
"framer-motion": "^10.16.4",
@@ -31,17 +32,20 @@
3132
"react-virtualized-auto-sizer": "^1.0.20",
3233
"react-window": "^1.8.9",
3334
"redoc": "^2.1.3",
34-
"store2": "^2.14.2"
35+
"store2": "^2.14.2",
36+
"truncate-middle": "^1.0.6"
3537
},
3638
"devDependencies": {
3739
"@crxjs/vite-plugin": "^2.0.0-beta.19",
3840
"@seriousme/openapi-schema-validator": "^2.1.2",
3941
"@types/chrome": "^0.0.246",
42+
"@types/cookie": "^0.5.4",
4043
"@types/json-stable-stringify": "^1.0.35",
4144
"@types/lodash": "^4.14.199",
4245
"@types/react": "^18.2.28",
4346
"@types/react-dom": "^18.2.13",
4447
"@types/react-window": "^1.8.7",
48+
"@types/truncate-middle": "^1.0.3",
4549
"@typescript-eslint/eslint-plugin": "^6.8.0",
4650
"@vitejs/plugin-react": "^4.1.0",
4751
"eslint": "^8.51.0",

resources/dist.zip

32.5 KB
Binary file not shown.

src/App.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
import { ChakraProvider } from "@chakra-ui/react";
22
import Main from "./ui/Main";
3+
import { closeButtonTheme } from "./ui/ControlDynamicDelete";
4+
import { extendTheme } from "@chakra-ui/react";
5+
6+
const theme = extendTheme({
7+
components: { CloseButton: closeButtonTheme },
8+
});
39

410
function App() {
511
return (
6-
<ChakraProvider>
12+
<ChakraProvider theme={theme}>
713
<Main />
814
</ChakraProvider>
915
);

src/lib/RequestStore.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ it("parameterisation works after export and import", () => {
178178
store.insert(req, { foo: 1 });
179179
store.parameterise(2, "/1/2/a", host);
180180
const exported = store.export();
181+
store.clear();
181182
store.import(exported);
182183
store.insert(req, { foo: 1 });
183184
const expected = {

src/lib/RequestStore.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export default class RequestStore {
5555
return stringify({
5656
leafMap: this.leafMap,
5757
disabledHosts: Array.from(this.disabledHosts),
58-
});
58+
}).trim();
5959
};
6060

6161
public clear(): void {

src/lib/__fixtures__/apikey.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Has header x-api-key
2+
// Has cookie foo
3+
const apikey: chrome.devtools.network.Request = {
4+
getContent() {},
5+
_priority: "High",
6+
_resourceType: "fetch",
7+
cache: {},
8+
connection: "14048",
9+
request: {
10+
method: "GET",
11+
url: "https://httpbin.org/basic-auth/d/d",
12+
httpVersion: "http/2.0",
13+
headers: [
14+
{
15+
name: "x-api-key",
16+
value: "123",
17+
},
18+
{
19+
name: "cookie",
20+
value: "foo=bar;sessionid=123",
21+
},
22+
],
23+
queryString: [],
24+
cookies: [],
25+
headersSize: -1,
26+
bodySize: 0,
27+
},
28+
response: {
29+
status: 200,
30+
statusText: "OK",
31+
httpVersion: "http/2.0",
32+
headers: [
33+
{
34+
name: "access-control-allow-credentials",
35+
value: "true",
36+
},
37+
{
38+
name: "access-control-allow-origin",
39+
value: "*",
40+
},
41+
{
42+
name: "content-length",
43+
value: "44",
44+
},
45+
{
46+
name: "content-type",
47+
value: "application/json",
48+
},
49+
{
50+
name: "date",
51+
value: "Mon, 30 Oct 2023 07:51:43 GMT",
52+
},
53+
{
54+
name: "server",
55+
value: "gunicorn/19.9.0",
56+
},
57+
],
58+
cookies: [],
59+
content: {
60+
size: 44,
61+
mimeType: "application/json",
62+
},
63+
redirectURL: "",
64+
headersSize: -1,
65+
bodySize: -1,
66+
_transferSize: 184,
67+
},
68+
serverIPAddress: "54.83.187.171",
69+
startedDateTime: "2023-10-30T07:51:43.115Z",
70+
time: 428.73000000250715,
71+
timings: {
72+
blocked: 123.52900002598763,
73+
dns: 0.012999999999991019,
74+
ssl: 104.29100000000003,
75+
connect: 204.75600000000003,
76+
send: 0.6009999999999991,
77+
wait: 98.76400000092758,
78+
receive: 1.066999975591898,
79+
},
80+
};
81+
82+
export default apikey;

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

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,26 @@ import { Authentication, AuthType } from "../utils/types";
1616

1717
export const createSecuritySchemeTypes = (auth?: Authentication): SecuritySchemeObject | undefined => {
1818
if (!auth) return;
19-
const isBearer = auth.id === AuthType.BEARER;
20-
const isBasic = auth.id === AuthType.BASIC;
21-
const isDigest = auth.id === AuthType.DIGEST;
19+
const isBearer = auth.authType === AuthType.HTTP_HEADER_BEARER;
20+
const isBasic = auth.authType === AuthType.HTTP_HEADER_BASIC;
21+
const isDigest = auth.authType === AuthType.HTTP_HEADER_DIGEST;
2222
if (isBearer || isBasic || isDigest) {
23-
const securitySchemeObject: SecuritySchemeObject = {
23+
const httpAuth: SecuritySchemeObject = {
2424
type: auth.type,
2525
in: auth.in,
2626
scheme: auth.scheme,
2727
};
28-
return securitySchemeObject;
28+
return httpAuth;
29+
}
30+
const isAPIKeyHeader = auth.authType.startsWith(AuthType.APIKEY_HEADER_);
31+
const isAPIKeyCookie = auth.authType.startsWith(AuthType.APIKEY_COOKIE_);
32+
if (isAPIKeyHeader || isAPIKeyCookie) {
33+
const apiKeyHeader: SecuritySchemeObject = {
34+
type: auth.type,
35+
in: auth.in,
36+
name: auth.name,
37+
};
38+
return apiKeyHeader;
2939
}
3040
}
3141

@@ -136,3 +146,9 @@ export const createQueryParameterTypes = (
136146
});
137147
};
138148

149+
// Format THIS_TXT_STR to this text str
150+
export const formatAuthType = (str: string) => {
151+
return str
152+
.replace(/_/g, " ")
153+
.toLowerCase();
154+
};

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

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import post from "./__fixtures__/post";
66
import bearer from "./__fixtures__/bearer";
77
import basic from "./__fixtures__/basic";
88
import digest from "./__fixtures__/digest";
9+
import apikey from "./__fixtures__/apikey";
910
import { cloneDeep } from "lodash";
1011
import { AuthType } from "../utils/types";
1112
import { defaultOptions } from "./store-helpers/persist-options";
13+
import { formatAuthType } from './endpoints-to-oai31.helpers';
1214

1315
const createRequestStoreWithDefaults = () => {
1416
const store = new RequestStore();
@@ -36,8 +38,11 @@ it("sets most recent request when enabled", () => {
3638
store.insert(post, { test: "string" });
3739
const endpoints = store.endpoints();
3840
const oai31 = endpointsToOAI31(endpoints, options);
39-
// @ts-expect-error ignored
40-
const result = oai31.rootDoc.paths?.['/v1/track'].post?.requestBody?.content['application/json'].example;
41+
const result =
42+
// @ts-expect-error ignored
43+
oai31.rootDoc.paths?.["/v1/track"].post?.requestBody?.content[
44+
"application/json"
45+
].example;
4146
expect(result).toEqual({ test: "integer" });
4247
});
4348

@@ -47,7 +52,10 @@ it("sets most recent response when enabled", () => {
4752
store.insert(post, { test: "string" });
4853
const endpoints = store.endpoints();
4954
const oai31 = endpointsToOAI31(endpoints, options);
50-
const result = oai31.rootDoc.paths?.['/v1/track'].post?.responses['200'].content['application/json'].example;
55+
const result =
56+
oai31.rootDoc.paths?.["/v1/track"].post?.responses["200"].content[
57+
"application/json"
58+
].example;
5159
expect(result).toEqual({ test: "string" });
5260
});
5361

@@ -57,7 +65,12 @@ it("sets bearer auth security schema when available", () => {
5765
const endpoints = store.endpoints();
5866
const oai31 = endpointsToOAI31(endpoints, defaultOptions);
5967
expect(oai31.rootDoc.components?.securitySchemes).toEqual({
60-
[AuthType.BEARER]: {
68+
[formatAuthType(AuthType.APIKEY_HEADER_ + 'AUTHORIZATION')]: {
69+
in: "header",
70+
name: "AUTHORIZATION",
71+
type: "apiKey",
72+
},
73+
[formatAuthType(AuthType.HTTP_HEADER_BEARER)]: {
6174
in: "header",
6275
scheme: "Bearer",
6376
type: "http",
@@ -71,7 +84,12 @@ it("sets basic auth security schema when available", () => {
7184
const endpoints = store.endpoints();
7285
const oai31 = endpointsToOAI31(endpoints, defaultOptions);
7386
expect(oai31.rootDoc.components?.securitySchemes).toEqual({
74-
[AuthType.BASIC]: {
87+
[formatAuthType(AuthType.APIKEY_HEADER_ + 'AUTHORIZATION')]: {
88+
in: "header",
89+
name: "AUTHORIZATION",
90+
type: "apiKey",
91+
},
92+
[formatAuthType(AuthType.HTTP_HEADER_BASIC)]: {
7593
in: "header",
7694
scheme: "Basic",
7795
type: "http",
@@ -85,10 +103,39 @@ it("sets digest auth security schema when available", () => {
85103
const endpoints = store.endpoints();
86104
const oai31 = endpointsToOAI31(endpoints, defaultOptions);
87105
expect(oai31.rootDoc.components?.securitySchemes).toEqual({
88-
[AuthType.DIGEST]: {
106+
[formatAuthType(AuthType.APIKEY_HEADER_ + 'AUTHORIZATION')]: {
107+
in: "header",
108+
name: "AUTHORIZATION",
109+
type: "apiKey",
110+
},
111+
[formatAuthType(AuthType.HTTP_HEADER_DIGEST)]: {
89112
in: "header",
90113
scheme: "Digest",
91114
type: "http",
92115
},
93116
});
94117
});
118+
119+
it("sets api keys from headers", () => {
120+
const store = new RequestStore();
121+
store.insert(apikey, { test: "string" });
122+
const endpoints = store.endpoints();
123+
const oai31 = endpointsToOAI31(endpoints, defaultOptions);
124+
expect(oai31.rootDoc.components?.securitySchemes).toEqual({
125+
[formatAuthType(AuthType.APIKEY_COOKIE_ + "SESSIONID")]: {
126+
in: "cookie",
127+
name: "sessionid",
128+
type: "apiKey",
129+
},
130+
[formatAuthType(AuthType.APIKEY_HEADER_ + "COOKIE")]: {
131+
in: "header",
132+
name: "COOKIE",
133+
type: "apiKey",
134+
},
135+
[formatAuthType(AuthType.APIKEY_HEADER_ + "X-API-KEY")]: {
136+
in: "header",
137+
name: "X-API-KEY",
138+
type: "apiKey",
139+
},
140+
});
141+
});

0 commit comments

Comments
 (0)