Add script to generate webhooks

This commit is contained in:
Josh Gross
2023-02-15 16:03:57 -05:00
parent 4957517e2d
commit baeb539fa8
8 changed files with 646 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
node_modules
dist
*.md
*.js
*.json
+9
View File
@@ -0,0 +1,9 @@
# Webhooks
Based on https://github.com/github/docs/blob/main/src/rest/README.md, this script pulls the updated OpenAPI webhook schemas from https://github.com/github/rest-api-description/blob/main/descriptions/api.github.com/dereferenced/api.github.com.deref.json and generates a `webhooks.json` file for consumption by the language service, preserving any descriptions and summaries as markdown.
## Usage
```shell
npm start
```
+288
View File
@@ -0,0 +1,288 @@
{
"name": "webhooks",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "webhooks",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"node-fetch": "^3.3.0"
},
"devDependencies": {
"@types/node-fetch": "^2.6.2"
}
},
"node_modules/@types/node": {
"version": "18.13.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz",
"integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==",
"dev": true
},
"node_modules/@types/node-fetch": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz",
"integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==",
"dev": true,
"dependencies": {
"@types/node": "*",
"form-data": "^3.0.0"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
"engines": {
"node": ">= 12"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"dev": true,
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "paypal",
"url": "https://paypal.me/jimmywarting"
}
],
"dependencies": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": "^12.20 || >= 14.13"
}
},
"node_modules/form-data": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
"dev": true,
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"dependencies": {
"fetch-blob": "^3.1.2"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz",
"integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==",
"dependencies": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/web-streams-polyfill": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==",
"engines": {
"node": ">= 8"
}
}
},
"dependencies": {
"@types/node": {
"version": "18.13.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.13.0.tgz",
"integrity": "sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==",
"dev": true
},
"@types/node-fetch": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz",
"integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==",
"dev": true,
"requires": {
"@types/node": "*",
"form-data": "^3.0.0"
}
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"requires": {
"delayed-stream": "~1.0.0"
}
},
"data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"dev": true
},
"fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"requires": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
}
},
"form-data": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
"dev": true,
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
"formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"requires": {
"fetch-blob": "^3.1.2"
}
},
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true
},
"mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"requires": {
"mime-db": "1.52.0"
}
},
"node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="
},
"node-fetch": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz",
"integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==",
"requires": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
}
},
"web-streams-polyfill": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q=="
}
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"name": "webhooks",
"version": "1.0.0",
"description": "",
"private": true,
"type": "module",
"main": "dist/index.js",
"scripts": {
"build": "tsc --build tsconfig.json",
"start": "npm run build && node dist/index.js",
"clean": "rimraf dist",
"format": "prettier --write '**/*.ts'",
"format-check": "prettier --check '**/*.ts'"
},
"dependencies": {
"node-fetch": "^3.3.0"
},
"devDependencies": {
"@types/node-fetch": "^2.6.2"
}
}
+213
View File
@@ -0,0 +1,213 @@
// Based on https://github.com/github/docs/blob/9be5ebb4855be066ac22a3ae0dafb9d615c7dfb4/src/rest/scripts/utils/get-body-params.js
// If there is a oneOf at the top level, then we have to present just one
// in the docs. We don't currently have a convention for showing more than one
// set of input parameters in the docs. Having a top-level oneOf is also very
// uncommon.
// Currently there aren't very many operations that require this treatment.
// As an example, the 'Add status check contexts' and 'Set status check contexts'
// operations have a top-level oneOf.
async function getTopLevelOneOfProperty(schema: any) {
if (!schema.oneOf) {
throw new Error("Schema does not have a requestBody oneOf property defined");
}
if (!(Array.isArray(schema.oneOf) && schema.oneOf.length > 0)) {
throw new Error("Schema requestBody oneOf property is not an array");
}
// When a oneOf exists but the `type` differs, the case has historically
// been that the alternate option is an array, where the first option
// is the array as a property of the object. We need to ensure that the
// first option listed is the most comprehensive and preferred option.
const firstOneOfObject = schema.oneOf[0];
const allOneOfAreObjects = schema.oneOf.every((elem: any) => elem.type === "object");
let required = firstOneOfObject.required || [];
let properties = firstOneOfObject.properties || {};
// When all of the oneOf objects have the `type: object` we
// need to display all of the parameters.
// This merges all of the properties and required values.
if (allOneOfAreObjects) {
for (const each of schema.oneOf.slice(1)) {
Object.assign(firstOneOfObject.properties, each.properties);
required = firstOneOfObject.required.concat(each.required);
}
properties = firstOneOfObject.properties;
}
return {properties, required};
}
// Gets the body parameters for a given schema recursively.
export async function getBodyParams(schema: any, topLevel = false): Promise<any> {
const bodyParametersParsed = [];
const schemaObject = schema.oneOf && topLevel ? await getTopLevelOneOfProperty(schema) : schema;
const properties: Record<string, any> = schemaObject.properties || {};
const required = schemaObject.required || [];
// Most operation requestBody schemas are objects. When the type is an array,
// there will not be properties on the `schema` object.
if (topLevel && schema.type === "array") {
const childParamsGroups = [];
const arrayType = schema.items.type;
const paramType = [schema.type];
if (arrayType === "object") {
childParamsGroups.push(...(await getBodyParams(schema.items, false)));
} else {
paramType.splice(paramType.indexOf("array"), 1, `array of ${arrayType}s`);
}
const paramDecorated = await getTransformedParam(schema, paramType, {
required,
topLevel,
childParamsGroups
});
return [paramDecorated];
}
for (const [paramKey, param] of Object.entries(properties)) {
// OpenAPI 3.0 only had a single value for `type`. OpenAPI 3.1
// will either be a single value or an array of values.
// This makes type an array regardless of how many values the array
// includes. This allows us to support 3.1 while remaining backwards
// compatible with 3.0.
const paramType = Array.isArray(param.type) ? param.type : [param.type];
const additionalPropertiesType = param.additionalProperties
? Array.isArray(param.additionalProperties.type)
? param.additionalProperties.type
: [param.additionalProperties.type]
: [];
const childParamsGroups = [];
// If the parameter is an array or object there may be child params
// If the parameter has oneOf or additionalProperties, they need to be
// recursively read too.
// There are a couple operations with additionalProperties, which allows
// the api to define input parameters with the type dictionary. These are the only
// two operations (at the time of adding this code) that use additionalProperties
// Create a snapshot of dependencies for a repository
// Update a gist
if (param.additionalProperties && additionalPropertiesType.includes("object")) {
const keyParam = {
type: "object",
name: "key",
description: `A user-defined key to represent an item in \`${paramKey}\`.`,
isRequired: param.required,
enum: param.enum,
default: param.default,
childParamsGroups: [] as any[]
};
keyParam.childParamsGroups.push(...(await getBodyParams(param.additionalProperties, false)));
childParamsGroups.push(keyParam);
} else if (paramType && paramType.includes("array")) {
const arrayType = param.items.type;
if (arrayType) {
paramType.splice(paramType.indexOf("array"), 1, `array of ${arrayType}s`);
}
if (arrayType === "object") {
childParamsGroups.push(...(await getBodyParams(param.items, false)));
}
} else if (paramType && paramType.includes("object")) {
childParamsGroups.push(...(await getBodyParams(param, false)));
} else if (param && param.oneOf) {
// get concatenated description and type
const descriptions = [];
for (const childParam of param.oneOf) {
paramType.push(childParam.type);
// If there is no parent description, create a description from
// each type
if (!param.description) {
if (childParam.type === "array") {
if (childParam.items.description) {
descriptions.push({
type: childParam.type,
description: childParam.items.description
});
}
} else {
if (childParam.description) {
descriptions.push({type: childParam.type, description: childParam.description});
}
}
}
}
// Occasionally, there is no parent description and the description
// is in the first child parameter.
const oneOfDescriptions = descriptions.length ? descriptions[0].description : "";
if (!param.description) param.description = oneOfDescriptions;
// This is a workaround for an operation that incorrectly defines allOf for a
// body parameter. As a workaround, we will use the first object in the list of
// the allOf array. Otherwise, fallback to the first item in the array.
// This isn't ideal, and in the case of an actual allOf occurrence, we should
// handle it differently by merging all of the properties. There is currently
// only one occurrence for the operation id repos/update-information-about-pages-site
// See Ecosystem API issue number #3332 for future plans to fix this in the OpenAPI
} else if (param && param.anyOf && Object.keys(param).length === 1) {
const firstObject = Object.values(param.anyOf).find((item: any) => item.type === "object") as {
description: string;
required: boolean;
};
if (firstObject) {
paramType.push("object");
param.description = firstObject.description;
param.isRequired = firstObject.required;
childParamsGroups.push(...(await getBodyParams(firstObject, false)));
} else {
paramType.push(param.anyOf[0].type);
param.description = param.anyOf[0].description;
param.isRequired = param.anyOf[0].required;
}
}
const paramDecorated = await getTransformedParam(param, paramType, {
paramKey,
required,
childParamsGroups,
topLevel
});
bodyParametersParsed.push(paramDecorated);
}
return bodyParametersParsed;
}
type Param = {
type: "string" | "string or null" | "number" | "boolean" | "array" | "object" | "object or null" | "integer or null";
name: string;
in: string;
isRequired: boolean;
description: string;
childParamsGroups?: Param[];
enum?: string[];
default: any;
};
async function getTransformedParam(param: any, paramType: any, props: any) {
const {paramKey, required, childParamsGroups, topLevel} = props;
const paramDecorated = {} as Param;
// Supports backwards compatibility for OpenAPI 3.0
// In 3.1 a nullable type is part of the param.type array and
// the property param.nullable does not exist.
if (param.nullable) paramType.push("null");
paramDecorated.type = paramType.filter(Boolean).join(" or ");
paramDecorated.name = paramKey;
if (topLevel) {
paramDecorated.in = "body";
}
paramDecorated.description = param.description;
if (required && required.includes(paramKey)) {
paramDecorated.isRequired = true;
}
if (childParamsGroups && childParamsGroups.length > 0) {
paramDecorated.childParamsGroups = childParamsGroups;
}
if (param.enum) {
paramDecorated.enum = param.enum;
}
// we also want to catch default values of `false` for booleans
if (param.default !== undefined) {
paramDecorated.default = param.default;
}
return paramDecorated;
}
+36
View File
@@ -0,0 +1,36 @@
import fetch from "node-fetch";
import {promises as fs} from "fs";
import Webhook from "./webhook.js";
const SCHEMA_URL =
"https://raw.githubusercontent.com/github/rest-api-description/main/descriptions/api.github.com/dereferenced/api.github.com.deref.json";
const OUTPUT_PATH = "src/webhooks.json";
const schema: any = await fetch(SCHEMA_URL).then(res => res.json());
const rawWebhooks = Object.values(schema["x-webhooks"]) as any[];
if (!rawWebhooks) {
throw new Error("No webhooks found in schema");
}
const webhooks = [];
for (const webhook of Object.values(rawWebhooks)) {
webhooks.push(new Webhook(webhook.post));
}
await Promise.all(webhooks.map(webhook => webhook.process()));
// The category is the name of the webhook
const categorizedWebhooks: Record<string, Record<string, Webhook>> = {};
for (const webhook of webhooks) {
if (!webhook.action) webhook.action = "default";
if (categorizedWebhooks[webhook.category]) {
categorizedWebhooks[webhook.category][webhook.action] = webhook;
} else {
categorizedWebhooks[webhook.category] = {};
categorizedWebhooks[webhook.category][webhook.action] = webhook;
}
}
await fs.writeFile(OUTPUT_PATH, JSON.stringify(categorizedWebhooks, null, 2));
+57
View File
@@ -0,0 +1,57 @@
// Based on https://github.com/github/docs/blob/main/src/rest/scripts/utils/webhook.js
import {getBodyParams} from "./get-body-params.js";
const NO_CHILD_PROPERTIES = ["action", "enterprise", "installation", "organization", "repository", "sender"];
export default class Webhook {
public description: string;
public summary: string;
public bodyParameters: any[];
public availability: string[];
public action: string;
public category: string;
#webhook: any;
constructor(webhook: any) {
this.#webhook = webhook;
this.description = webhook.description;
this.summary = webhook.summary;
this.bodyParameters = [];
this.availability = webhook["x-github"]["supported-webhook-types"];
// for some webhook action types (like some pull-request webhook types) the
// schema properties are under a oneOf so we try and take the action from
// the first one (the action will be the same across oneOf items)
this.action =
webhook.requestBody.content["application/json"]?.schema?.properties?.action?.enum?.[0] ||
webhook.requestBody.content["application/json"]?.schema?.oneOf?.[0]?.properties?.action?.enum?.[0] ||
null;
// The OpenAPI uses hyphens for the webhook names, but the webhooks
// are sent using underscores (e.g. `branch_protection_rule` instead
// of `branch-protection-rule`)
this.category = webhook["x-github"].subcategory.replaceAll("-", "_");
return this;
}
public async process() {
await this.renderBodyParameterDescriptions();
}
private async renderBodyParameterDescriptions() {
if (!this.#webhook.requestBody) return [];
const schema = this.#webhook.requestBody.content["application/json"]?.schema || {};
const isObject = schema !== null && typeof schema === "object";
this.bodyParameters = isObject ? await getBodyParams(schema, true) : [];
// Removes the children of the common properties
this.bodyParameters.forEach(param => {
if (NO_CHILD_PROPERTIES.includes(param.name)) {
param.childParamsGroups = [];
}
});
}
}
+17
View File
@@ -0,0 +1,17 @@
{
"include": ["src"],
"compilerOptions": {
"module": "esnext",
"target": "ES2020",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"noEmit": false,
"outDir": "./dist"
},
}