Skip to content

Commit 1168fa6

Browse files
authored
feat(types): use OpenAPI spec (#841)
1 parent d51daf8 commit 1168fa6

13 files changed

+1089
-172
lines changed

README.md

+73-65
Large diffs are not rendered by default.

package-lock.json

+13-20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-3
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,16 @@
4242
"dependencies": {
4343
"@octokit/request-error": "^5.0.0",
4444
"@octokit/webhooks-methods": "^4.0.0",
45-
"@octokit/webhooks-types": "7.3.2",
45+
"@wolfy1339/openapi-webhooks-types": "5.1.3",
4646
"aggregate-error": "^3.1.0"
4747
},
4848
"devDependencies": {
4949
"@jest/types": "^29.0.0",
5050
"@octokit/tsconfig": "^2.0.0",
51-
"@octokit/webhooks-schemas": "7.3.2",
5251
"@types/jest": "^29.0.0",
53-
"@types/json-schema": "^7.0.7",
5452
"@types/node": "^20.0.0",
5553
"@types/prettier": "^2.0.0",
54+
"@wolfy1339/openapi-webhooks": "5.1.3",
5655
"axios": "^1.0.0",
5756
"esbuild": "^0.20.0",
5857
"express": "^4.17.1",

scripts/generate-types.ts

100755100644
+41-66
Original file line numberDiff line numberDiff line change
@@ -2,76 +2,34 @@
22

33
import { strict as assert } from "node:assert";
44
import * as fs from "node:fs";
5-
import type { JSONSchema7, JSONSchema7Definition } from "json-schema";
5+
import type { OpenAPI3, OperationObject, PathItemObject } from "./types.js";
66
import { format } from "prettier";
77

8-
type JSONSchemaWithRef = JSONSchema7 & Required<Pick<JSONSchema7, "$ref">>;
9-
10-
interface Schema extends JSONSchema7 {
11-
definitions: Record<string, JSONSchema7>;
12-
oneOf: JSONSchemaWithRef[];
13-
}
14-
15-
const schema = require("@octokit/webhooks-schemas") as Schema;
16-
17-
const guessAtEventName = (name: string) => {
18-
const [, eventName] = /^(.+)[$_-]event/u.exec(name) ?? [];
19-
20-
assert.ok(eventName, `unable to guess event name for "${name}"`);
21-
22-
return eventName;
23-
};
24-
const guessAtActionName = (name: string) => name.replace("$", ".");
25-
26-
const getDefinitionName = (ref: string): string => {
27-
assert.ok(
28-
ref.startsWith("#/definitions/"),
29-
`${ref} does not reference a valid definition`,
30-
);
31-
32-
const [, name] = /^#\/definitions\/(.+)$/u.exec(ref) ?? [];
33-
34-
assert.ok(name, `unable to find definition name ${ref}`);
35-
36-
return name;
37-
};
38-
39-
type NameAndActions = [name: string, actions: string[]];
40-
41-
const buildEventProperties = ([
42-
eventName,
43-
actions,
44-
]: NameAndActions): string[] => [
45-
guessAtEventName(eventName),
46-
...actions.map(guessAtActionName),
47-
];
48-
49-
const isJSONSchemaWithRef = (
50-
object: JSONSchema7Definition,
51-
): object is JSONSchemaWithRef =>
52-
typeof object === "object" && object.$ref !== undefined;
53-
54-
const listEvents = () => {
55-
return schema.oneOf.map<NameAndActions>(({ $ref }) => {
56-
const name = getDefinitionName($ref);
57-
const definition = schema.definitions[name];
58-
59-
assert.ok(definition, `unable to find definition named ${name}`);
60-
61-
if (definition.oneOf?.every(isJSONSchemaWithRef)) {
62-
return [name, definition.oneOf.map((def) => getDefinitionName(def.$ref))];
63-
}
64-
65-
return [name, []];
66-
});
67-
};
8+
const schema = require("@wolfy1339/openapi-webhooks").schemas[
9+
"api.github.com"
10+
] as OpenAPI3;
6811

6912
const getEmitterEvents = (): string[] => {
70-
return listEvents().reduce<string[]>(
71-
(properties, event) => properties.concat(buildEventProperties(event)),
72-
[],
73-
);
13+
return Array.from(events).sort();
7414
};
15+
const eventsMap: Record<string, Set<string>> = {};
16+
const events = new Set<string>();
17+
18+
for (let webhookDefinitionKey of Object.keys(schema.webhooks!)) {
19+
const webhookDefinition = schema.webhooks![
20+
webhookDefinitionKey
21+
] as PathItemObject;
22+
const operationDefinition = webhookDefinition.post as OperationObject;
23+
const emitterEventName = operationDefinition
24+
.operationId!.replace(/-/g, "_")
25+
.replace("/", ".");
26+
27+
const [eventName] = emitterEventName.split(".");
28+
events.add(eventName);
29+
events.add(emitterEventName);
30+
eventsMap[eventName] ||= new Set<string>();
31+
eventsMap[eventName].add(webhookDefinitionKey);
32+
}
7533

7634
const outDir = "src/generated/";
7735

@@ -84,7 +42,7 @@ const generateTypeScriptFile = async (name: string, contents: string[]) => {
8442

8543
const asCode = (str: string): string => `\`${str}\``;
8644
const asLink = (event: string): string => {
87-
const link = `https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#${event.replace(
45+
const link = `https://docs.github.com/en/webhooks-and-events/webhook-events-and-payloads#${event.replace(
8846
/[^a-z_0-9]/g,
8947
"",
9048
)}`;
@@ -153,6 +111,23 @@ const run = async () => {
153111
"] as const;",
154112
]);
155113

114+
await generateTypeScriptFile("webhook-identifiers", [
115+
"// THIS FILE IS GENERATED - DO NOT EDIT DIRECTLY",
116+
"// make edits in scripts/generate-types.ts",
117+
"",
118+
"import type { WebhookEventDefinition } from '../types.js';",
119+
"",
120+
"export type EventPayloadMap = {",
121+
...Object.keys(eventsMap).map(
122+
(key) =>
123+
`"${key}": ${Array.from(eventsMap[key])
124+
.map((event) => `WebhookEventDefinition<` + `"${event}">`)
125+
.join(" | ")}`,
126+
),
127+
"}",
128+
"export type WebhookEventName = keyof EventPayloadMap;",
129+
]);
130+
156131
await updateReadme(emitterEvents);
157132
};
158133

0 commit comments

Comments
 (0)