From e042ef6f8868060d9644c68f5bed2a0d67453f5a Mon Sep 17 00:00:00 2001 From: Christopher Schleiden Date: Thu, 16 Mar 2023 16:28:42 -0700 Subject: [PATCH] Support events without default `types` Merge all payloads for the events in that case. --- .../context-providers/events/eventPayloads.ts | 37 +++++++++---- .../src/context-providers/github.test.ts | 53 +++++++++++++++++++ .../src/context-providers/github.ts | 10 ++-- 3 files changed, 86 insertions(+), 14 deletions(-) diff --git a/languageservice/src/context-providers/events/eventPayloads.ts b/languageservice/src/context-providers/events/eventPayloads.ts index 93821f5..039f0b1 100644 --- a/languageservice/src/context-providers/events/eventPayloads.ts +++ b/languageservice/src/context-providers/events/eventPayloads.ts @@ -1,7 +1,7 @@ import {data, DescriptionDictionary} from "@github/actions-expressions"; -import webhooks from "./webhooks.json"; import webhookObjects from "./objects.json"; +import webhooks from "./webhooks.json"; import schedule from "./schedule.json" assert {type: "json"}; import workflow_call from "./workflow_call.json" assert {type: "json"}; @@ -73,9 +73,12 @@ type DeduplicatedWebhooks = { }; }; +// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const dedupedWebhookPayloads: DeduplicatedWebhooks = webhooks as any; +// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const objects: Param[] = webhookObjects as any; +// Hydrated webhook payloads const webhookPayloads: Webhooks = {}; // Hydrate the workflow dispatch payload if it exists @@ -89,7 +92,18 @@ if (inputs) { delete inputs.childParamsGroups; } -export function getEventPayload(event: string, action: string = "default"): DescriptionDictionary | undefined { +export function getSupportedEventTypes(event: string): string[] { + const payloads = dedupedWebhookPayloads?.[event]; + if (!payloads) { + if (customEventPayloads[event]) { + return ["default"]; + } + } + + return Object.keys(payloads || {}); +} + +export function getEventPayload(event: string, action: string): DescriptionDictionary | undefined { const payload = getWebhookPayload(event, action); if (!payload) { // Not all events are real webhooks. Check if there is a custom payload for this event @@ -117,7 +131,7 @@ function mergeParam(target: DescriptionDictionary, param: Param) { // auto-completion. Possible existence and the description are enough. // // As a special case, if the param is already set, do not overwrite it. - if (!!target.get(param.name)) { + if (target.get(param.name)) { return; } @@ -125,16 +139,16 @@ function mergeParam(target: DescriptionDictionary, param: Param) { } } -function mergeObject(d: DescriptionDictionary, toAdd: Object): DescriptionDictionary { +function mergeObject(d: DescriptionDictionary, toAdd: object): DescriptionDictionary { for (const [key, value] of Object.entries(toAdd)) { if (value && typeof value === "object" && !d.get(key)) { - if (!Array.isArray(value) && Object.entries(value).length === 0) { + if (!Array.isArray(value) && Object.entries(value as object).length === 0) { // Allow an empty object to be any value d.add(key, new data.Null()); continue; } - d.add(key, mergeObject(new DescriptionDictionary(), value)); + d.add(key, mergeObject(new DescriptionDictionary(), value as object)); } else { d.add(key, new data.Null()); } @@ -144,16 +158,17 @@ function mergeObject(d: DescriptionDictionary, toAdd: Object): DescriptionDictio } function getWebhookPayload(event: string, action: string): WebhookPayload | undefined { - const deduplicatedPayload = dedupedWebhookPayloads?.[event]?.[action]; - if (!deduplicatedPayload) { - return undefined; - } - + // Is the payload already hydrated? const existingPayload = webhookPayloads?.[event]?.[action]; if (existingPayload) { return existingPayload; } + const deduplicatedPayload = dedupedWebhookPayloads?.[event]?.[action]; + if (!deduplicatedPayload) { + return undefined; + } + // Recreate the full payload and store it for reuse const params = deduplicatedPayload.bodyParameters.map(p => fullParam(p)); const payload = { diff --git a/languageservice/src/context-providers/github.test.ts b/languageservice/src/context-providers/github.test.ts index 929ce19..1d30e10 100644 --- a/languageservice/src/context-providers/github.test.ts +++ b/languageservice/src/context-providers/github.test.ts @@ -74,6 +74,59 @@ jobs: ]); }); + it("single event - no default types", async () => { + const workflowContext = await testGetWorkflowContext(` +on: + issues: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - run: ec|ho`); + + const g = getGithubContext(workflowContext, Mode.Completion); + if (!isDescriptionDictionary(g)) { + fail(); + } + + const e = g.get("event") as DescriptionDictionary; + const i = e.get("issue") as DescriptionDictionary; + + expect(i.pairs().map(p => p.key)).toEqual([ + "active_lock_reason", + "assignee", + "assignees", + "author_association", + "body", + "closed_at", + "comments", + "comments_url", + "created_at", + "draft", + "events_url", + "html_url", + "id", + "labels", + "labels_url", + "locked", + "milestone", + "node_id", + "number", + "performed_via_github_app", + "pull_request", + "reactions", + "repository_url", + "state", + "state_reason", + "timeline_url", + "title", + "updated_at", + "url", + "user" + ]); + }); + it("multiple events - multiple types", async () => { const workflowContext = await testGetWorkflowContext(` on: diff --git a/languageservice/src/context-providers/github.ts b/languageservice/src/context-providers/github.ts index a57aad4..05f29d1 100644 --- a/languageservice/src/context-providers/github.ts +++ b/languageservice/src/context-providers/github.ts @@ -4,7 +4,7 @@ import {TypesFilterConfig} from "@github/actions-workflow-parser/model/workflow- import {WorkflowContext} from "../context/workflow-context"; import {Mode} from "./default"; import {getDescription} from "./descriptions"; -import {getEventPayload} from "./events/eventPayloads"; +import {getEventPayload, getSupportedEventTypes} from "./events/eventPayloads"; import {getInputsContext} from "./inputs"; export function getGithubContext(workflowContext: WorkflowContext, mode: Mode): DescriptionDictionary { @@ -98,10 +98,14 @@ function getEventContext(workflowContext: WorkflowContext, mode: Mode): Expressi for (const eventName of events) { const event = eventsConfig[eventName] as TypesFilterConfig; - const types = getTypes(eventName, event.types); + let types = getTypes(eventName, event.types); + const payloadEventName = getPayloadEventName(eventName); + + if (types.length === 1 && types[0] === "default") { + types = getSupportedEventTypes(eventName); + } for (const type of types) { - const payloadEventName = getPayloadEventName(eventName); const eventPayload = getEventPayload(payloadEventName, type); if (!eventPayload) {