Merge pull request #179 from github/joshmgross/deduplicate-webhook-objects
Deduplicate objects in webhook payloads
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import Webhook from "./webhook";
|
||||
|
||||
// Store any repeated body parameters in an array
|
||||
// and replace them in the webhook with an index in the array
|
||||
export function deduplicateWebhooks(webhooks: Record<string, Record<string, Webhook>>): any[] {
|
||||
/**
|
||||
* Build a map of all objects by name
|
||||
*/
|
||||
const objectsByName: Record<string, any[]> = {};
|
||||
const objectCount: Record<string, number> = {};
|
||||
|
||||
for (const webhook of iterateWebhooks(webhooks)) {
|
||||
for (const param of webhook.bodyParameters) {
|
||||
objectsByName[param.name] ||= [];
|
||||
const index = findOrAdd(param, objectsByName[param.name]);
|
||||
const key = `${param.name}:${index}`;
|
||||
objectCount[key] ||= 0;
|
||||
objectCount[key]++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace any duplicated body parameters with an index
|
||||
*/
|
||||
const duplicatedBodyParams: any[] = [];
|
||||
const bodyParamIndexMap: Record<string, number> = {};
|
||||
|
||||
for (const webhook of iterateWebhooks(webhooks)) {
|
||||
const newParams: any[] = [];
|
||||
for (const param of webhook.bodyParameters) {
|
||||
const index = find(param, objectsByName[param.name]);
|
||||
const key = `${param.name}:${index}`;
|
||||
if (objectCount[key] > 1) {
|
||||
newParams.push(indexForParam(param, index, bodyParamIndexMap, duplicatedBodyParams));
|
||||
} else {
|
||||
// If an object is only used once, keep it inline
|
||||
newParams.push(param);
|
||||
}
|
||||
}
|
||||
|
||||
webhook.bodyParameters = newParams;
|
||||
}
|
||||
|
||||
return duplicatedBodyParams;
|
||||
}
|
||||
|
||||
function* iterateWebhooks(webhooks: Record<string, Record<string, Webhook>>) {
|
||||
for (const webhookActions of Object.values(webhooks)) {
|
||||
for (const webhook of Object.values(webhookActions)) {
|
||||
yield webhook;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findOrAdd(param: any, objects: any[]): number {
|
||||
for (const [index, object] of objects.entries()) {
|
||||
if (JSON.stringify(object) === JSON.stringify(param)) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return objects.push(param) - 1;
|
||||
}
|
||||
|
||||
function find(param: any, objects: any[]): number {
|
||||
for (const [index, object] of objects.entries()) {
|
||||
if (JSON.stringify(object) === JSON.stringify(param)) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Could not find object ${param.name}`);
|
||||
}
|
||||
|
||||
function indexForParam(
|
||||
param: any,
|
||||
paramNameIndex: number,
|
||||
objectIndexMap: Record<string, number>,
|
||||
duplicatedBodyParams: any[]
|
||||
): number {
|
||||
const key = `${param.name}:${paramNameIndex}`;
|
||||
|
||||
const existingIndex = objectIndexMap[key];
|
||||
if (existingIndex !== undefined) {
|
||||
// Object has already been added to the array
|
||||
return existingIndex;
|
||||
}
|
||||
|
||||
// Add the object to the array
|
||||
const objIdx = duplicatedBodyParams.push(param) - 1;
|
||||
objectIndexMap[key] = objIdx;
|
||||
return objIdx;
|
||||
}
|
||||
@@ -2,9 +2,11 @@ import {promises as fs} from "fs";
|
||||
import Webhook from "./webhook.js";
|
||||
|
||||
import schemaImport from "rest-api-description/descriptions/api.github.com/dereferenced/api.github.com.deref.json" assert {type: "json"};
|
||||
import {deduplicateWebhooks} from "./deduplicate.js";
|
||||
const schema = schemaImport as any;
|
||||
|
||||
const OUTPUT_PATH = "./src/context-providers/events/webhooks.json";
|
||||
const OBJECTS_PATH = "./src/context-providers/events/objects.json";
|
||||
|
||||
const rawWebhooks = Object.values(schema.webhooks || schema["x-webhooks"]) as any[];
|
||||
if (!rawWebhooks) {
|
||||
@@ -31,4 +33,7 @@ for (const webhook of webhooks) {
|
||||
}
|
||||
}
|
||||
|
||||
const objectsArray = deduplicateWebhooks(categorizedWebhooks);
|
||||
|
||||
await fs.writeFile(OBJECTS_PATH, JSON.stringify(objectsArray, null, 2));
|
||||
await fs.writeFile(OUTPUT_PATH, JSON.stringify(categorizedWebhooks, null, 2));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {data, DescriptionDictionary} from "@github/actions-expressions";
|
||||
|
||||
import webhooks from "./webhooks.json";
|
||||
import webhookObjects from "./objects.json";
|
||||
|
||||
import schedule from "./schedule.json" assert {type: "json"};
|
||||
import workflow_call from "./workflow_call.json" assert {type: "json"};
|
||||
@@ -47,17 +48,38 @@ type Param = {
|
||||
enum?: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* A full {@link Param} or an index into the objects array for deduplicated parameters
|
||||
*/
|
||||
type DeduplicatedParam = Param | number;
|
||||
|
||||
type WebhookPayload = {
|
||||
descriptionHtml: string;
|
||||
summaryHtml: string;
|
||||
bodyParameters: Param[];
|
||||
};
|
||||
|
||||
type Webhooks = {
|
||||
[name: string]: {
|
||||
[action: string]: {
|
||||
descriptionHtml: string;
|
||||
summaryHtml: string;
|
||||
bodyParameters: Param[];
|
||||
[action: string]: WebhookPayload;
|
||||
};
|
||||
};
|
||||
|
||||
type DeduplicatedWebhooks = {
|
||||
[name: string]: {
|
||||
[action: string]: WebhookPayload & {
|
||||
bodyParameters: DeduplicatedParam[];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const webhookPayloads: Webhooks = webhooks as any;
|
||||
const dedupedWebhookPayloads: DeduplicatedWebhooks = webhooks as any;
|
||||
const objects: Param[] = webhookObjects as any;
|
||||
|
||||
const webhookPayloads: Webhooks = {};
|
||||
|
||||
// Hydrate the workflow dispatch payload if it exists
|
||||
getWebhookPayload("workflow_dispatch", "default");
|
||||
|
||||
//
|
||||
// Manual work-arounds for webhook issues
|
||||
@@ -68,7 +90,7 @@ if (inputs) {
|
||||
}
|
||||
|
||||
export function getEventPayload(event: string, action: string = "default"): DescriptionDictionary | undefined {
|
||||
const payload = webhookPayloads?.[event]?.[action];
|
||||
const payload = getWebhookPayload(event, action);
|
||||
if (!payload) {
|
||||
// Not all events are real webhooks. Check if there is a custom payload for this event
|
||||
const customPayload = customEventPayloads[event];
|
||||
@@ -120,3 +142,36 @@ function mergeObject(d: DescriptionDictionary, toAdd: Object): DescriptionDictio
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
function getWebhookPayload(event: string, action: string): WebhookPayload | undefined {
|
||||
const deduplicatedPayload = dedupedWebhookPayloads?.[event]?.[action];
|
||||
if (!deduplicatedPayload) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const existingPayload = webhookPayloads?.[event]?.[action];
|
||||
if (existingPayload) {
|
||||
return existingPayload;
|
||||
}
|
||||
|
||||
// Recreate the full payload and store it for reuse
|
||||
const params = deduplicatedPayload.bodyParameters.map(p => fullParam(p));
|
||||
const payload = {
|
||||
...deduplicatedPayload,
|
||||
bodyParameters: params
|
||||
};
|
||||
webhookPayloads[event] ||= {};
|
||||
webhookPayloads[event][action] = payload;
|
||||
return payload;
|
||||
}
|
||||
|
||||
function fullParam(dedupedParam: DeduplicatedParam): Param {
|
||||
if (typeof dedupedParam === "number") {
|
||||
if (dedupedParam >= objects.length) {
|
||||
throw new Error(`Unknown object ${dedupedParam}`);
|
||||
}
|
||||
return objects[dedupedParam];
|
||||
}
|
||||
|
||||
return dedupedParam;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user