From 95c27b143b3af1261832854b0798b6b89f28738b Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Wed, 11 Jan 2023 11:12:41 +0000 Subject: [PATCH 01/45] Try debugging everything --- flightcontrol.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flightcontrol.json b/flightcontrol.json index afa7e6c9c..ecf778aa1 100644 --- a/flightcontrol.json +++ b/flightcontrol.json @@ -90,7 +90,8 @@ }, "PIZZLY_HOST": "https://auth.trigger.dev", "TRIGGER_LOG_LEVEL": "debug", - "PULSAR_DEBUG": true + "PULSAR_DEBUG": true, + "DEBUG": "*" } }, { From 9187a6ae01090faf9df588e3a0132e3cb38f58e3 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 18 Jan 2023 17:16:53 -0800 Subject: [PATCH 02/45] Added a new settings page to archive and disable workflows --- apps/webapp/app/components/layout/List.tsx | 2 +- .../app/components/navigation/SideMenu.tsx | 6 +++++ .../app/components/primitives/Buttons.tsx | 13 ++++++++- .../workflows/$workflowSlug/index.tsx | 11 ++++++-- .../workflows/$workflowSlug/settings.tsx | 27 +++++++++++++++++++ 5 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/settings.tsx diff --git a/apps/webapp/app/components/layout/List.tsx b/apps/webapp/app/components/layout/List.tsx index c80bef3b0..d7e0b583c 100644 --- a/apps/webapp/app/components/layout/List.tsx +++ b/apps/webapp/app/components/layout/List.tsx @@ -4,7 +4,7 @@ export type ListProps = { export function List({ children }: { children: React.ReactNode }) { return ( -
+
    {children}
); diff --git a/apps/webapp/app/components/navigation/SideMenu.tsx b/apps/webapp/app/components/navigation/SideMenu.tsx index 20b4bf691..35bb7b526 100644 --- a/apps/webapp/app/components/navigation/SideMenu.tsx +++ b/apps/webapp/app/components/navigation/SideMenu.tsx @@ -10,6 +10,7 @@ import { BeakerIcon, ClipboardDocumentCheckIcon, Squares2X2Icon, + Cog6ToothIcon, } from "@heroicons/react/24/outline"; import { Link, NavLink } from "@remix-run/react"; import { @@ -94,6 +95,11 @@ export function WorkflowsSideMenu() { icon: , to: `integrations`, }, + { + name: "Settings", + icon: , + to: `settings`, + }, ]; return ( diff --git a/apps/webapp/app/components/primitives/Buttons.tsx b/apps/webapp/app/components/primitives/Buttons.tsx index 18bea99b2..5ddbde36a 100644 --- a/apps/webapp/app/components/primitives/Buttons.tsx +++ b/apps/webapp/app/components/primitives/Buttons.tsx @@ -11,11 +11,14 @@ const secondaryClasses = classnames( commonClasses, "bg-transparent border border-slate-700 text-white hover:bg-white/5 hover:border-slate-700 focus:ring-slate-300 gap-2" ); - const tertiaryClasses = classnames( commonClasses, "text-white/60 hover:text-white gap-1" ); +const dangerClasses = classnames( + commonClasses, + "bg-rose-700 text-white hover:bg-rose-600 focus:ring-rose-800 gap-2" +); type ButtonProps = React.DetailedHTMLProps< React.ButtonHTMLAttributes, @@ -57,6 +60,14 @@ export function TertiaryButton({ children, className, ...props }: ButtonProps) { ); } +export function DangerButton({ children, className, ...props }: ButtonProps) { + return ( + + ); +} + export function PrimaryLink({ children, className, to, ...props }: LinkProps) { return ( diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx index 50f07e681..ffd889e08 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx @@ -1,3 +1,4 @@ +import { ArrowsRightLeftIcon } from "@heroicons/react/24/outline"; import type { LoaderArgs } from "@remix-run/server-runtime"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import invariant from "tiny-invariant"; @@ -5,8 +6,14 @@ import { WorkflowConnections } from "~/components/integrations/WorkflowConnectio import { Panel } from "~/components/layout/Panel"; import { PanelHeader } from "~/components/layout/PanelHeader"; import { PanelWarning } from "~/components/layout/PanelWarning"; -import { PrimaryLink, SecondaryLink } from "~/components/primitives/Buttons"; +import { + DangerButton, + PrimaryLink, + SecondaryButton, + SecondaryLink, +} from "~/components/primitives/Buttons"; import { Body } from "~/components/primitives/text/Body"; +import { Header3 } from "~/components/primitives/text/Headers"; import { SubTitle } from "~/components/primitives/text/SubTitle"; import { Title } from "~/components/primitives/text/Title"; import { RunsTable } from "~/components/runs/RunsTable"; @@ -123,7 +130,7 @@ export default function Page() { View all
- + + Settings + Workflow + +
+ + + send-to-slack-on-new-domain is disabled + +
+
+ Disable + Archive +
+
+ + ); +} From 5848b613c59f3879186baed48efc63a8c752cb85 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 18 Jan 2023 17:19:20 -0800 Subject: [PATCH 03/45] Examples onboarding link goes to the right place in the docs --- apps/webapp/app/components/CreateNewWorkflow.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/webapp/app/components/CreateNewWorkflow.tsx b/apps/webapp/app/components/CreateNewWorkflow.tsx index cc051a776..dca9ac0a9 100644 --- a/apps/webapp/app/components/CreateNewWorkflow.tsx +++ b/apps/webapp/app/components/CreateNewWorkflow.tsx @@ -19,7 +19,7 @@ export default function CreateNewWorkflow() { Documentation @@ -51,7 +51,7 @@ export function CreateNewWorkflowNoWorkflows() { Documentation From 089433e00fbd6bd9f967b46ea7a6198cd415b2d9 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 18 Jan 2023 17:22:36 -0800 Subject: [PATCH 04/45] Removed un-used imports --- .../$organizationSlug/workflows/$workflowSlug/index.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx index ffd889e08..295511510 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx @@ -1,4 +1,3 @@ -import { ArrowsRightLeftIcon } from "@heroicons/react/24/outline"; import type { LoaderArgs } from "@remix-run/server-runtime"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import invariant from "tiny-invariant"; @@ -6,12 +5,7 @@ import { WorkflowConnections } from "~/components/integrations/WorkflowConnectio import { Panel } from "~/components/layout/Panel"; import { PanelHeader } from "~/components/layout/PanelHeader"; import { PanelWarning } from "~/components/layout/PanelWarning"; -import { - DangerButton, - PrimaryLink, - SecondaryButton, - SecondaryLink, -} from "~/components/primitives/Buttons"; +import { PrimaryLink, SecondaryLink } from "~/components/primitives/Buttons"; import { Body } from "~/components/primitives/text/Body"; import { Header3 } from "~/components/primitives/text/Headers"; import { SubTitle } from "~/components/primitives/text/SubTitle"; From 07b8ca893afb8ab372ee3586e2aa1632e283fbf7 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 18 Jan 2023 17:54:44 -0800 Subject: [PATCH 05/45] Added the api logo to the settings page --- .../workflows/$workflowSlug/settings.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/settings.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/settings.tsx index 999c45a1c..b7c0df006 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/settings.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/settings.tsx @@ -1,4 +1,4 @@ -import { ArrowsRightLeftIcon } from "@heroicons/react/24/outline"; +import { ApiLogoIcon } from "~/components/code/ApiLogoIcon"; import { Panel } from "~/components/layout/Panel"; import { SecondaryButton, DangerButton } from "~/components/primitives/Buttons"; import { Header3 } from "~/components/primitives/text/Headers"; @@ -12,9 +12,12 @@ export default function Page() { Workflow
- + - send-to-slack-on-new-domain is disabled + Send to Slack on a new domain is disabled
From 5f27ad936b771a4853acf05617f595ee8363dd03 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 18 Jan 2023 17:56:39 -0800 Subject: [PATCH 06/45] Added a disabled warning to the overview page --- .../workflows/$workflowSlug/index.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx index 295511510..01e4202f2 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx @@ -5,9 +5,12 @@ import { WorkflowConnections } from "~/components/integrations/WorkflowConnectio import { Panel } from "~/components/layout/Panel"; import { PanelHeader } from "~/components/layout/PanelHeader"; import { PanelWarning } from "~/components/layout/PanelWarning"; -import { PrimaryLink, SecondaryLink } from "~/components/primitives/Buttons"; +import { + PrimaryLink, + SecondaryLink, + TertiaryLink, +} from "~/components/primitives/Buttons"; import { Body } from "~/components/primitives/text/Body"; -import { Header3 } from "~/components/primitives/text/Headers"; import { SubTitle } from "~/components/primitives/text/SubTitle"; import { Title } from "~/components/primitives/text/Title"; import { RunsTable } from "~/components/runs/RunsTable"; @@ -88,6 +91,14 @@ export default function Page() { )} + + + This workflow is disabled. + + + Settings + + {apiConnectionCount > 0 && } {eventRule && ( <> From c7901f9597a2ac4297e8b4e0fc1aca67e8d4a3fb Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 18 Jan 2023 17:56:59 -0800 Subject: [PATCH 07/45] Added a new disabled status for the workflow list --- .../orgs/$organizationSlug/__org/index.tsx | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx index 10bd85be3..325b7507e 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx @@ -114,7 +114,8 @@ function WorkflowList({ {workflow.trigger.title}
-
+
+ {workflow.trigger.properties && workflow.trigger.properties.map((property) => ( ); } + +function WorkflowStatus({ + label, + content, +}: { + label: string; + content: string; +}) { + return ( +
+ + {label} + + + {content} + +
+ ); +} From 1cebed4cf74e7e2dfacde3d59bd96c5a06d56a4b Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 18 Jan 2023 17:57:22 -0800 Subject: [PATCH 08/45] Update to the button paddings --- apps/webapp/app/components/primitives/Buttons.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/webapp/app/components/primitives/Buttons.tsx b/apps/webapp/app/components/primitives/Buttons.tsx index 5ddbde36a..e878f8eea 100644 --- a/apps/webapp/app/components/primitives/Buttons.tsx +++ b/apps/webapp/app/components/primitives/Buttons.tsx @@ -2,14 +2,14 @@ import { Link } from "@remix-run/react"; import classnames from "classnames"; const commonClasses = - "inline-flex items-center justify-center max-w-max rounded px-4 py-2 text-sm transition whitespace-nowrap"; + "inline-flex items-center justify-center max-w-max rounded text-sm transition whitespace-nowrap"; const primaryClasses = classnames( commonClasses, - "bg-indigo-700 text-white hover:bg-indigo-600 focus:ring-indigo-800 gap-2" + "px-4 py-2 bg-indigo-700 text-white hover:bg-indigo-600 focus:ring-indigo-800 gap-2" ); const secondaryClasses = classnames( commonClasses, - "bg-transparent border border-slate-700 text-white hover:bg-white/5 hover:border-slate-700 focus:ring-slate-300 gap-2" + "px-4 py-2 bg-transparent border border-slate-700 text-white hover:bg-white/5 hover:border-slate-700 focus:ring-slate-300 gap-2" ); const tertiaryClasses = classnames( commonClasses, @@ -17,7 +17,7 @@ const tertiaryClasses = classnames( ); const dangerClasses = classnames( commonClasses, - "bg-rose-700 text-white hover:bg-rose-600 focus:ring-rose-800 gap-2" + "px-4 py-2 bg-rose-700 text-white hover:bg-rose-600 focus:ring-rose-800 gap-2" ); type ButtonProps = React.DetailedHTMLProps< From 765e8a4816bc47223196718649904300cecae315 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 18 Jan 2023 18:00:36 -0800 Subject: [PATCH 09/45] Filter message now matches the warning status styles --- apps/webapp/app/components/runs/RunsTable.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/webapp/app/components/runs/RunsTable.tsx b/apps/webapp/app/components/runs/RunsTable.tsx index 71600e5ab..9a5a23d3c 100644 --- a/apps/webapp/app/components/runs/RunsTable.tsx +++ b/apps/webapp/app/components/runs/RunsTable.tsx @@ -5,6 +5,7 @@ import humanizeDuration from "humanize-duration"; import type { ReactNode } from "react"; import type { WorkflowRunListPresenter } from "~/models/workflowRunListPresenter.server"; import { dateDifference, formatDateTime } from "~/utils"; +import { PanelWarning } from "../layout/PanelWarning"; import { Spinner } from "../primitives/Spinner"; import { runStatusIcon, runStatusLabel } from "./runStatus"; @@ -151,10 +152,7 @@ function BlankRow({ children }: { children: ReactNode }) { export function NoRuns({ title }: { title: string }) { return (
-
- - {title} -
+ {title}
); } From 5de2a1aec2b3430ecb55829945f8506802ea6003 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Wed, 18 Jan 2023 18:09:03 -0800 Subject: [PATCH 10/45] Fixed issue with workflow runs not completing when the run function returned undefined or null --- .changeset/dry-beds-double.md | 5 +++++ apps/webapp/app/models/workflowRun.server.ts | 9 ++++++--- .../app/services/messageBroker.server.ts | 4 ++-- .../internal-bridge/src/schemas/server.ts | 2 +- packages/internal-bridge/src/zodRPC.ts | 20 ++++++++++--------- .../src/messages/schemas/workflowRuns.ts | 2 +- 6 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 .changeset/dry-beds-double.md diff --git a/.changeset/dry-beds-double.md b/.changeset/dry-beds-double.md new file mode 100644 index 000000000..8d73326fd --- /dev/null +++ b/.changeset/dry-beds-double.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/sdk": patch +--- + +Fixed issue with workflow runs not completing when the run function returned undefined or null diff --git a/apps/webapp/app/models/workflowRun.server.ts b/apps/webapp/app/models/workflowRun.server.ts index ba60ce6a8..ba12dfc0a 100644 --- a/apps/webapp/app/models/workflowRun.server.ts +++ b/apps/webapp/app/models/workflowRun.server.ts @@ -80,10 +80,10 @@ export async function failWorkflowRun( } export async function completeWorkflowRun( - output: string, runId: string, apiKey: string, - timestamp: string + timestamp: string, + output?: string | null ) { const workflowRun = await findWorkflowRunScopedToApiKey(runId, apiKey); @@ -104,6 +104,9 @@ export async function completeWorkflowRun( }, }); + const parsedOutput = + typeof output === "string" ? JSON.parse(output) : undefined; + await tx.workflowRunStep.upsert({ where: { runId_idempotencyKey: { @@ -115,7 +118,7 @@ export async function completeWorkflowRun( runId, idempotencyKey: "output", type: "OUTPUT", - output: JSON.parse(output), + output: parsedOutput === null ? undefined : parsedOutput, context: {}, startedAt: new Date(), finishedAt: new Date(), diff --git a/apps/webapp/app/services/messageBroker.server.ts b/apps/webapp/app/services/messageBroker.server.ts index 064655f6b..d1966fa7d 100644 --- a/apps/webapp/app/services/messageBroker.server.ts +++ b/apps/webapp/app/services/messageBroker.server.ts @@ -187,10 +187,10 @@ function createCommandSubscriber() { }, WORKFLOW_RUN_COMPLETE: async (id, data, properties) => { await completeWorkflowRun( - data.output, properties["x-workflow-run-id"], properties["x-api-key"], - properties["x-timestamp"] + properties["x-timestamp"], + data.output ); return true; diff --git a/packages/internal-bridge/src/schemas/server.ts b/packages/internal-bridge/src/schemas/server.ts index 08e52851b..7adc906ce 100644 --- a/packages/internal-bridge/src/schemas/server.ts +++ b/packages/internal-bridge/src/schemas/server.ts @@ -81,7 +81,7 @@ export const ServerRPCSchema = { COMPLETE_WORKFLOW_RUN: { request: z.object({ runId: z.string(), - output: z.string(), + output: z.string().optional(), timestamp: z.string(), }), response: z.boolean(), diff --git a/packages/internal-bridge/src/zodRPC.ts b/packages/internal-bridge/src/zodRPC.ts index 89ba8b8a8..96cf46868 100644 --- a/packages/internal-bridge/src/zodRPC.ts +++ b/packages/internal-bridge/src/zodRPC.ts @@ -1,6 +1,7 @@ import { z, ZodError } from "zod"; import { createHash } from "node:crypto"; import { IConnection } from "./types"; +import { Logger } from "./logger"; export const RPCMessageSchema = z.object({ id: z.string(), @@ -45,6 +46,7 @@ export class ZodRPC< #receiver: ReceiverSchema; #handlers: ZodRPCHandlers; #pendingCalls = new Map(); + #logger: Logger = new Logger("ZodRPC"); constructor(options: ZodRPCOptions) { this.#connection = options.connection; @@ -73,7 +75,7 @@ export class ZodRPC< await this.#onResponse(data); } } catch (err) { - console.error(err); + this.#logger.error(err); } } @@ -82,14 +84,12 @@ export class ZodRPC< await this.#handleCall(message); } catch (callError) { if (callError instanceof ZodError) { - console.error( - `[ZodRPC][foobar] Received invalid call:\n${JSON.stringify( - message - )}: `, + this.#logger.error( + `[ZodRPC] Received invalid call:\n${JSON.stringify(message)}: `, callError.errors ); } else { - console.error( + this.#logger.error( `[ZodRPC] Error handling call:\n${JSON.stringify(message)}: `, callError ); @@ -102,12 +102,12 @@ export class ZodRPC< await this.#handleResponse(message); } catch (callError) { if (callError instanceof ZodError) { - console.error( + this.#logger.error( `[ZodRPC] Received invalid response\n\n${JSON.stringify(message)}: `, callError.flatten() ); } else { - console.error( + this.#logger.error( `[ZodRPC] Error handling response\n\n${JSON.stringify(message)}: `, callError ); @@ -153,6 +153,8 @@ export class ZodRPC< throw new Error(`There is no method for ${message.methodName}`); } + this.#logger.debug("Received call", { message }); + // struggling to get real inference here const inputs = method.request.parse(message.data); @@ -169,7 +171,7 @@ export class ZodRPC< try { await this.#connection.send(preparedResponseText); } catch (err) { - console.error("Failed sending response", preparedResponseText, err); + this.#logger.error("Failed sending response", preparedResponseText, err); } return; diff --git a/packages/internal-platform/src/messages/schemas/workflowRuns.ts b/packages/internal-platform/src/messages/schemas/workflowRuns.ts index 9e82022ad..a619c73f2 100644 --- a/packages/internal-platform/src/messages/schemas/workflowRuns.ts +++ b/packages/internal-platform/src/messages/schemas/workflowRuns.ts @@ -5,7 +5,7 @@ import { WorkflowSendRunEventPropertiesSchema } from "../sharedSchemas"; export const commands = { WORKFLOW_RUN_COMPLETE: { data: z.object({ - output: z.string(), + output: z.string().optional(), }), properties: WorkflowSendRunEventPropertiesSchema, }, From 7f2654884aa4cd7baa4c5617465f9328360eabc1 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Wed, 18 Jan 2023 18:25:52 -0800 Subject: [PATCH 11/45] Added some logging messages to the client, and disabled them by default --- .changeset/breezy-chicken-hide.md | 5 +++++ examples/smoke-test/src/index.ts | 11 +---------- packages/internal-bridge/src/logger.ts | 25 ++++++++++++++++--------- packages/trigger-sdk/src/client.ts | 6 ++++++ 4 files changed, 28 insertions(+), 19 deletions(-) create mode 100644 .changeset/breezy-chicken-hide.md diff --git a/.changeset/breezy-chicken-hide.md b/.changeset/breezy-chicken-hide.md new file mode 100644 index 000000000..ce66e38dd --- /dev/null +++ b/.changeset/breezy-chicken-hide.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/sdk": patch +--- + +Added some logging messages (and disabled any messages by default) diff --git a/examples/smoke-test/src/index.ts b/examples/smoke-test/src/index.ts index def483e05..16a628954 100644 --- a/examples/smoke-test/src/index.ts +++ b/examples/smoke-test/src/index.ts @@ -11,7 +11,7 @@ const trigger = new Trigger({ name: "My workflow", apiKey: "trigger_dev_zC25mKNn6c0q", endpoint: "ws://localhost:8889/ws", - logLevel: "debug", + logLevel: "log", on: customEvent({ name: "user.created", schema: userCreatedEvent }), run: async (event, ctx) => { await ctx.logger.info("Inside the smoke test workflow, received event", { @@ -34,12 +34,3 @@ const trigger = new Trigger({ }); trigger.listen(); - -(async () => { - process.env.TRIGGER_API_KEY = "trigger_dev_zC25mKNn6c0q"; - process.env.TRIGGER_API_URL = "http://localhost:3000"; - await sendEvent(ulid(), { - name: "user.created", - payload: { id: "123" }, - }); -})(); diff --git a/packages/internal-bridge/src/logger.ts b/packages/internal-bridge/src/logger.ts index b3ccd59e1..4344e9bbe 100644 --- a/packages/internal-bridge/src/logger.ts +++ b/packages/internal-bridge/src/logger.ts @@ -1,14 +1,21 @@ // Create a logger class that uses the debug package internally -export type LogLevel = "log" | "error" | "warn" | "info" | "debug"; +const logLevels = [ + "disabled", + "log", + "error", + "warn", + "info", + "debug", +] as const; -const logLevels: Array = ["log", "error", "warn", "info", "debug"]; +export type LogLevel = (typeof logLevels)[number]; export class Logger { #name: string; readonly #level: number; - constructor(name: string, level: LogLevel = "info") { + constructor(name: string, level: LogLevel = "disabled") { this.#name = name; this.#level = logLevels.indexOf( (process.env.TRIGGER_LOG_LEVEL ?? level) as LogLevel @@ -16,31 +23,31 @@ export class Logger { } log(...args: any[]) { - if (this.#level < 0) return; + if (this.#level < 1) return; - console.log(`[${formattedDateTime()}] [${this.#name}] `, ...args); + console.log(`[${this.#name}] `, ...args); } error(...args: any[]) { - if (this.#level < 1) return; + if (this.#level < 2) return; console.error(`[${formattedDateTime()}] [${this.#name}] `, ...args); } warn(...args: any[]) { - if (this.#level < 2) return; + if (this.#level < 3) return; console.warn(`[${formattedDateTime()}] [${this.#name}] `, ...args); } info(...args: any[]) { - if (this.#level < 3) return; + if (this.#level < 4) return; console.info(`[${formattedDateTime()}] [${this.#name}] `, ...args); } debug(...args: any[]) { - if (this.#level < 4) return; + if (this.#level < 5) return; console.debug(`[${formattedDateTime()}] [${this.#name}] `, ...args); } diff --git a/packages/trigger-sdk/src/client.ts b/packages/trigger-sdk/src/client.ts index 4263654e5..720a2c065 100644 --- a/packages/trigger-sdk/src/client.ts +++ b/packages/trigger-sdk/src/client.ts @@ -362,6 +362,12 @@ export class TriggerClient { return this.#trigger.options .run(eventData, ctx) .then((output) => { + this.#logger.log( + `Completed workflow '${this.#options.name}', run ${ + data.id + } 🏃` + ); + return serverRPC.send("COMPLETE_WORKFLOW_RUN", { runId: data.id, output: JSON.stringify(output), From 0994308b63983f3b6f3768b6d0576730ccd51482 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Wed, 18 Jan 2023 18:45:13 -0800 Subject: [PATCH 12/45] Complete CUSTOM_EVENT steps after ingesting the event --- apps/webapp/app/models/workflowRun.server.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/webapp/app/models/workflowRun.server.ts b/apps/webapp/app/models/workflowRun.server.ts index ba12dfc0a..663e00bac 100644 --- a/apps/webapp/app/models/workflowRun.server.ts +++ b/apps/webapp/app/models/workflowRun.server.ts @@ -173,6 +173,13 @@ export async function triggerEventInRun( }, workflowRun.environment.organization ); + + await prisma.workflowRunStep.update({ + where: { id: step.step.id }, + data: { + status: "SUCCESS", + }, + }); } export async function logMessageInRun( From 598d98473c7695c77b4c1815938531e63bb17a0f Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 18 Jan 2023 18:53:36 -0800 Subject: [PATCH 13/45] =?UTF-8?q?Updated=20the=20disabled=20state=20to=20b?= =?UTF-8?q?e=20more=20=E2=80=98info=E2=80=99=20than=20=E2=80=98warning?= =?UTF-8?q?=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/components/layout/PanelInfo.tsx | 23 ++++++++++++++++ .../app/components/layout/PanelWarning.tsx | 2 +- apps/webapp/app/components/runs/RunsTable.tsx | 2 +- .../orgs/$organizationSlug/__org/index.tsx | 26 +++++-------------- .../workflows/$workflowSlug/index.tsx | 10 ++++--- .../workflows/$workflowSlug/integrations.tsx | 13 +++++++++- .../workflows/$workflowSlug/runs/index.tsx | 13 +++++++++- .../workflows/$workflowSlug/settings.tsx | 25 +++++++++++++++--- .../workflows/$workflowSlug/test.tsx | 15 ++++++++--- 9 files changed, 95 insertions(+), 34 deletions(-) create mode 100644 apps/webapp/app/components/layout/PanelInfo.tsx diff --git a/apps/webapp/app/components/layout/PanelInfo.tsx b/apps/webapp/app/components/layout/PanelInfo.tsx new file mode 100644 index 000000000..967235abe --- /dev/null +++ b/apps/webapp/app/components/layout/PanelInfo.tsx @@ -0,0 +1,23 @@ +import { InformationCircleIcon } from "@heroicons/react/24/solid"; + +export type PanelInfoProps = { + children: React.ReactNode; + className?: string; +}; + +export function PanelInfo({ + children, + className, +}: { + children: React.ReactNode; + className?: string; +}) { + return ( +
+ + {children} +
+ ); +} diff --git a/apps/webapp/app/components/layout/PanelWarning.tsx b/apps/webapp/app/components/layout/PanelWarning.tsx index 5096f6a3f..8cc71705a 100644 --- a/apps/webapp/app/components/layout/PanelWarning.tsx +++ b/apps/webapp/app/components/layout/PanelWarning.tsx @@ -16,7 +16,7 @@ export function PanelWarning({
- + {children}
); diff --git a/apps/webapp/app/components/runs/RunsTable.tsx b/apps/webapp/app/components/runs/RunsTable.tsx index 9a5a23d3c..ba5a6b7fe 100644 --- a/apps/webapp/app/components/runs/RunsTable.tsx +++ b/apps/webapp/app/components/runs/RunsTable.tsx @@ -1,4 +1,4 @@ -import { BeakerIcon, InformationCircleIcon } from "@heroicons/react/24/outline"; +import { BeakerIcon } from "@heroicons/react/24/outline"; import { Link } from "@remix-run/react"; import classNames from "classnames"; import humanizeDuration from "humanize-duration"; diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx index 325b7507e..a9685af43 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx @@ -4,6 +4,7 @@ import { } from "@heroicons/react/24/solid"; import { Link } from "@remix-run/react"; import type { LoaderArgs } from "@remix-run/server-runtime"; +import classNames from "classnames"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import invariant from "tiny-invariant"; import { ApiLogoIcon } from "~/components/code/ApiLogoIcon"; @@ -84,7 +85,10 @@ function WorkflowList({
  • @@ -115,7 +119,6 @@ function WorkflowList({
    - {workflow.trigger.properties && workflow.trigger.properties.map((property) => ( - - {label} - - - {content} - -
    - ); -} +const workflowDisabled = "opacity-30"; diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx index 01e4202f2..47b3f899f 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx @@ -4,6 +4,7 @@ import invariant from "tiny-invariant"; import { WorkflowConnections } from "~/components/integrations/WorkflowConnections"; import { Panel } from "~/components/layout/Panel"; import { PanelHeader } from "~/components/layout/PanelHeader"; +import { PanelInfo } from "~/components/layout/PanelInfo"; import { PanelWarning } from "~/components/layout/PanelWarning"; import { PrimaryLink, @@ -85,20 +86,21 @@ export default function Page() {
    {workflow.status !== "READY" && ( <> - 1 issue This workflow requires its APIs to be connected before it can run. )} - + - This workflow is disabled. + This workflow is disabled. Runs cannot be triggered or tested while + disabled. If a run is currently in progress, it will fail. + Settings - + {apiConnectionCount > 0 && } {eventRule && ( <> diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/integrations.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/integrations.tsx index d2a31eb71..b80f7b6fc 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/integrations.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/integrations.tsx @@ -1,6 +1,9 @@ import invariant from "tiny-invariant"; import { WorkflowConnections } from "~/components/integrations/WorkflowConnections"; +import { PanelInfo } from "~/components/layout/PanelInfo"; import { PanelWarning } from "~/components/layout/PanelWarning"; +import { TertiaryLink } from "~/components/primitives/Buttons"; +import { Body } from "~/components/primitives/text/Body"; import { SubTitle } from "~/components/primitives/text/SubTitle"; import { Title } from "~/components/primitives/text/Title"; import { useConnectionSlots } from "~/hooks/useConnectionSlots"; @@ -18,12 +21,20 @@ export default function Page() { Connected APIs {workflow.status !== "READY" && ( <> - 1 issue This workflow requires its APIs to be connected before it can run. )} + + + This workflow is disabled. Runs cannot be triggered or tested while + disabled. If a run is currently in progress, it will fail. + + + Settings + + {connectionSlots.source || connectionSlots.services.length > 0 ? ( ) : ( diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/runs/index.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/runs/index.tsx index 3d85a10ef..d9eb93075 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/runs/index.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/runs/index.tsx @@ -24,6 +24,9 @@ import { Title } from "~/components/primitives/text/Title"; import { useCurrentWorkflow } from "~/hooks/useWorkflows"; import { PanelWarning } from "~/components/layout/PanelWarning"; import { SubTitle } from "~/components/primitives/text/SubTitle"; +import { TertiaryLink } from "~/components/primitives/Buttons"; +import { Body } from "~/components/primitives/text/Body"; +import { PanelInfo } from "~/components/layout/PanelInfo"; export const loader = async ({ request, params }: LoaderArgs) => { const userId = await requireUserId(request); @@ -79,12 +82,20 @@ export default function Page() { Runs {workflow.status !== "READY" && ( <> - 1 issue This workflow requires its APIs to be connected before it can run. )} + + + This workflow is disabled. Runs cannot be triggered or tested while + disabled. If a run is currently in progress, it will fail. + + + Settings + + - Send to Slack on a new domain is disabled + Send to Slack on a new domain workflow is disabled. + +
  • +
    + Enable + Archive +
    + + +
    + + + Send to Slack on a new domain workflow is archived.
    Disable - Archive + Unarchive
    diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/test.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/test.tsx index aeaca13c3..61a79dd4d 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/test.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/test.tsx @@ -5,10 +5,11 @@ import { typedjson, useTypedLoaderData } from "remix-typedjson"; import invariant from "tiny-invariant"; import { JSONEditor } from "~/components/code/JSONEditor"; import { Panel } from "~/components/layout/Panel"; +import { PanelInfo } from "~/components/layout/PanelInfo"; import { PanelWarning } from "~/components/layout/PanelWarning"; -import { PrimaryButton } from "~/components/primitives/Buttons"; +import { PrimaryButton, TertiaryLink } from "~/components/primitives/Buttons"; import { Select } from "~/components/primitives/Select"; -import { SubTitle } from "~/components/primitives/text/SubTitle"; +import { Body } from "~/components/primitives/text/Body"; import { Title } from "~/components/primitives/text/Title"; import { useCurrentOrganization } from "~/hooks/useOrganizations"; import { useCurrentWorkflow } from "~/hooks/useWorkflows"; @@ -42,12 +43,20 @@ export default function Page() { Test {workflow.status !== "READY" && ( <> - 1 issue This workflow requires its APIs to be connected before it can run. )} + + + This workflow is disabled. Runs cannot be triggered or tested while + disabled. If a run is currently in progress, it will fail. + + + Settings + + {workflow.status === "READY" && ( Date: Wed, 18 Jan 2023 19:03:42 -0800 Subject: [PATCH 14/45] Added another state to the settings page --- .../workflows/$workflowSlug/settings.tsx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/settings.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/settings.tsx index 1599a1b25..1c33994c8 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/settings.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/settings.tsx @@ -14,6 +14,21 @@ export default function Page() { <> Settings Workflow + +
    + + + Send to Slack on a new domain workflow is active. + +
    +
    + Disable + Archive +
    +
    - Disable Unarchive
    From 6966ad0f55396f335be4f97b5f98a25fdd25a74d Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Wed, 18 Jan 2023 19:04:28 -0800 Subject: [PATCH 15/45] Increase ioThreads and messageListenerThreads (and turned off batching) to pulsar clients --- apps/webapp/app/services/messageBroker.server.ts | 5 +++++ apps/wss/src/index.ts | 1 + apps/wss/src/pulsarClient.ts | 6 +++++- apps/wss/src/server.ts | 3 ++- packages/internal-platform/src/messages/zodPubSub.ts | 6 +++++- 5 files changed, 18 insertions(+), 3 deletions(-) diff --git a/apps/webapp/app/services/messageBroker.server.ts b/apps/webapp/app/services/messageBroker.server.ts index d1966fa7d..08f17a71e 100644 --- a/apps/webapp/app/services/messageBroker.server.ts +++ b/apps/webapp/app/services/messageBroker.server.ts @@ -130,6 +130,9 @@ export async function init() { function createClient() { const client = createPulsarClient({ serviceUrl: env.PULSAR_SERVICE_URL, + ioThreads: 5, + messageListenerThreads: 5, + operationTimeoutSeconds: 30, }); console.log(`📡 Connected to pulsar at ${env.PULSAR_SERVICE_URL}`); @@ -142,6 +145,7 @@ function createTriggerPublisher() { client: pulsarClient, config: { topic: Topics.triggers, + batchingEnabled: false, }, schema: triggerCatalog, }); @@ -251,6 +255,7 @@ function createCommandResponsePublisher() { client: pulsarClient, config: { topic: Topics.runCommandResponses, + batchingEnabled: false, }, schema: commandResponseCatalog, }); diff --git a/apps/wss/src/index.ts b/apps/wss/src/index.ts index 0c532fda3..84628ca26 100644 --- a/apps/wss/src/index.ts +++ b/apps/wss/src/index.ts @@ -58,6 +58,7 @@ async function main() { client: pulsarClient, config: { topic: Topics.runCommands, + batchingEnabled: false, }, }); diff --git a/apps/wss/src/pulsarClient.ts b/apps/wss/src/pulsarClient.ts index 3ab781c16..243d00a7a 100644 --- a/apps/wss/src/pulsarClient.ts +++ b/apps/wss/src/pulsarClient.ts @@ -72,4 +72,8 @@ function oauth2AuthenticationFromEnv(): AuthenticationOauth2 | undefined { }); } -export const pulsarClient = createPulsarClient(); +export const pulsarClient = createPulsarClient({ + ioThreads: 4, + messageListenerThreads: 4, + operationTimeoutSeconds: 30, +}); diff --git a/apps/wss/src/server.ts b/apps/wss/src/server.ts index c2d16f663..72a48c664 100644 --- a/apps/wss/src/server.ts +++ b/apps/wss/src/server.ts @@ -296,7 +296,7 @@ export class TriggerServer { } async #initializeWorkflow( - data: z.infer + data: z.infer<(typeof ServerRPCSchema)["INITIALIZE_HOST"]["request"]> ) { if (this.#isInitialized) { throw new Error( @@ -502,6 +502,7 @@ function createForwardHandler< client: pulsarClient, config: { topic: topicName, + batchingEnabled: false, }, }); diff --git a/packages/internal-platform/src/messages/zodPubSub.ts b/packages/internal-platform/src/messages/zodPubSub.ts index ea2bf4a81..c75cf0e25 100644 --- a/packages/internal-platform/src/messages/zodPubSub.ts +++ b/packages/internal-platform/src/messages/zodPubSub.ts @@ -27,7 +27,11 @@ export class ZodPubSub { constructor(options: ZodPubSubOptions) { this.#publisher = new ZodPublisher({ client: options.client, - config: { ...options.publisherConfig, topic: options.topic }, + config: { + ...options.publisherConfig, + topic: options.topic, + batchingEnabled: false, + }, schema: options.schema, }); From 287cdd2c23cdca6b7fe3ba2bed11a5fdee2566b5 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 18 Jan 2023 19:53:04 -0800 Subject: [PATCH 16/45] Fixed the dropdown not displaying --- apps/webapp/app/components/layout/List.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webapp/app/components/layout/List.tsx b/apps/webapp/app/components/layout/List.tsx index d7e0b583c..bc3e512cf 100644 --- a/apps/webapp/app/components/layout/List.tsx +++ b/apps/webapp/app/components/layout/List.tsx @@ -4,7 +4,7 @@ export type ListProps = { export function List({ children }: { children: React.ReactNode }) { return ( -
    +
      {children}
    ); From 347567e64831fef5ce5a5c9783e5c0a2c1fae072 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 18 Jan 2023 20:03:19 -0800 Subject: [PATCH 17/45] buttons and dropdowns now consistent size --- apps/webapp/app/components/integrations/ConnectionSelector.tsx | 2 +- apps/webapp/app/components/primitives/ListBox.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/webapp/app/components/integrations/ConnectionSelector.tsx b/apps/webapp/app/components/integrations/ConnectionSelector.tsx index 668908863..d63c8105f 100644 --- a/apps/webapp/app/components/integrations/ConnectionSelector.tsx +++ b/apps/webapp/app/components/integrations/ConnectionSelector.tsx @@ -55,7 +55,7 @@ export function ConnectionSelector({ {selectedConnection ? ( diff --git a/apps/webapp/app/components/primitives/ListBox.tsx b/apps/webapp/app/components/primitives/ListBox.tsx index 12a7ad4f9..b0d313de4 100644 --- a/apps/webapp/app/components/primitives/ListBox.tsx +++ b/apps/webapp/app/components/primitives/ListBox.tsx @@ -17,7 +17,7 @@ type ButtonProps = Parameters[0] & { }; const buttonClassName = - "relative w-full rounded bg-slate-700 py-2 pl-3 pr-10 text-sm text-left shadow-md hover:cursor-pointer hover:bg-slate-600/80 transition"; + "relative w-full rounded bg-slate-700 py-2 pl-4 pr-10 text-slate-300 text-sm text-left shadow-md hover:cursor-pointer hover:bg-slate-600/80 transition"; function Button({ children, ...props }: ButtonProps) { return ( Date: Wed, 18 Jan 2023 20:10:32 -0800 Subject: [PATCH 18/45] Updated the integrations buttons --- .../__app/orgs/$organizationSlug/__org/integrations.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/integrations.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/integrations.tsx index 87b725031..4faea3b33 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/integrations.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/integrations.tsx @@ -1,4 +1,4 @@ -import { SquaresPlusIcon } from "@heroicons/react/24/outline"; +import { EnvelopeIcon } from "@heroicons/react/24/outline"; import { PlusCircleIcon } from "@heroicons/react/24/solid"; import type { LoaderArgs } from "@remix-run/server-runtime"; import type { Provider } from "@trigger.dev/providers"; @@ -104,7 +104,7 @@ export default function Integrations() { className="flex flex-col group max-w-[160px] rounded-md bg-slate-800 border border-slate-800 gap-4 text-sm text-slate-200 items-center overflow-hidden hover:bg-slate-800/30 transition shadow-md disabled:opacity-50" >
    - +
    Need an integration? @@ -129,7 +129,7 @@ function AddButtonContent({ return ( <>
    - + {integration.name} Date: Wed, 18 Jan 2023 19:19:23 -0800 Subject: [PATCH 19/45] All GitHub issue webhooks now work --- examples/github-webhook/src/index.ts | 23 +- .../internal-integrations/src/github/index.ts | 1 - .../src/integrations/github/events.ts | 8 +- .../src/providers/github/schemas.ts | 240 ------------ .../src/providers/github/schemas/index.ts | 23 ++ .../src/providers/github/schemas/issues.ts | 311 +++++++++++++++ .../src/providers/github/schemas/shared.ts | 366 ++++++++++++++++++ 7 files changed, 710 insertions(+), 262 deletions(-) delete mode 100644 packages/trigger-providers/src/providers/github/schemas.ts create mode 100644 packages/trigger-providers/src/providers/github/schemas/index.ts create mode 100644 packages/trigger-providers/src/providers/github/schemas/issues.ts create mode 100644 packages/trigger-providers/src/providers/github/schemas/shared.ts diff --git a/examples/github-webhook/src/index.ts b/examples/github-webhook/src/index.ts index 5418f6ac7..7664e05f1 100644 --- a/examples/github-webhook/src/index.ts +++ b/examples/github-webhook/src/index.ts @@ -3,28 +3,17 @@ import { github } from "@trigger.dev/integrations"; const trigger = new Trigger({ id: "github-webhook-9", - name: "GitHub Issue changes for trigger.dev", + name: "GitHub changes made to triggerdotdev/trigger.dev-examples", apiKey: "trigger_dev_zC25mKNn6c0q", endpoint: "ws://localhost:8889/ws", logLevel: "debug", - on: github.events.repoIssueEvent({ repo: "triggerdotdev/trigger.dev" }), + on: github.events.repoIssueEvent({ + repo: "triggerdotdev/trigger.dev-examples", + }), run: async (event, ctx) => { - await ctx.logger.info( - "Inside the github-webhook workflow, received event", - { - event, - } - ); + await ctx.logger.info(`Action was ${event.action}`); - if (event.action === "assigned") { - await ctx.logger.info(`New assignee: ${event.assignee.login}`); - // await slack.sendMessage({ - // channel: "C01BQKZJZ7M", - // text: `New issue: ${event.issue.title}`, - // }); - } - - return event; + return {}; }, }); diff --git a/packages/internal-integrations/src/github/index.ts b/packages/internal-integrations/src/github/index.ts index 570ebd0a9..397d46d46 100644 --- a/packages/internal-integrations/src/github/index.ts +++ b/packages/internal-integrations/src/github/index.ts @@ -8,7 +8,6 @@ import { Webhooks } from "@octokit/webhooks"; import { z } from "zod"; import { github } from "@trigger.dev/providers"; import { getAccessToken } from "../accessInfo"; -import fetch from "node-fetch"; export class GitHubWebhookIntegration implements WebhookIntegration { keyForSource(source: unknown): string { diff --git a/packages/trigger-integrations/src/integrations/github/events.ts b/packages/trigger-integrations/src/integrations/github/events.ts index 1703840ee..189616163 100644 --- a/packages/trigger-integrations/src/integrations/github/events.ts +++ b/packages/trigger-integrations/src/integrations/github/events.ts @@ -3,7 +3,7 @@ import { github } from "@trigger.dev/providers"; export function repoIssueEvent(params: { repo: string; -}): TriggerEvent { +}): TriggerEvent { return { metadata: { type: "WEBHOOK", @@ -25,13 +25,13 @@ export function repoIssueEvent(params: { events: ["issues"], }), }, - schema: github.schemas.IssueEventSchema, + schema: github.schemas.issues.issuesEventSchema, }; } export function orgIssueEvent(params: { org: string; -}): TriggerEvent { +}): TriggerEvent { return { metadata: { type: "WEBHOOK", @@ -53,6 +53,6 @@ export function orgIssueEvent(params: { events: ["issues"], }), }, - schema: github.schemas.IssueEventSchema, + schema: github.schemas.issues.issuesEventSchema, }; } diff --git a/packages/trigger-providers/src/providers/github/schemas.ts b/packages/trigger-providers/src/providers/github/schemas.ts deleted file mode 100644 index ab00d0e3a..000000000 --- a/packages/trigger-providers/src/providers/github/schemas.ts +++ /dev/null @@ -1,240 +0,0 @@ -import { z } from "zod"; - -export const WebhookRepoSourceSchema = z.object({ - subresource: z.literal("repository"), - scopes: z.array(z.string()), - repo: z.string(), - events: z.array(z.string()), -}); - -export const WebhookOrganizationSourceSchema = z.object({ - subresource: z.literal("organization"), - scopes: z.array(z.string()), - org: z.string(), - events: z.array(z.string()), -}); - -export const WebhookSourceSchema = z.union([ - WebhookRepoSourceSchema, - WebhookOrganizationSourceSchema, -]); - -const ReactionsSchema = z.object({ - "+1": z.number().int(), - "-1": z.number().int(), - url: z.string().url(), - eyes: z.number().int(), - heart: z.number().int(), - laugh: z.number().int(), - hooray: z.number().int(), - rocket: z.number().int(), - confused: z.number().int(), - total_count: z.number().int(), -}); - -const UserSchema = z.object({ - id: z.number().int(), - url: z.string().url(), - type: z.string(), - login: z.string(), - node_id: z.string(), - html_url: z.string().url(), - gists_url: z.string().url(), - repos_url: z.string().url(), - avatar_url: z.string().url(), - events_url: z.string().url(), - site_admin: z.boolean(), - gravatar_id: z.string(), - starred_url: z.string().url(), - followers_url: z.string().url(), - following_url: z.string().url(), - organizations_url: z.string().url(), - subscriptions_url: z.string().url(), - received_events_url: z.string().url(), -}); - -const RepositorySchema = z.object({ - id: z.number().int(), - url: z.string().url(), - fork: z.boolean(), - name: z.string(), - size: z.number().int(), - forks: z.number().int(), - owner: UserSchema, - topics: z.array(z.string()), - git_url: z.string().url(), - license: z.object({ - key: z.string(), - url: z.string().url(), - name: z.string(), - node_id: z.string(), - spdx_id: z.string(), - }), - node_id: z.string(), - private: z.boolean(), - ssh_url: z.string(), - svn_url: z.string().url(), - archived: z.boolean(), - disabled: z.boolean(), - has_wiki: z.boolean(), - homepage: z.string().url(), - html_url: z.string().url(), - keys_url: z.string().url(), - language: z.string(), - tags_url: z.string().url(), - watchers: z.number().int(), - blobs_url: z.string().url(), - clone_url: z.string().url(), - forks_url: z.string().url(), - full_name: z.string(), - has_pages: z.boolean(), - hooks_url: z.string().url(), - pulls_url: z.string().url(), - pushed_at: z.string(), - teams_url: z.string().url(), - trees_url: z.string().url(), - created_at: z.string(), - events_url: z.string().url(), - has_issues: z.boolean(), - issues_url: z.string().url(), - labels_url: z.string().url(), - merges_url: z.string().url(), - mirror_url: z.null(), - updated_at: z.string(), - visibility: z.string(), - archive_url: z.string().url(), - commits_url: z.string().url(), - compare_url: z.string().url(), - description: z.string(), - forks_count: z.number().int(), - is_template: z.boolean(), - open_issues: z.number().int(), - branches_url: z.string().url(), - comments_url: z.string().url(), - contents_url: z.string().url(), - git_refs_url: z.string().url(), - git_tags_url: z.string().url(), - has_projects: z.boolean(), - releases_url: z.string().url(), - statuses_url: z.string().url(), - allow_forking: z.boolean(), - assignees_url: z.string().url(), - downloads_url: z.string().url(), - has_downloads: z.boolean(), - languages_url: z.string().url(), - default_branch: z.string(), - milestones_url: z.string().url(), - stargazers_url: z.string().url(), - watchers_count: z.number().int(), - deployments_url: z.string().url(), - git_commits_url: z.string().url(), - has_discussions: z.boolean(), - subscribers_url: z.string().url(), - contributors_url: z.string().url(), - issue_events_url: z.string().url(), - stargazers_count: z.number().int(), - subscription_url: z.string().url(), - collaborators_url: z.string().url(), - issue_comment_url: z.string().url(), - notifications_url: z.string().url(), - open_issues_count: z.number().int(), - web_commit_signoff_required: z.boolean(), -}); - -const IssueSchema = z.object({ - id: z.number().int(), - url: z.string().url(), - body: z.string(), - user: UserSchema, - state: z.string(), - title: z.string(), - labels: z.array(z.any()), - locked: z.boolean(), - number: z.number().int(), - node_id: z.string(), - assignee: UserSchema.optional(), - comments: z.number().int(), - html_url: z.string().url(), - assignees: z.array(UserSchema), - closed_at: z.null(), - milestone: z.null(), - reactions: ReactionsSchema, - created_at: z.string(), - events_url: z.string().url(), - labels_url: z.string().url(), - updated_at: z.string(), - comments_url: z.string().url(), - state_reason: z.null(), - timeline_url: z.string().url(), - repository_url: z.string().url(), - active_lock_reason: z.null(), - author_association: z.string(), - performed_via_github_app: z.null(), -}); - -const LabelSchema = z.object({ - id: z.number().int(), - url: z.string().url(), - name: z.string(), - color: z.string(), - default: z.boolean(), - node_id: z.string(), - description: z.string(), -}); - -const OrganizationSchema = z.object({ - id: z.number().int(), - url: z.string().url(), - login: z.string(), - node_id: z.string(), - hooks_url: z.string().url(), - repos_url: z.string().url(), - avatar_url: z.string().url(), - events_url: z.string().url(), - issues_url: z.string().url(), - description: z.string(), - members_url: z.string().url(), - public_members_url: z.string().url(), -}); - -const SharedIssueEventSchema = z.object({ - issue: IssueSchema, - sender: UserSchema, - repository: RepositorySchema, - organization: OrganizationSchema, -}); - -const LabeledIssueEventSchema = z - .object({ - action: z.literal("labeled"), - label: LabelSchema, - }) - .merge(SharedIssueEventSchema); - -const UnlabeledIssueEventSchema = z - .object({ - action: z.literal("unlabeled"), - label: LabelSchema, - }) - .merge(SharedIssueEventSchema); - -const AssignedIssueEventSchema = z - .object({ - action: z.literal("assigned"), - assignee: UserSchema, - }) - .merge(SharedIssueEventSchema); - -const UnassignedIssueEventSchema = z - .object({ - action: z.literal("unassigned"), - assignee: UserSchema, - }) - .merge(SharedIssueEventSchema); - -export const IssueEventSchema = z.discriminatedUnion("action", [ - LabeledIssueEventSchema, - UnlabeledIssueEventSchema, - AssignedIssueEventSchema, - UnassignedIssueEventSchema, -]); diff --git a/packages/trigger-providers/src/providers/github/schemas/index.ts b/packages/trigger-providers/src/providers/github/schemas/index.ts new file mode 100644 index 000000000..a9d505a8e --- /dev/null +++ b/packages/trigger-providers/src/providers/github/schemas/index.ts @@ -0,0 +1,23 @@ +import { z } from "zod"; +import * as issues from "./issues"; + +export const WebhookRepoSourceSchema = z.object({ + subresource: z.literal("repository"), + scopes: z.array(z.string()), + repo: z.string(), + events: z.array(z.string()), +}); + +export const WebhookOrganizationSourceSchema = z.object({ + subresource: z.literal("organization"), + scopes: z.array(z.string()), + org: z.string(), + events: z.array(z.string()), +}); + +export const WebhookSourceSchema = z.union([ + WebhookRepoSourceSchema, + WebhookOrganizationSourceSchema, +]); + +export { issues }; diff --git a/packages/trigger-providers/src/providers/github/schemas/issues.ts b/packages/trigger-providers/src/providers/github/schemas/issues.ts new file mode 100644 index 000000000..224db1a82 --- /dev/null +++ b/packages/trigger-providers/src/providers/github/schemas/issues.ts @@ -0,0 +1,311 @@ +import { z } from "zod"; +import { + appSchema, + authorAssociationSchema, + installationLiteSchema, + labelSchema, + milestoneSchema, + organizationSchema, + reactionsSchema, + repositorySchema, + userSchema, +} from "./shared"; + +export const issueSchema = z.object({ + url: z.string(), + repository_url: z.string(), + labels_url: z.string(), + comments_url: z.string(), + events_url: z.string(), + html_url: z.string(), + id: z.number(), + node_id: z.string(), + number: z.number(), + title: z.string(), + user: userSchema, + labels: z.array(labelSchema).optional(), + state: z.union([z.literal("open"), z.literal("closed")]).optional(), + locked: z.boolean().optional(), + assignee: userSchema.optional().nullable(), + assignees: z.array(userSchema), + milestone: milestoneSchema.nullable(), + comments: z.number(), + created_at: z.string(), + updated_at: z.string(), + closed_at: z.string().nullable(), + author_association: authorAssociationSchema, + active_lock_reason: z + .union([ + z.literal("resolved"), + z.literal("off-topic"), + z.literal("too heated"), + z.literal("spam"), + ]) + .nullable(), + draft: z.boolean().optional(), + performed_via_github_app: appSchema.optional().nullable(), + pull_request: z + .object({ + url: z.string().optional(), + html_url: z.string().optional(), + diff_url: z.string().optional(), + patch_url: z.string().optional(), + merged_at: z.string().optional().nullable(), + }) + .optional(), + body: z.string().nullable(), + reactions: reactionsSchema, + timeline_url: z.string().optional(), + state_reason: z.string().optional().nullable(), +}); + +export const issuesAssignedEventSchema = z.object({ + action: z.literal("assigned"), + issue: issueSchema, + assignee: userSchema.optional().nullable(), + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issuesClosedEventSchema = z.object({ + action: z.literal("closed"), + issue: issueSchema.and( + z.object({ + state: z.literal("closed"), + closed_at: z.string(), + }) + ), + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issuesDeletedEventSchema = z.object({ + action: z.literal("deleted"), + issue: issueSchema, + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issuesDemilestonedEventSchema = z.object({ + action: z.literal("demilestoned"), + issue: issueSchema.and( + z.object({ + milestone: z.null(), + }) + ), + milestone: milestoneSchema, + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issuesEditedEventSchema = z.object({ + action: z.literal("edited"), + issue: issueSchema, + label: labelSchema.optional(), + changes: z.object({ + body: z + .object({ + from: z.string(), + }) + .optional(), + title: z + .object({ + from: z.string(), + }) + .optional(), + }), + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issuesLabeledEventSchema = z.object({ + action: z.literal("labeled"), + issue: issueSchema, + label: labelSchema.optional(), + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issuesLockedEventSchema = z.object({ + action: z.literal("locked"), + issue: issueSchema.and( + z.object({ + locked: z.literal(true), + active_lock_reason: z + .union([ + z.literal("resolved"), + z.literal("off-topic"), + z.literal("too heated"), + z.literal("spam"), + ]) + .nullable(), + }) + ), + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issuesMilestonedEventSchema = z.object({ + action: z.literal("milestoned"), + issue: issueSchema.and( + z.object({ + milestone: milestoneSchema, + }) + ), + milestone: milestoneSchema, + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issuesOpenedEventSchema = z.object({ + action: z.literal("opened"), + changes: z + .object({ + old_issue: issueSchema, + old_repository: repositorySchema, + }) + .optional(), + issue: issueSchema.and( + z.object({ + state: z.literal("open"), + closed_at: z.null(), + }) + ), + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issuesPinnedEventSchema = z.object({ + action: z.literal("pinned"), + issue: issueSchema, + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issuesReopenedEventSchema = z.object({ + action: z.literal("reopened"), + issue: issueSchema.and( + z.object({ + state: z.literal("open"), + }) + ), + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issuesTransferredEventSchema = z.object({ + action: z.literal("transferred"), + changes: z.object({ + new_issue: issueSchema, + new_repository: repositorySchema, + }), + issue: issueSchema, + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issuesUnassignedEventSchema = z.object({ + action: z.literal("unassigned"), + issue: issueSchema, + assignee: userSchema.optional().nullable(), + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issuesUnlabeledEventSchema = z.object({ + action: z.literal("unlabeled"), + issue: issueSchema, + label: labelSchema.optional(), + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issuesUnlockedEventSchema = z.object({ + action: z.literal("unlocked"), + issue: issueSchema.and( + z.object({ + locked: z.literal(false), + active_lock_reason: z.null(), + }) + ), + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issuesUnpinnedEventSchema = z.object({ + action: z.literal("unpinned"), + issue: issueSchema, + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issuesEventSchema: z.ZodDiscriminatedUnion< + "action", + [ + typeof issuesAssignedEventSchema, + typeof issuesClosedEventSchema, + typeof issuesDeletedEventSchema, + typeof issuesDemilestonedEventSchema, + typeof issuesEditedEventSchema, + typeof issuesLabeledEventSchema, + typeof issuesLockedEventSchema, + typeof issuesMilestonedEventSchema, + typeof issuesOpenedEventSchema, + typeof issuesPinnedEventSchema, + typeof issuesReopenedEventSchema, + typeof issuesTransferredEventSchema, + typeof issuesUnassignedEventSchema, + typeof issuesUnlabeledEventSchema, + typeof issuesUnlockedEventSchema, + typeof issuesUnpinnedEventSchema + ] +> = z.discriminatedUnion("action", [ + issuesAssignedEventSchema, + issuesClosedEventSchema, + issuesDeletedEventSchema, + issuesDemilestonedEventSchema, + issuesEditedEventSchema, + issuesLabeledEventSchema, + issuesLockedEventSchema, + issuesMilestonedEventSchema, + issuesOpenedEventSchema, + issuesPinnedEventSchema, + issuesReopenedEventSchema, + issuesTransferredEventSchema, + issuesUnassignedEventSchema, + issuesUnlabeledEventSchema, + issuesUnlockedEventSchema, + issuesUnpinnedEventSchema, +]); diff --git a/packages/trigger-providers/src/providers/github/schemas/shared.ts b/packages/trigger-providers/src/providers/github/schemas/shared.ts new file mode 100644 index 000000000..28278e662 --- /dev/null +++ b/packages/trigger-providers/src/providers/github/schemas/shared.ts @@ -0,0 +1,366 @@ +import { z } from "zod"; + +export const userSchema = z.object({ + login: z.string(), + id: z.number(), + node_id: z.string(), + name: z.string().optional(), + email: z.string().optional().nullable(), + avatar_url: z.string(), + gravatar_id: z.string(), + url: z.string(), + html_url: z.string(), + followers_url: z.string(), + following_url: z.string(), + gists_url: z.string(), + starred_url: z.string(), + subscriptions_url: z.string(), + organizations_url: z.string(), + repos_url: z.string(), + events_url: z.string(), + received_events_url: z.string(), + type: z.union([ + z.literal("Bot"), + z.literal("User"), + z.literal("Organization"), + ]), + site_admin: z.boolean(), +}); + +export const licenseSchema = z.object({ + key: z.string(), + name: z.string(), + spdx_id: z.string(), + url: z.string().nullable(), + node_id: z.string(), +}); + +export const installationLiteSchema = z.object({ + id: z.number(), + node_id: z.string(), +}); + +export const organizationSchema = z.object({ + login: z.string(), + id: z.number(), + node_id: z.string(), + url: z.string(), + html_url: z.string().optional(), + repos_url: z.string(), + events_url: z.string(), + hooks_url: z.string(), + issues_url: z.string(), + members_url: z.string(), + public_members_url: z.string(), + avatar_url: z.string(), + description: z.string().nullable(), +}); + +export const repositorySchema = z.object({ + id: z.number(), + node_id: z.string(), + name: z.string(), + full_name: z.string(), + private: z.boolean(), + owner: userSchema, + html_url: z.string(), + description: z.string().nullable(), + fork: z.boolean(), + url: z.string(), + forks_url: z.string(), + keys_url: z.string(), + collaborators_url: z.string(), + teams_url: z.string(), + hooks_url: z.string(), + issue_events_url: z.string(), + events_url: z.string(), + assignees_url: z.string(), + branches_url: z.string(), + tags_url: z.string(), + blobs_url: z.string(), + git_tags_url: z.string(), + git_refs_url: z.string(), + trees_url: z.string(), + statuses_url: z.string(), + languages_url: z.string(), + stargazers_url: z.string(), + contributors_url: z.string(), + subscribers_url: z.string(), + subscription_url: z.string(), + commits_url: z.string(), + git_commits_url: z.string(), + comments_url: z.string(), + issue_comment_url: z.string(), + contents_url: z.string(), + compare_url: z.string(), + merges_url: z.string(), + archive_url: z.string(), + downloads_url: z.string(), + issues_url: z.string(), + pulls_url: z.string(), + milestones_url: z.string(), + notifications_url: z.string(), + labels_url: z.string(), + releases_url: z.string(), + deployments_url: z.string(), + created_at: z.union([z.number(), z.string()]), + updated_at: z.string(), + pushed_at: z.union([z.number(), z.string()]).nullable(), + git_url: z.string(), + ssh_url: z.string(), + clone_url: z.string(), + svn_url: z.string(), + homepage: z.string().nullable(), + size: z.number(), + stargazers_count: z.number(), + watchers_count: z.number(), + language: z.string().nullable(), + has_issues: z.boolean(), + has_projects: z.boolean(), + has_downloads: z.boolean(), + has_wiki: z.boolean(), + has_pages: z.boolean(), + forks_count: z.number(), + mirror_url: z.string().nullable(), + archived: z.boolean(), + disabled: z.boolean().optional(), + open_issues_count: z.number(), + license: licenseSchema.nullable(), + forks: z.number(), + open_issues: z.number(), + watchers: z.number(), + stargazers: z.number().optional(), + default_branch: z.string(), + allow_squash_merge: z.boolean().optional(), + allow_merge_commit: z.boolean().optional(), + allow_rebase_merge: z.boolean().optional(), + allow_auto_merge: z.boolean().optional(), + allow_forking: z.boolean().optional(), + allow_update_branch: z.boolean().optional(), + use_squash_pr_title_as_default: z.boolean().optional(), + squash_merge_commit_message: z.string().optional(), + squash_merge_commit_title: z.string().optional(), + merge_commit_message: z.string().optional(), + merge_commit_title: z.string().optional(), + is_template: z.boolean(), + web_commit_signoff_required: z.boolean(), + topics: z.array(z.string()), + visibility: z.union([ + z.literal("public"), + z.literal("private"), + z.literal("internal"), + ]), + delete_branch_on_merge: z.boolean().optional(), + master_branch: z.string().optional(), + permissions: z + .object({ + pull: z.boolean(), + push: z.boolean(), + admin: z.boolean(), + maintain: z.boolean().optional(), + triage: z.boolean().optional(), + }) + .optional(), + public: z.boolean().optional(), + organization: z.string().optional(), +}); + +export const appSchema = z.object({ + id: z.number(), + slug: z.string().optional(), + node_id: z.string(), + owner: userSchema, + name: z.string(), + description: z.string().nullable(), + external_url: z.string(), + html_url: z.string(), + created_at: z.string(), + updated_at: z.string(), + permissions: z + .object({ + actions: z.union([z.literal("read"), z.literal("write")]).optional(), + administration: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + blocking: z.union([z.literal("read"), z.literal("write")]).optional(), + checks: z.union([z.literal("read"), z.literal("write")]).optional(), + content_references: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + contents: z.union([z.literal("read"), z.literal("write")]).optional(), + deployments: z.union([z.literal("read"), z.literal("write")]).optional(), + discussions: z.union([z.literal("read"), z.literal("write")]).optional(), + emails: z.union([z.literal("read"), z.literal("write")]).optional(), + environments: z.union([z.literal("read"), z.literal("write")]).optional(), + issues: z.union([z.literal("read"), z.literal("write")]).optional(), + keys: z.union([z.literal("read"), z.literal("write")]).optional(), + members: z.union([z.literal("read"), z.literal("write")]).optional(), + merge_queues: z.union([z.literal("read"), z.literal("write")]).optional(), + metadata: z.union([z.literal("read"), z.literal("write")]).optional(), + organization_administration: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + organization_hooks: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + organization_packages: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + organization_plan: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + organization_projects: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + organization_secrets: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + organization_self_hosted_runners: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + organization_user_blocking: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + packages: z.union([z.literal("read"), z.literal("write")]).optional(), + pages: z.union([z.literal("read"), z.literal("write")]).optional(), + pull_requests: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + repository_hooks: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + repository_projects: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + secret_scanning_alerts: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + secrets: z.union([z.literal("read"), z.literal("write")]).optional(), + security_events: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + security_scanning_alert: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + single_file: z.union([z.literal("read"), z.literal("write")]).optional(), + statuses: z.union([z.literal("read"), z.literal("write")]).optional(), + team_discussions: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + vulnerability_alerts: z + .union([z.literal("read"), z.literal("write")]) + .optional(), + workflows: z.union([z.literal("read"), z.literal("write")]).optional(), + }) + .optional(), + events: z + .array( + z.union([ + z.literal("branch_protection_rule"), + z.literal("check_run"), + z.literal("check_suite"), + z.literal("code_scanning_alert"), + z.literal("commit_comment"), + z.literal("content_reference"), + z.literal("create"), + z.literal("delete"), + z.literal("deployment"), + z.literal("deployment_review"), + z.literal("deployment_status"), + z.literal("deploy_key"), + z.literal("discussion"), + z.literal("discussion_comment"), + z.literal("fork"), + z.literal("gollum"), + z.literal("issues"), + z.literal("issue_comment"), + z.literal("label"), + z.literal("member"), + z.literal("membership"), + z.literal("merge_group"), + z.literal("merge_queue_entry"), + z.literal("milestone"), + z.literal("organization"), + z.literal("org_block"), + z.literal("page_build"), + z.literal("project"), + z.literal("projects_v2_item"), + z.literal("project_card"), + z.literal("project_column"), + z.literal("public"), + z.literal("pull_request"), + z.literal("pull_request_review"), + z.literal("pull_request_review_comment"), + z.literal("push"), + z.literal("registry_package"), + z.literal("release"), + z.literal("repository"), + z.literal("repository_dispatch"), + z.literal("secret_scanning_alert"), + z.literal("secret_scanning_alert_location"), + z.literal("security_and_analysis"), + z.literal("star"), + z.literal("status"), + z.literal("team"), + z.literal("team_add"), + z.literal("watch"), + z.literal("workflow_dispatch"), + z.literal("workflow_run"), + z.literal("workflow_job"), + ]) + ) + .optional(), +}); + +export const reactionsSchema = z.object({ + url: z.string(), + total_count: z.number(), + "+1": z.number(), + "-1": z.number(), + laugh: z.number(), + hooray: z.number(), + confused: z.number(), + heart: z.number(), + rocket: z.number(), + eyes: z.number(), +}); + +export const authorAssociationSchema = z.union([ + z.literal("COLLABORATOR"), + z.literal("CONTRIBUTOR"), + z.literal("FIRST_TIMER"), + z.literal("FIRST_TIME_CONTRIBUTOR"), + z.literal("MANNEQUIN"), + z.literal("MEMBER"), + z.literal("NONE"), + z.literal("OWNER"), +]); + +export const milestoneSchema = z.object({ + url: z.string(), + html_url: z.string(), + labels_url: z.string(), + id: z.number(), + node_id: z.string(), + number: z.number(), + title: z.string(), + description: z.string().nullable(), + creator: userSchema, + open_issues: z.number(), + closed_issues: z.number(), + state: z.union([z.literal("open"), z.literal("closed")]), + created_at: z.string(), + updated_at: z.string(), + due_on: z.string().nullable(), + closed_at: z.string().nullable(), +}); + +export const labelSchema = z.object({ + id: z.number(), + node_id: z.string(), + url: z.string(), + name: z.string(), + description: z.string().nullable(), + color: z.string(), + default: z.boolean(), +}); From f9eabe3dfb7db0cdc3e08b34126bd79f45f3ba67 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Wed, 18 Jan 2023 20:11:17 -0800 Subject: [PATCH 20/45] Added GitHub issue commented webhook --- examples/github-webhook/src/index.ts | 24 ++++- .../internal-integrations/src/github/index.ts | 4 +- .../src/integrations/github/events.ts | 24 ++--- .../src/providers/github/schemas/index.ts | 3 +- .../providers/github/schemas/issueComments.ts | 91 +++++++++++++++++++ 5 files changed, 127 insertions(+), 19 deletions(-) create mode 100644 packages/trigger-providers/src/providers/github/schemas/issueComments.ts diff --git a/examples/github-webhook/src/index.ts b/examples/github-webhook/src/index.ts index 7664e05f1..20c014acd 100644 --- a/examples/github-webhook/src/index.ts +++ b/examples/github-webhook/src/index.ts @@ -1,13 +1,13 @@ import { Trigger } from "@trigger.dev/sdk"; import { github } from "@trigger.dev/integrations"; -const trigger = new Trigger({ +new Trigger({ id: "github-webhook-9", - name: "GitHub changes made to triggerdotdev/trigger.dev-examples", + name: "GitHub issue: triggerdotdev/trigger.dev-examples", apiKey: "trigger_dev_zC25mKNn6c0q", endpoint: "ws://localhost:8889/ws", logLevel: "debug", - on: github.events.repoIssueEvent({ + on: github.events.issueEvent({ repo: "triggerdotdev/trigger.dev-examples", }), run: async (event, ctx) => { @@ -15,6 +15,20 @@ const trigger = new Trigger({ return {}; }, -}); +}).listen(); -trigger.listen(); +new Trigger({ + id: "github-webhook-issue_comment", + name: "GitHub issue comment: triggerdotdev/trigger.dev-examples", + apiKey: "trigger_dev_zC25mKNn6c0q", + endpoint: "ws://localhost:8889/ws", + logLevel: "debug", + on: github.events.issueCommentEvent({ + repo: "triggerdotdev/trigger.dev-examples", + }), + run: async (event, ctx) => { + await ctx.logger.info(`Action was ${event.action}`); + + return {}; + }, +}).listen(); diff --git a/packages/internal-integrations/src/github/index.ts b/packages/internal-integrations/src/github/index.ts index 397d46d46..3b3a71cd9 100644 --- a/packages/internal-integrations/src/github/index.ts +++ b/packages/internal-integrations/src/github/index.ts @@ -14,7 +14,9 @@ export class GitHubWebhookIntegration implements WebhookIntegration { const githubSource = parseWebhookSource(source); if (githubSource.subresource === "repository") { - return `repository.${githubSource.repo}`; + return `repository.${githubSource.repo}.${githubSource.events.join( + "." + )}}`; } else if (githubSource.subresource === "organization") { return `organization.${githubSource.org}`; } else { diff --git a/packages/trigger-integrations/src/integrations/github/events.ts b/packages/trigger-integrations/src/integrations/github/events.ts index 189616163..f9ca0a0a4 100644 --- a/packages/trigger-integrations/src/integrations/github/events.ts +++ b/packages/trigger-integrations/src/integrations/github/events.ts @@ -1,7 +1,7 @@ import { TriggerEvent } from "@trigger.dev/sdk"; import { github } from "@trigger.dev/providers"; -export function repoIssueEvent(params: { +export function issueEvent(params: { repo: string; }): TriggerEvent { return { @@ -29,30 +29,30 @@ export function repoIssueEvent(params: { }; } -export function orgIssueEvent(params: { - org: string; -}): TriggerEvent { +export function issueCommentEvent(params: { + repo: string; +}): TriggerEvent { return { metadata: { type: "WEBHOOK", service: "github", - name: "issues", + name: "issue_comment", filter: { service: ["github"], payload: { - organizaton: { - name: [params.org], + repository: { + full_name: [params.repo], }, }, - event: ["issues"], + event: ["issue_comment"], }, source: github.schemas.WebhookSourceSchema.parse({ - subresource: "organization", + subresource: "repository", scopes: ["repo"], - org: params.org, - events: ["issues"], + repo: params.repo, + events: ["issue_comment"], }), }, - schema: github.schemas.issues.issuesEventSchema, + schema: github.schemas.issuesComments.issueCommentEventSchema, }; } diff --git a/packages/trigger-providers/src/providers/github/schemas/index.ts b/packages/trigger-providers/src/providers/github/schemas/index.ts index a9d505a8e..9994968f1 100644 --- a/packages/trigger-providers/src/providers/github/schemas/index.ts +++ b/packages/trigger-providers/src/providers/github/schemas/index.ts @@ -1,5 +1,6 @@ import { z } from "zod"; import * as issues from "./issues"; +import * as issuesComments from "./issueComments"; export const WebhookRepoSourceSchema = z.object({ subresource: z.literal("repository"), @@ -20,4 +21,4 @@ export const WebhookSourceSchema = z.union([ WebhookOrganizationSourceSchema, ]); -export { issues }; +export { issues, issuesComments }; diff --git a/packages/trigger-providers/src/providers/github/schemas/issueComments.ts b/packages/trigger-providers/src/providers/github/schemas/issueComments.ts new file mode 100644 index 000000000..c19bfe134 --- /dev/null +++ b/packages/trigger-providers/src/providers/github/schemas/issueComments.ts @@ -0,0 +1,91 @@ +import { z } from "zod"; +import { issueSchema } from "./issues"; +import { + appSchema, + authorAssociationSchema, + installationLiteSchema, + labelSchema, + organizationSchema, + reactionsSchema, + repositorySchema, + userSchema, +} from "./shared"; + +export const issueCommentSchema = z.object({ + url: z.string(), + html_url: z.string(), + issue_url: z.string(), + id: z.number(), + node_id: z.string(), + user: userSchema, + created_at: z.string(), + updated_at: z.string(), + author_association: authorAssociationSchema, + body: z.string(), + reactions: reactionsSchema, + performed_via_github_app: appSchema.nullable(), +}); + +export const issueCommentCreatedEventSchema = z.object({ + action: z.literal("created"), + issue: issueSchema.and( + z.object({ + assignee: userSchema.nullable(), + state: z.union([z.literal("open"), z.literal("closed")]), + locked: z.boolean(), + labels: z.array(labelSchema), + }) + ), + comment: issueCommentSchema, + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issueCommentDeletedEventSchema = z.object({ + action: z.literal("deleted"), + issue: issueSchema.and( + z.object({ + assignee: userSchema.nullable(), + state: z.union([z.literal("open"), z.literal("closed")]), + locked: z.boolean(), + labels: z.array(labelSchema), + }) + ), + comment: issueCommentSchema, + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issueCommentEditedEventSchema = z.object({ + action: z.literal("edited"), + changes: z.object({ + body: z + .object({ + from: z.string(), + }) + .optional(), + }), + issue: issueSchema.and( + z.object({ + assignee: userSchema.nullable(), + state: z.union([z.literal("open"), z.literal("closed")]), + locked: z.boolean(), + labels: z.array(labelSchema), + }) + ), + comment: issueCommentSchema, + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); + +export const issueCommentEventSchema = z.union([ + issueCommentCreatedEventSchema, + issueCommentDeletedEventSchema, + issueCommentEditedEventSchema, +]); From ef3ce5dbc2f6fd7d6dabfea82914f26fe04c739d Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Thu, 19 Jan 2023 09:46:13 -0800 Subject: [PATCH 21/45] Added GitHub Pull Request webhook support --- examples/github-webhook/src/index.ts | 6 +- .../src/integrations/github/events.ts | 28 ++ .../src/providers/github/schemas/index.ts | 3 +- .../providers/github/schemas/issueComments.ts | 2 +- .../providers/github/schemas/pullRequests.ts | 411 ++++++++++++++++++ .../src/providers/github/schemas/shared.ts | 42 ++ 6 files changed, 487 insertions(+), 5 deletions(-) create mode 100644 packages/trigger-providers/src/providers/github/schemas/pullRequests.ts diff --git a/examples/github-webhook/src/index.ts b/examples/github-webhook/src/index.ts index 20c014acd..b9d7745c9 100644 --- a/examples/github-webhook/src/index.ts +++ b/examples/github-webhook/src/index.ts @@ -18,12 +18,12 @@ new Trigger({ }).listen(); new Trigger({ - id: "github-webhook-issue_comment", - name: "GitHub issue comment: triggerdotdev/trigger.dev-examples", + id: "github-webhook-pull_request", + name: "GitHub PR: triggerdotdev/trigger.dev-examples", apiKey: "trigger_dev_zC25mKNn6c0q", endpoint: "ws://localhost:8889/ws", logLevel: "debug", - on: github.events.issueCommentEvent({ + on: github.events.pullRequestEvent({ repo: "triggerdotdev/trigger.dev-examples", }), run: async (event, ctx) => { diff --git a/packages/trigger-integrations/src/integrations/github/events.ts b/packages/trigger-integrations/src/integrations/github/events.ts index f9ca0a0a4..3a23b1e10 100644 --- a/packages/trigger-integrations/src/integrations/github/events.ts +++ b/packages/trigger-integrations/src/integrations/github/events.ts @@ -56,3 +56,31 @@ export function issueCommentEvent(params: { schema: github.schemas.issuesComments.issueCommentEventSchema, }; } + +export function pullRequestEvent(params: { + repo: string; +}): TriggerEvent { + return { + metadata: { + type: "WEBHOOK", + service: "github", + name: "pull_request", + filter: { + service: ["github"], + payload: { + repository: { + full_name: [params.repo], + }, + }, + event: ["pull_request"], + }, + source: github.schemas.WebhookSourceSchema.parse({ + subresource: "repository", + scopes: ["repo"], + repo: params.repo, + events: ["pull_request"], + }), + }, + schema: github.schemas.pullRequest.pullRequestEventSchema, + }; +} diff --git a/packages/trigger-providers/src/providers/github/schemas/index.ts b/packages/trigger-providers/src/providers/github/schemas/index.ts index 9994968f1..21f29812e 100644 --- a/packages/trigger-providers/src/providers/github/schemas/index.ts +++ b/packages/trigger-providers/src/providers/github/schemas/index.ts @@ -1,6 +1,7 @@ import { z } from "zod"; import * as issues from "./issues"; import * as issuesComments from "./issueComments"; +import * as pullRequest from "./pullRequests"; export const WebhookRepoSourceSchema = z.object({ subresource: z.literal("repository"), @@ -21,4 +22,4 @@ export const WebhookSourceSchema = z.union([ WebhookOrganizationSourceSchema, ]); -export { issues, issuesComments }; +export { issues, issuesComments, pullRequest }; diff --git a/packages/trigger-providers/src/providers/github/schemas/issueComments.ts b/packages/trigger-providers/src/providers/github/schemas/issueComments.ts index c19bfe134..8be801aad 100644 --- a/packages/trigger-providers/src/providers/github/schemas/issueComments.ts +++ b/packages/trigger-providers/src/providers/github/schemas/issueComments.ts @@ -84,7 +84,7 @@ export const issueCommentEditedEventSchema = z.object({ organization: organizationSchema.optional(), }); -export const issueCommentEventSchema = z.union([ +export const issueCommentEventSchema = z.discriminatedUnion("action", [ issueCommentCreatedEventSchema, issueCommentDeletedEventSchema, issueCommentEditedEventSchema, diff --git a/packages/trigger-providers/src/providers/github/schemas/pullRequests.ts b/packages/trigger-providers/src/providers/github/schemas/pullRequests.ts new file mode 100644 index 000000000..a55e85e81 --- /dev/null +++ b/packages/trigger-providers/src/providers/github/schemas/pullRequests.ts @@ -0,0 +1,411 @@ +import { z } from "zod"; +import { + authorAssociationSchema, + installationLiteSchema, + labelSchema, + linkSchema, + milestoneSchema, + organizationSchema, + repositorySchema, + teamSchema, + userSchema, +} from "./shared"; + +export const pullRequestAutoMergeSchema = z.object({ + enabled_by: userSchema, + merge_method: z.union([ + z.literal("merge"), + z.literal("squash"), + z.literal("rebase"), + ]), + commit_title: z.string(), + commit_message: z.string(), +}); + +export const pullRequestSchema = z.object({ + url: z.string(), + id: z.number(), + node_id: z.string(), + html_url: z.string(), + diff_url: z.string(), + patch_url: z.string(), + issue_url: z.string(), + number: z.number(), + state: z.union([z.literal("open"), z.literal("closed")]), + locked: z.boolean(), + title: z.string(), + user: userSchema, + body: z.string().nullable(), + created_at: z.string(), + updated_at: z.string(), + closed_at: z.string().nullable(), + merged_at: z.string().nullable(), + merge_commit_sha: z.string().nullable(), + assignee: userSchema.nullable(), + assignees: z.array(userSchema), + requested_reviewers: z.array(z.union([userSchema, teamSchema])), + requested_teams: z.array(teamSchema), + labels: z.array(labelSchema), + milestone: milestoneSchema.nullable(), + commits_url: z.string(), + review_comments_url: z.string(), + review_comment_url: z.string(), + comments_url: z.string(), + statuses_url: z.string(), + head: z.object({ + label: z.string(), + ref: z.string(), + sha: z.string(), + user: userSchema, + repo: repositorySchema, + }), + base: z.object({ + label: z.string(), + ref: z.string(), + sha: z.string(), + user: userSchema, + repo: repositorySchema, + }), + _links: z.object({ + self: linkSchema, + html: linkSchema, + issue: linkSchema, + comments: linkSchema, + review_comments: linkSchema, + review_comment: linkSchema, + commits: linkSchema, + statuses: linkSchema, + }), + author_association: authorAssociationSchema, + auto_merge: pullRequestAutoMergeSchema.nullable(), + active_lock_reason: z + .union([ + z.literal("resolved"), + z.literal("off-topic"), + z.literal("too heated"), + z.literal("spam"), + ]) + .nullable(), + draft: z.boolean(), + merged: z.boolean().nullable(), + mergeable: z.boolean().nullable(), + rebaseable: z.boolean().nullable(), + mergeable_state: z.string(), + merged_by: userSchema.nullable(), + comments: z.number(), + review_comments: z.number(), + maintainer_can_modify: z.boolean(), + commits: z.number(), + additions: z.number(), + deletions: z.number(), + changed_files: z.number(), +}); + +export const pullRequestAssignedEventSchema = z.object({ + action: z.literal("assigned"), + number: z.number(), + pull_request: pullRequestSchema, + assignee: userSchema, + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestAutoMergeDisabledEventSchema = z.object({ + action: z.literal("auto_merge_disabled"), + number: z.number(), + pull_request: pullRequestSchema, + reason: z.string(), + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestAutoMergeEnabledEventSchema = z.object({ + action: z.literal("auto_merge_enabled"), + number: z.number(), + pull_request: pullRequestSchema, + reason: z.string(), + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestClosedEventSchema = z.object({ + action: z.literal("closed"), + number: z.number(), + pull_request: pullRequestSchema.and( + z.object({ + state: z.literal("closed"), + closed_at: z.string(), + merged: z.boolean(), + }) + ), + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestConvertedToDraftEventSchema = z.object({ + action: z.literal("converted_to_draft"), + number: z.number(), + pull_request: pullRequestSchema.and( + z.object({ + closed_at: z.null(), + merged_at: z.null(), + draft: z.literal(true), + merged: z.literal(false), + merged_by: z.null(), + }) + ), + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestDequeuedEventSchema = z.object({ + action: z.literal("dequeued"), + number: z.number(), + reason: z.string(), + pull_request: pullRequestSchema, + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestEditedEventSchema = z.object({ + action: z.literal("edited"), + number: z.number(), + changes: z.object({ + body: z + .object({ + from: z.string(), + }) + .optional(), + title: z + .object({ + from: z.string(), + }) + .optional(), + base: z + .object({ + ref: z.object({ + from: z.string(), + }), + sha: z.object({ + from: z.string(), + }), + }) + .optional(), + }), + pull_request: pullRequestSchema, + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestLabeledEventSchema = z.object({ + action: z.literal("labeled"), + number: z.number(), + pull_request: pullRequestSchema, + label: labelSchema, + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestLockedEventSchema = z.object({ + action: z.literal("locked"), + number: z.number(), + pull_request: pullRequestSchema, + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestOpenedEventSchema = z.object({ + action: z.literal("opened"), + number: z.number(), + pull_request: pullRequestSchema.and( + z.object({ + state: z.literal("open"), + closed_at: z.null(), + merged_at: z.null(), + active_lock_reason: z.null(), + merged_by: z.null(), + }) + ), + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestQueuedEventSchema = z.object({ + action: z.literal("queued"), + number: z.number(), + pull_request: pullRequestSchema, + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestReadyForReviewEventSchema = z.object({ + action: z.literal("ready_for_review"), + number: z.number(), + pull_request: pullRequestSchema.and( + z.object({ + state: z.literal("open"), + closed_at: z.null(), + merged_at: z.null(), + draft: z.literal(false), + merged: z.boolean(), + merged_by: z.null(), + }) + ), + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestReopenedEventSchema = z.object({ + action: z.literal("reopened"), + number: z.number(), + pull_request: pullRequestSchema.and( + z.object({ + state: z.literal("open"), + closed_at: z.null(), + merged_at: z.null(), + merged: z.boolean(), + merged_by: z.null(), + }) + ), + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestSynchronizeEventSchema = z.object({ + action: z.literal("synchronize"), + number: z.number(), + before: z.string(), + after: z.string(), + pull_request: pullRequestSchema, + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestUnassignedEventSchema = z.object({ + action: z.literal("unassigned"), + number: z.number(), + pull_request: pullRequestSchema, + assignee: userSchema, + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestUnlabeledEventSchema = z.object({ + action: z.literal("unlabeled"), + number: z.number(), + pull_request: pullRequestSchema, + label: labelSchema, + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestUnlockedEventSchema = z.object({ + action: z.literal("unlocked"), + number: z.number(), + pull_request: pullRequestSchema, + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestReviewRequestRemovedEventSchema = z.object({ + action: z.literal("review_request_removed"), + number: z.number(), + pull_request: pullRequestSchema, + requested_reviewer: userSchema.optional(), + requested_team: teamSchema.optional(), + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestReviewRequestedEventSchema = z.object({ + action: z.literal("review_requested"), + number: z.number(), + pull_request: pullRequestSchema, + requested_reviewer: userSchema.optional(), + requested_team: teamSchema.optional(), + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestEventSchema: z.ZodDiscriminatedUnion< + "action", + [ + typeof pullRequestAssignedEventSchema, + typeof pullRequestAutoMergeDisabledEventSchema, + typeof pullRequestAutoMergeEnabledEventSchema, + typeof pullRequestClosedEventSchema, + typeof pullRequestConvertedToDraftEventSchema, + typeof pullRequestDequeuedEventSchema, + typeof pullRequestEditedEventSchema, + typeof pullRequestLabeledEventSchema, + typeof pullRequestLockedEventSchema, + typeof pullRequestOpenedEventSchema, + typeof pullRequestQueuedEventSchema, + typeof pullRequestReadyForReviewEventSchema, + typeof pullRequestReopenedEventSchema, + typeof pullRequestReviewRequestRemovedEventSchema, + typeof pullRequestReviewRequestedEventSchema, + typeof pullRequestSynchronizeEventSchema, + typeof pullRequestUnassignedEventSchema, + typeof pullRequestUnlabeledEventSchema, + typeof pullRequestUnlockedEventSchema + ] +> = z.discriminatedUnion("action", [ + pullRequestAssignedEventSchema, + pullRequestAutoMergeDisabledEventSchema, + pullRequestAutoMergeEnabledEventSchema, + pullRequestClosedEventSchema, + pullRequestConvertedToDraftEventSchema, + pullRequestDequeuedEventSchema, + pullRequestEditedEventSchema, + pullRequestLabeledEventSchema, + pullRequestLockedEventSchema, + pullRequestOpenedEventSchema, + pullRequestQueuedEventSchema, + pullRequestReadyForReviewEventSchema, + pullRequestReopenedEventSchema, + pullRequestReviewRequestRemovedEventSchema, + pullRequestReviewRequestedEventSchema, + pullRequestSynchronizeEventSchema, + pullRequestUnassignedEventSchema, + pullRequestUnlabeledEventSchema, + pullRequestUnlockedEventSchema, +]); diff --git a/packages/trigger-providers/src/providers/github/schemas/shared.ts b/packages/trigger-providers/src/providers/github/schemas/shared.ts index 28278e662..8c14efaac 100644 --- a/packages/trigger-providers/src/providers/github/schemas/shared.ts +++ b/packages/trigger-providers/src/providers/github/schemas/shared.ts @@ -165,6 +165,48 @@ export const repositorySchema = z.object({ organization: z.string().optional(), }); +export const teamSchema = z.object({ + name: z.string(), + id: z.number(), + node_id: z.string(), + slug: z.string(), + description: z.string().nullable(), + privacy: z.union([ + z.literal("open"), + z.literal("closed"), + z.literal("secret"), + ]), + url: z.string(), + html_url: z.string(), + members_url: z.string(), + repositories_url: z.string(), + permission: z.string(), + parent: z + .object({ + name: z.string(), + id: z.number(), + node_id: z.string(), + slug: z.string(), + description: z.string().nullable(), + privacy: z.union([ + z.literal("open"), + z.literal("closed"), + z.literal("secret"), + ]), + url: z.string(), + html_url: z.string(), + members_url: z.string(), + repositories_url: z.string(), + permission: z.string(), + }) + .optional() + .nullable(), +}); + +export const linkSchema = z.object({ + href: z.string(), +}); + export const appSchema = z.object({ id: z.number(), slug: z.string().optional(), From 6c05e6ae52a076b62c40179cfb8f4b87df54a198 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Thu, 19 Jan 2023 10:05:05 -0800 Subject: [PATCH 22/45] Workflows are listed alphabetically on the org page --- apps/webapp/app/models/workflowListPresenter.server.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/webapp/app/models/workflowListPresenter.server.ts b/apps/webapp/app/models/workflowListPresenter.server.ts index e614e554a..88afd9b69 100644 --- a/apps/webapp/app/models/workflowListPresenter.server.ts +++ b/apps/webapp/app/models/workflowListPresenter.server.ts @@ -79,6 +79,7 @@ function getWorkflows(prismaClient: PrismaClient, organizationSlug: string) { orderBy: { finishedAt: { sort: "desc", nulls: "last" } }, }, }, + orderBy: { title: "asc" }, }); } From e60d8198cd55ebb9487f2f69981d5ed4e976f315 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Thu, 19 Jan 2023 10:30:07 -0800 Subject: [PATCH 23/45] Added GitHub Pull Request review comments --- examples/github-webhook/src/index.ts | 16 + .../src/integrations/github/events.ts | 31 ++ .../src/providers/github/schemas/index.ts | 3 +- .../github/schemas/pullRequestComments.ts | 285 ++++++++++++++++++ 4 files changed, 334 insertions(+), 1 deletion(-) create mode 100644 packages/trigger-providers/src/providers/github/schemas/pullRequestComments.ts diff --git a/examples/github-webhook/src/index.ts b/examples/github-webhook/src/index.ts index b9d7745c9..9203ae8ac 100644 --- a/examples/github-webhook/src/index.ts +++ b/examples/github-webhook/src/index.ts @@ -32,3 +32,19 @@ new Trigger({ return {}; }, }).listen(); + +new Trigger({ + id: "github-webhook-pull_request_comment", + name: "GitHub PR comment: triggerdotdev/trigger.dev-examples", + apiKey: "trigger_dev_zC25mKNn6c0q", + endpoint: "ws://localhost:8889/ws", + logLevel: "debug", + on: github.events.pullRequestCommentEvent({ + repo: "triggerdotdev/trigger.dev-examples", + }), + run: async (event, ctx) => { + await ctx.logger.info(`Action was ${event.action}`); + + return {}; + }, +}).listen(); diff --git a/packages/trigger-integrations/src/integrations/github/events.ts b/packages/trigger-integrations/src/integrations/github/events.ts index 3a23b1e10..f91069708 100644 --- a/packages/trigger-integrations/src/integrations/github/events.ts +++ b/packages/trigger-integrations/src/integrations/github/events.ts @@ -84,3 +84,34 @@ export function pullRequestEvent(params: { schema: github.schemas.pullRequest.pullRequestEventSchema, }; } + +export function pullRequestCommentEvent(params: { + repo: string; +}): TriggerEvent< + typeof github.schemas.pullRequestComments.pullRequestReviewCommentEventSchema +> { + return { + metadata: { + type: "WEBHOOK", + service: "github", + name: "pull_request_review_comment", + filter: { + service: ["github"], + payload: { + repository: { + full_name: [params.repo], + }, + }, + event: ["pull_request_review_comment"], + }, + source: github.schemas.WebhookSourceSchema.parse({ + subresource: "repository", + scopes: ["repo"], + repo: params.repo, + events: ["pull_request_review_comment"], + }), + }, + schema: + github.schemas.pullRequestComments.pullRequestReviewCommentEventSchema, + }; +} diff --git a/packages/trigger-providers/src/providers/github/schemas/index.ts b/packages/trigger-providers/src/providers/github/schemas/index.ts index 21f29812e..7288300dd 100644 --- a/packages/trigger-providers/src/providers/github/schemas/index.ts +++ b/packages/trigger-providers/src/providers/github/schemas/index.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import * as issues from "./issues"; import * as issuesComments from "./issueComments"; import * as pullRequest from "./pullRequests"; +import * as pullRequestComments from "./pullRequestComments"; export const WebhookRepoSourceSchema = z.object({ subresource: z.literal("repository"), @@ -22,4 +23,4 @@ export const WebhookSourceSchema = z.union([ WebhookOrganizationSourceSchema, ]); -export { issues, issuesComments, pullRequest }; +export { issues, issuesComments, pullRequest, pullRequestComments }; diff --git a/packages/trigger-providers/src/providers/github/schemas/pullRequestComments.ts b/packages/trigger-providers/src/providers/github/schemas/pullRequestComments.ts new file mode 100644 index 000000000..a2a6b1b2c --- /dev/null +++ b/packages/trigger-providers/src/providers/github/schemas/pullRequestComments.ts @@ -0,0 +1,285 @@ +import { z } from "zod"; +import { pullRequestAutoMergeSchema } from "./pullRequests"; +import { + userSchema, + teamSchema, + labelSchema, + milestoneSchema, + repositorySchema, + linkSchema, + authorAssociationSchema, + installationLiteSchema, + organizationSchema, + reactionsSchema, +} from "./shared"; + +export const pullRequestReviewCommentSchema = z.object({ + url: z.string(), + pull_request_review_id: z.number(), + id: z.number(), + node_id: z.string(), + diff_hunk: z.string(), + path: z.string(), + position: z.number().nullable(), + original_position: z.number(), + commit_id: z.string(), + original_commit_id: z.string(), + user: userSchema, + body: z.string(), + created_at: z.string(), + updated_at: z.string(), + html_url: z.string(), + pull_request_url: z.string(), + author_association: authorAssociationSchema, + _links: z.object({ + self: linkSchema, + html: linkSchema, + pull_request: linkSchema, + }), + reactions: reactionsSchema, + start_line: z.number().nullable(), + original_start_line: z.number().nullable(), + start_side: z.union([z.literal("LEFT"), z.literal("RIGHT")]).nullable(), + line: z.number().nullable(), + original_line: z.number(), + side: z.union([z.literal("LEFT"), z.literal("RIGHT")]), + in_reply_to_id: z.number().optional(), +}); + +export const pullRequestReviewCommentCreatedEventSchema = z.object({ + action: z.literal("created"), + comment: pullRequestReviewCommentSchema, + pull_request: z.object({ + url: z.string(), + id: z.number(), + node_id: z.string(), + html_url: z.string(), + diff_url: z.string(), + patch_url: z.string(), + issue_url: z.string(), + number: z.number(), + state: z.union([z.literal("open"), z.literal("closed")]), + locked: z.boolean(), + title: z.string(), + user: userSchema, + body: z.string().nullable(), + created_at: z.string(), + updated_at: z.string(), + closed_at: z.string().nullable(), + merged_at: z.string().nullable(), + merge_commit_sha: z.string().nullable(), + assignee: userSchema.nullable(), + assignees: z.array(userSchema), + requested_reviewers: z.array(z.union([userSchema, teamSchema])), + requested_teams: z.array(teamSchema), + labels: z.array(labelSchema), + milestone: milestoneSchema.nullable(), + draft: z.boolean().optional(), + commits_url: z.string(), + review_comments_url: z.string(), + review_comment_url: z.string(), + comments_url: z.string(), + statuses_url: z.string(), + head: z.object({ + label: z.string(), + ref: z.string(), + sha: z.string(), + user: userSchema, + repo: repositorySchema, + }), + base: z.object({ + label: z.string(), + ref: z.string(), + sha: z.string(), + user: userSchema, + repo: repositorySchema, + }), + _links: z.object({ + self: linkSchema, + html: linkSchema, + issue: linkSchema, + comments: linkSchema, + review_comments: linkSchema, + review_comment: linkSchema, + commits: linkSchema, + statuses: linkSchema, + }), + auto_merge: pullRequestAutoMergeSchema.optional().nullable(), + author_association: authorAssociationSchema, + active_lock_reason: z + .union([ + z.literal("resolved"), + z.literal("off-topic"), + z.literal("too heated"), + z.literal("spam"), + ]) + .nullable(), + }), + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestReviewCommentDeletedEventSchema = z.object({ + action: z.literal("deleted"), + comment: pullRequestReviewCommentSchema, + pull_request: z.object({ + url: z.string(), + id: z.number(), + node_id: z.string(), + html_url: z.string(), + diff_url: z.string(), + patch_url: z.string(), + issue_url: z.string(), + number: z.number(), + state: z.union([z.literal("open"), z.literal("closed")]), + locked: z.boolean(), + title: z.string(), + user: userSchema, + body: z.string().nullable(), + created_at: z.string(), + updated_at: z.string(), + closed_at: z.string().nullable(), + merged_at: z.string().nullable(), + merge_commit_sha: z.string().nullable(), + assignee: userSchema.nullable(), + assignees: z.array(userSchema), + requested_reviewers: z.array(z.union([userSchema, teamSchema])), + requested_teams: z.array(teamSchema), + labels: z.array(labelSchema), + milestone: milestoneSchema.nullable(), + draft: z.boolean().optional(), + commits_url: z.string(), + review_comments_url: z.string(), + review_comment_url: z.string(), + comments_url: z.string(), + statuses_url: z.string(), + head: z.object({ + label: z.string(), + ref: z.string(), + sha: z.string(), + user: userSchema, + repo: repositorySchema, + }), + base: z.object({ + label: z.string(), + ref: z.string(), + sha: z.string(), + user: userSchema, + repo: repositorySchema, + }), + _links: z.object({ + self: linkSchema, + html: linkSchema, + issue: linkSchema, + comments: linkSchema, + review_comments: linkSchema, + review_comment: linkSchema, + commits: linkSchema, + statuses: linkSchema, + }), + auto_merge: pullRequestAutoMergeSchema.optional().nullable(), + author_association: authorAssociationSchema, + active_lock_reason: z + .union([ + z.literal("resolved"), + z.literal("off-topic"), + z.literal("too heated"), + z.literal("spam"), + ]) + .nullable(), + }), + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestReviewCommentEditedEventSchema = z.object({ + action: z.literal("edited"), + changes: z.object({ + body: z + .object({ + from: z.string(), + }) + .optional(), + }), + comment: pullRequestReviewCommentSchema, + pull_request: z.object({ + url: z.string(), + id: z.number(), + node_id: z.string(), + html_url: z.string(), + diff_url: z.string(), + patch_url: z.string(), + issue_url: z.string(), + number: z.number(), + state: z.union([z.literal("open"), z.literal("closed")]), + locked: z.boolean(), + title: z.string(), + user: userSchema, + body: z.string().nullable(), + created_at: z.string(), + updated_at: z.string(), + closed_at: z.string().nullable(), + merged_at: z.string().nullable(), + merge_commit_sha: z.string().nullable(), + assignee: userSchema.nullable(), + assignees: z.array(userSchema), + requested_reviewers: z.array(z.union([userSchema, teamSchema])), + requested_teams: z.array(teamSchema), + labels: z.array(labelSchema), + milestone: milestoneSchema.nullable(), + draft: z.boolean().optional(), + commits_url: z.string(), + review_comments_url: z.string(), + review_comment_url: z.string(), + comments_url: z.string(), + statuses_url: z.string(), + head: z.object({ + label: z.string(), + ref: z.string(), + sha: z.string(), + user: userSchema, + repo: repositorySchema, + }), + base: z.object({ + label: z.string(), + ref: z.string(), + sha: z.string(), + user: userSchema, + repo: repositorySchema, + }), + _links: z.object({ + self: linkSchema, + html: linkSchema, + issue: linkSchema, + comments: linkSchema, + review_comments: linkSchema, + review_comment: linkSchema, + commits: linkSchema, + statuses: linkSchema, + }), + auto_merge: pullRequestAutoMergeSchema.optional().nullable(), + author_association: authorAssociationSchema, + active_lock_reason: z + .union([ + z.literal("resolved"), + z.literal("off-topic"), + z.literal("too heated"), + z.literal("spam"), + ]) + .nullable(), + }), + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestReviewCommentEventSchema = z.union([ + pullRequestReviewCommentCreatedEventSchema, + pullRequestReviewCommentDeletedEventSchema, + pullRequestReviewCommentEditedEventSchema, +]); From 5075df90680392523b31740a467714a5b60b04fd Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Thu, 19 Jan 2023 13:36:20 -0800 Subject: [PATCH 24/45] Feature: Enable/Disable & Archive/Unarchive workflows --- .../components/navigation/WorkflowMenu.tsx | 2 +- apps/webapp/app/hooks/useWorkflows.ts | 2 + apps/webapp/app/models/message.server.ts | 16 + .../models/workflowListPresenter.server.ts | 7 +- .../orgs/$organizationSlug/__org/index.tsx | 4 +- .../workflows/$workflowSlug/index.tsx | 22 +- .../workflows/$workflowSlug/integrations.tsx | 22 +- .../workflows/$workflowSlug/runs/index.tsx | 41 +- .../workflows/$workflowSlug/settings.tsx | 299 +++++++-- .../workflows/$workflowSlug/test.tsx | 23 +- .../app/services/events/dispatch.server.ts | 5 + .../scheduler/deliverScheduledEvent.server.ts | 4 + .../workflows/archiveWorkflow.server.ts | 71 +++ .../workflows/disableWorkflow.server.ts | 84 +++ .../workflows/enableWorkflow.server.ts | 97 +++ .../workflows/registerWorkflow.server.ts | 7 + .../workflows/unarchiveWorkflow.server.ts | 68 +++ .../migration.sql | 14 + .../migration.sql | 14 + .../migration.sql | 2 + .../migration.sql | 19 + apps/webapp/prisma/schema.prisma | 7 + examples/playground/src/index.ts | 566 +++++++++--------- 23 files changed, 1012 insertions(+), 384 deletions(-) create mode 100644 apps/webapp/app/services/workflows/archiveWorkflow.server.ts create mode 100644 apps/webapp/app/services/workflows/disableWorkflow.server.ts create mode 100644 apps/webapp/app/services/workflows/enableWorkflow.server.ts create mode 100644 apps/webapp/app/services/workflows/unarchiveWorkflow.server.ts create mode 100644 apps/webapp/prisma/migrations/20230119164847_add_schema_for_disabling_and_archiving/migration.sql create mode 100644 apps/webapp/prisma/migrations/20230119174326_move_new_datetime_fields_to_workflow/migration.sql create mode 100644 apps/webapp/prisma/migrations/20230119175445_add_enabled_to_event_rule/migration.sql create mode 100644 apps/webapp/prisma/migrations/20230119183126_removed_archived_status/migration.sql diff --git a/apps/webapp/app/components/navigation/WorkflowMenu.tsx b/apps/webapp/app/components/navigation/WorkflowMenu.tsx index 7e289fdca..153c3c7d4 100644 --- a/apps/webapp/app/components/navigation/WorkflowMenu.tsx +++ b/apps/webapp/app/components/navigation/WorkflowMenu.tsx @@ -84,7 +84,7 @@ export function WorkflowMenu() { )} >
    - {workflow.status !== "READY" && ( + {workflow.status === "CREATED" && ( )} ; diff --git a/apps/webapp/app/models/message.server.ts b/apps/webapp/app/models/message.server.ts index 4948070d3..598449b71 100644 --- a/apps/webapp/app/models/message.server.ts +++ b/apps/webapp/app/models/message.server.ts @@ -61,3 +61,19 @@ export async function redirectWithErrorMessage( }, }); } + +export async function redirectBackWithErrorMessage( + request: Request, + message: string +) { + const url = new URL(request.url); + return redirectWithErrorMessage(url.pathname, request, message); +} + +export async function redirectBackWithSuccessMessage( + request: Request, + message: string +) { + const url = new URL(request.url); + return redirectWithSuccessMessage(url.pathname, request, message); +} diff --git a/apps/webapp/app/models/workflowListPresenter.server.ts b/apps/webapp/app/models/workflowListPresenter.server.ts index 88afd9b69..d1970e0f6 100644 --- a/apps/webapp/app/models/workflowListPresenter.server.ts +++ b/apps/webapp/app/models/workflowListPresenter.server.ts @@ -57,7 +57,7 @@ export class WorkflowListPresenter { function getWorkflows(prismaClient: PrismaClient, organizationSlug: string) { return prismaClient.workflow.findMany({ - where: { organization: { slug: organizationSlug } }, + where: { organization: { slug: organizationSlug }, isArchived: false }, include: { externalServices: { select: { @@ -79,7 +79,10 @@ function getWorkflows(prismaClient: PrismaClient, organizationSlug: string) { orderBy: { finishedAt: { sort: "desc", nulls: "last" } }, }, }, - orderBy: { title: "asc" }, + orderBy: [ + { disabledAt: { sort: "asc", nulls: "first" } }, + { title: "asc" }, + ], }); } diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx index a9685af43..f7a125cf2 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/__org/index.tsx @@ -87,13 +87,13 @@ function WorkflowList({ to={`/orgs/${currentOrganizationSlug}/workflows/${workflow.slug}`} className={classNames( "block hover:bg-slate-850/40 transition", - workflowDisabled + workflow.status === "DISABLED" ? workflowDisabled : "" )} >
    - {workflow.status !== "READY" && ( + {workflow.status === "CREATED" && ( )}
    diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx index 47b3f899f..e86acde79 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/index.tsx @@ -84,23 +84,25 @@ export default function Page() { {workflow.slug}
    - {workflow.status !== "READY" && ( + {workflow.status === "CREATED" && ( <> This workflow requires its APIs to be connected before it can run. )} - - - This workflow is disabled. Runs cannot be triggered or tested while - disabled. If a run is currently in progress, it will fail. - + {workflow.status === "DISABLED" && ( + + + This workflow is disabled. Runs cannot be triggered or tested while + disabled. Runs in progress will continue until complete. + - - Settings - - + + Settings + + + )} {apiConnectionCount > 0 && } {eventRule && ( <> diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/integrations.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/integrations.tsx index b80f7b6fc..303cd498d 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/integrations.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/integrations.tsx @@ -19,22 +19,24 @@ export default function Page() { return ( <> Connected APIs - {workflow.status !== "READY" && ( + {workflow.status === "CREATED" && ( <> This workflow requires its APIs to be connected before it can run. )} - - - This workflow is disabled. Runs cannot be triggered or tested while - disabled. If a run is currently in progress, it will fail. - - - Settings - - + {workflow.status === "DISABLED" && ( + + + This workflow is disabled. Runs cannot be triggered or tested while + disabled. Runs in progress will continue until complete. + + + Settings + + + )} {connectionSlots.source || connectionSlots.services.length > 0 ? ( ) : ( diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/runs/index.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/runs/index.tsx index d9eb93075..420331342 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/runs/index.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/runs/index.tsx @@ -7,26 +7,25 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import invariant from "tiny-invariant"; import { Panel } from "~/components/layout/Panel"; +import { PanelInfo } from "~/components/layout/PanelInfo"; +import { PanelWarning } from "~/components/layout/PanelWarning"; import { PaginationControls } from "~/components/Pagination"; +import { TertiaryLink } from "~/components/primitives/Buttons"; import { StyledListBox } from "~/components/primitives/ListBox"; +import { Body } from "~/components/primitives/text/Body"; +import { Title } from "~/components/primitives/text/Title"; +import { RunsTable } from "~/components/runs/RunsTable"; import { runStatusIcon, runStatusLabel, runStatusTitle, } from "~/components/runs/runStatus"; -import { RunsTable } from "~/components/runs/RunsTable"; +import { useCurrentWorkflow } from "~/hooks/useWorkflows"; +import { getRuntimeEnvironmentFromRequest } from "~/models/runtimeEnvironment.server"; import type { WorkflowRunStatus } from "~/models/workflowRun.server"; import { WorkflowRunListPresenter } from "~/models/workflowRunListPresenter.server"; -import { requireUserId } from "~/services/session.server"; -import { getRuntimeEnvironmentFromRequest } from "~/models/runtimeEnvironment.server"; import { allStatuses } from "~/models/workflowRunStatus"; -import { Title } from "~/components/primitives/text/Title"; -import { useCurrentWorkflow } from "~/hooks/useWorkflows"; -import { PanelWarning } from "~/components/layout/PanelWarning"; -import { SubTitle } from "~/components/primitives/text/SubTitle"; -import { TertiaryLink } from "~/components/primitives/Buttons"; -import { Body } from "~/components/primitives/text/Body"; -import { PanelInfo } from "~/components/layout/PanelInfo"; +import { requireUserId } from "~/services/session.server"; export const loader = async ({ request, params }: LoaderArgs) => { const userId = await requireUserId(request); @@ -80,22 +79,24 @@ export default function Page() { return ( <> Runs - {workflow.status !== "READY" && ( + {workflow.status === "CREATED" && ( <> This workflow requires its APIs to be connected before it can run. )} - - - This workflow is disabled. Runs cannot be triggered or tested while - disabled. If a run is currently in progress, it will fail. - - - Settings - - + {workflow.status === "DISABLED" && ( + + + This workflow is disabled. Runs cannot be triggered or tested while + disabled. Runs in progress will continue until complete. + + + Settings + + + )} + ) : workflow.isArchived ? ( + + ) : workflow.status === "DISABLED" ? ( + + ) : null; + return ( <> Settings - Workflow - -
    - - - Send to Slack on a new domain workflow is active. - -
    -
    - Disable - Archive -
    -
    - -
    - - - Send to Slack on a new domain workflow is disabled. - -
    -
    - Enable - Archive -
    -
    - -
    - - - Send to Slack on a new domain workflow is archived. - -
    -
    - Unarchive -
    -
    + Workflow Status + {panel} ); } + +function WorkflowReadyPanel({ + workflow, +}: { + workflow: NonNullable; +}) { + return ( + +
    + + + {workflow.title} is active. + +
    +
    +
    + !confirm( + "Disabling this workflow will prevent any new triggers from running, but will not stop in-progress runs from completing. Are you sure you want to disable this workflow?" + ) && e.preventDefault() + } + > + + Disable + +
    + +
    + !confirm( + "Archiving this workflow disable it and remove it from your list of workflows. Are you sure you want to archive this workflow?" + ) && e.preventDefault() + } + > + + Archive + +
    +
    +
    + ); +} + +function WorkflowDisabledPanel({ + workflow, +}: { + workflow: NonNullable; +}) { + return ( + +
    + + + {workflow.title} is disabled. + +
    +
    +
    + !confirm( + "Enabling this workflow will resume triggering workflows from new events. Are you sure you want to enable this workflow?" + ) && e.preventDefault() + } + > + + Enable + +
    + +
    + !confirm( + "Archiving this workflow will remove it from your list of workflows. Are you sure you want to archive this workflow?" + ) && e.preventDefault() + } + > + + Archive + +
    +
    +
    + ); +} + +function WorkflowArchivedPanel({ + workflow, +}: { + workflow: NonNullable; +}) { + return ( + +
    + + + {workflow.title} is archived. + +
    +
    +
    + !confirm( + "Unarchiving this workflow will add it back to your list of workflows, but not enable it. Are you sure you want to unarchive this workflow?" + ) && e.preventDefault() + } + > + + Unarchive + +
    +
    +
    + ); +} diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/test.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/test.tsx index 61a79dd4d..91f0912ba 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/test.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/test.tsx @@ -41,22 +41,25 @@ export default function Page() { return ( <> Test - {workflow.status !== "READY" && ( + {workflow.status === "CREATED" && ( <> This workflow requires its APIs to be connected before it can run. )} - - - This workflow is disabled. Runs cannot be triggered or tested while - disabled. If a run is currently in progress, it will fail. - - - Settings - - + {workflow.status === "DISABLED" && ( + + + This workflow is disabled. Runs cannot be triggered or tested while + disabled. Runs in progress will continue until complete. + + + Settings + + + )} + {workflow.status === "READY" && ( { +// // await ctx.logger.info( +// // "This workflow will post a message to Slack immediately and display an Error, Info, Debug, and Warning message." +// // ); // new Trigger({ // id: "playground-1", // name: "Post to Slack immediately", @@ -19,113 +34,6 @@ import { z } from "zod"; // await ctx.logger.info( // "This workflow will post a message to Slack immediately and display an Error, Info, Debug, and Warning message." // ); -new Trigger({ - id: "playground-1", - name: "Post to Slack immediately", - apiKey: "trigger_dev_zC25mKNn6c0q", - endpoint: "ws://localhost:8889/ws", - on: customEvent({ - name: "playground", - schema: z.object({ - id: z.string(), - }), - }), - run: async (event, ctx) => { - await ctx.logger.info( - "This workflow will post a message to Slack immediately and display an Error, Info, Debug, and Warning message." - ); - - await ctx.logger.error("Error message!", { event }); - - await ctx.logger.info("Info message", { event }); - - await ctx.logger.debug("Debug message"); - - await ctx.logger.warn("Warning message!"); - - const response = await slack.postMessage("send-to-slack", { - channel: "test-integrations", - text: `This is a message from the "Posts to Slack" workflow ${event.id}`, - }); - - return response.message; - }, -}).listen(); - -// Webhook workflow that sends a message to Slack when a Github issue is created after 2 delays - -new Trigger({ - id: "playground-2", - name: "Posts to Slack after a GitHub Issue created", - apiKey: "trigger_dev_zC25mKNn6c0q", - endpoint: "ws://localhost:8889/ws", - on: github.events.repoIssueEvent({ repo: "triggerdotdev/trigger.dev" }), - - run: async (event, ctx) => { - await ctx.logger.info( - "This workflow will post to Slack when a GitHub Issue created or modified after 2 delays." - ); - - await ctx.waitFor("initial-wait", { seconds: 30 }); - - await ctx.waitUntil("wait-until", new Date(Date.now() + 1000 * 30)); - - await ctx.logger.info("Both types of delay happened"); - - const response = await slack.postMessage("send-to-slack", { - channel: "test-integrations", - text: `This is a message posts after an Issue was created on GitHub ${event.action}`, - }); - - await ctx.logger.debug("Debug message"); - - await ctx.logger.warn("Warning message!"); - - return response.message; - }, -}).listen(); - -// Workflow that shows all the log types with a 5 second delay between each - -new Trigger({ - id: "playground-3", - name: "All log types with a 5 second delay between each", - apiKey: "trigger_dev_zC25mKNn6c0q", - endpoint: "ws://localhost:8889/ws", - on: customEvent({ - name: "playground", - schema: z.object({ - id: z.string(), - }), - }), - - run: async (event, ctx) => { - await ctx.logger.info( - "This workflow prints all the log types with a 5 second delay between each." - ); - - await ctx.logger.info( - "Hey there! This is a really long message to see how the layout handles it. If this breaks the layout, I will fix it. Hey there! This is a really long message to see how the layout handles it. If this breaks the layout, I will fix it. Hey there! This is a really long message to see how the layout handles it. If this breaks the layout, I will fix it. Hey there! This is a really long message to see how the layout handles it. If this breaks the layout, I will fix it." - ); - - await ctx.waitFor("first-wait", { seconds: 5 }); - - await ctx.logger.error( - "This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message!", - { event } - ); - - await ctx.waitFor("second-wait", { seconds: 5 }); - - await ctx.logger.info( - "This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message." - ); - - await ctx.waitFor("third-wait", { seconds: 5 }); - - await ctx.logger.debug( - "This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. " - ); // await ctx.logger.error("Error message!", { event }); @@ -219,129 +127,276 @@ new Trigger({ // "This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. " // ); -// await ctx.waitFor("fourth-wait", { seconds: 5 }); +// // await ctx.logger.error("Error message!", { event }); -// await ctx.logger.warn( -// "This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! " -// ); +// // await ctx.logger.info("Info message", { event }); -// await ctx.waitFor("fifth-wait", { seconds: 5 }); +// // await ctx.logger.debug("Debug message"); -// const response = await slack.postMessage("send-to-slack", { -// channel: "test-integrations", -// text: `This test displays all the log types in the webapp ${event.id}`, -// }); +// // await ctx.logger.warn("Warning message!"); -// return response.message; -// }, -// }).listen(); +// // const response = await slack.postMessage("send-to-slack", { +// // channel: "test-integrations", +// // text: `This is a message from the "Posts to Slack" workflow ${event.id}`, +// // }); -// // Workflow that sends 2 messages to Slack with a 10 second delay between both +// // return response.message; +// // }, +// // }).listen(); -// new Trigger({ -// id: "playground-4", -// name: "Post to Slack twice after 10 second delays", -// apiKey: "trigger_dev_zC25mKNn6c0q", +// // // Webhook workflow that sends a message to Slack when a Github issue is created after 2 delays + +// // new Trigger({ +// // id: "playground-2", +// // name: "Posts to Slack after a GitHub Issue created", +// // apiKey: "trigger_dev_zC25mKNn6c0q", +// // endpoint: "ws://localhost:8889/ws", +// // on: github.events.repoIssueEvent({ repo: "triggerdotdev/trigger.dev" }), + +// // run: async (event, ctx) => { +// // await ctx.logger.info( +// // "This workflow will post to Slack when a GitHub Issue created or modified after 2 delays." +// // ); + +// // await ctx.waitFor("initial-wait", { seconds: 30 }); + +// // await ctx.waitUntil("wait-until", new Date(Date.now() + 1000 * 30)); + +// // await ctx.logger.info("Both types of delay happened"); + +// // const response = await slack.postMessage("send-to-slack", { +// // channel: "test-integrations", +// // text: `This is a message posts after an Issue was created on GitHub ${event.action}`, +// // }); + +// // await ctx.logger.debug("Debug message"); + +// // await ctx.logger.warn("Warning message!"); + +// // return response.message; +// // }, +// // }).listen(); + +// // // Workflow that shows all the log types with a 5 second delay between each + +// // new Trigger({ +// // id: "playground-3", +// // name: "All log types with a 5 second delay between each", +// // apiKey: "trigger_dev_zC25mKNn6c0q", +// // endpoint: "ws://localhost:8889/ws", +// // on: customEvent({ +// // name: "playground", +// // schema: z.object({ +// // id: z.string(), +// // }), +// // }), + +// // run: async (event, ctx) => { +// // await ctx.logger.info( +// // "This workflow prints all the log types with a 5 second delay between each." +// // ); + +// // await ctx.logger.info( +// // "Hey there! This is a really long message to see how the layout handles it. If this breaks the layout, I will fix it. Hey there! This is a really long message to see how the layout handles it. If this breaks the layout, I will fix it. Hey there! This is a really long message to see how the layout handles it. If this breaks the layout, I will fix it. Hey there! This is a really long message to see how the layout handles it. If this breaks the layout, I will fix it." +// // ); + +// // await ctx.waitFor("first-wait", { seconds: 5 }); + +// // await ctx.logger.error( +// // "This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message! This is a really long Error message!", +// // { event } +// // ); + +// // await ctx.waitFor("second-wait", { seconds: 5 }); + +// // await ctx.logger.info( +// // "This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message. This is a really long Info message." +// // ); + +// // await ctx.waitFor("third-wait", { seconds: 5 }); + +// // await ctx.logger.debug( +// // "This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. This is a really long Debug message. " +// // ); + +// // await ctx.waitFor("fourth-wait", { seconds: 5 }); + +// // await ctx.logger.warn( +// // "This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! This is a really long Warning message! " +// // ); + +// // await ctx.waitFor("fifth-wait", { seconds: 5 }); + +// // const response = await slack.postMessage("send-to-slack", { +// // channel: "test-integrations", +// // text: `This test displays all the log types in the webapp ${event.id}`, +// // }); + +// // return response.message; +// // }, +// // }).listen(); + +// // // Workflow that sends 2 messages to Slack with a 10 second delay between both + +// // new Trigger({ +// // id: "playground-4", +// // name: "Post to Slack twice after 10 second delays", +// // apiKey: "trigger_dev_zC25mKNn6c0q", +// // endpoint: "ws://localhost:8889/ws", +// // on: customEvent({ +// // name: "playground", +// // schema: z.object({ +// // id: z.string(), +// // }), +// // }), +// // run: async (event, ctx) => { +// // await ctx.logger.info( +// // "This workflow sends 2 messages to Slack with a 10 second delay between both." +// // ); + +// // await ctx.waitFor("initial-wait", { seconds: 10 }); + +// // await slack.postMessage("send-to-slack-1", { +// // channel: "test-integrations", +// // text: `This is test message 1/2 from "Post to Slack twice after 10 second delays" workflow ${event.id}`, +// // }); + +// // await ctx.waitFor("second-wait", { seconds: 10 }); + +// // const response = await slack.postMessage("send-to-slack-2", { +// // channel: "test-integrations", +// // text: `This is test message 2/2 from "Post to Slack twice after 10 second delays" workflow ${event.id}`, +// // }); + +// // return response.message; +// // }, +// // }).listen(); + +// // // Workflow that send a message to Slack after 1 hour + +// // new Trigger({ +// // id: "playground-5", +// // name: "Post to Slack after 1 hour", +// // apiKey: "trigger_dev_zC25mKNn6c0q", +// // endpoint: "ws://localhost:8889/ws", +// // on: customEvent({ +// // name: "playground", +// // schema: z.object({ +// // id: z.string(), +// // }), +// // }), +// // run: async (event, ctx) => { +// // await ctx.logger.info( +// // "This workflow posts a message to Slack after a 1 hour delay" +// // ); + +// // await ctx.waitUntil("wait-until", new Date(Date.now() + 1000 * 60 * 60)); + +// // await ctx.logger.info("The workflow resumed after the 1 hour delay", { +// // event, +// // }); + +// // const response = await slack.postMessage("send-to-slack", { +// // channel: "test-integrations", +// // text: `This message was sent after a 1 hour delay ${event.id}`, +// // }); + +// // return response.message; +// // }, +// // }).listen(); + +// // // Workflow that send a message to Slack after 24 hours + +// // new Trigger({ +// // id: "playground-6", +// // name: "Post to Slack after a 24 hour delay", +// // apiKey: "trigger_dev_zC25mKNn6c0q", +// // endpoint: "ws://localhost:8889/ws", +// // on: customEvent({ +// // name: "playground", +// // schema: z.object({ +// // id: z.string(), +// // }), +// // }), +// // run: async (event, ctx) => { +// // await ctx.logger.info( +// // "This workflow posts a message to Slack after a 24 hour delay" +// // ); + +// // await ctx.waitUntil( +// // "wait-until", +// // new Date(Date.now() + 1000 * 60 * 60 * 24) +// // ); + +// // await ctx.logger.info("The workflow resumed after the 24 hour delay", { +// // event, +// // }); + +// // const response = await slack.postMessage("send-to-slack", { +// // channel: "test-integrations", +// // text: `This message was sent after a 24 hour delay ${event.id}`, +// // }); + +// // return response.message; +// // }, +// // }).listen(); + +// // // Workflow that send a message to Slack after 24 hours + +// // new Trigger({ +// // id: "playground-7", +// // name: "Post to Slack many times in a loop", +// // apiKey: "trigger_dev_zC25mKNn6c0q", +// // endpoint: "ws://localhost:8889/ws", +// // on: customEvent({ +// // name: "playground", +// // schema: z.object({ +// // id: z.string(), +// // }), +// // }), +// // run: async (event, ctx) => { +// // for (let index = 0; index < 4; index++) { +// // const response = await slack.postMessage(`send-to-slack-${index}`, { +// // channel: "test-integrations", +// // text: `This is a post to Slack many times in a loop ${index} ${event.id}`, +// // }); +// // } + +// // return {}; +// // }, +// // }).listen(); + +// const postMessage = new Trigger({ +// id: "new-user", +// name: "New user slack message", +// apiKey: "trigger_development_lwlXEjyhSNF4", // endpoint: "ws://localhost:8889/ws", +// logLevel: "info", // on: customEvent({ -// name: "playground", +// name: "user.created", // schema: z.object({ -// id: z.string(), +// name: z.string(), +// email: z.string(), +// paidPlan: z.boolean(), // }), // }), // run: async (event, ctx) => { -// await ctx.logger.info( -// "This workflow sends 2 messages to Slack with a 10 second delay between both." -// ); - -// await ctx.waitFor("initial-wait", { seconds: 10 }); - -// await slack.postMessage("send-to-slack-1", { -// channel: "test-integrations", -// text: `This is test message 1/2 from "Post to Slack twice after 10 second delays" workflow ${event.id}`, -// }); - -// await ctx.waitFor("second-wait", { seconds: 10 }); - -// const response = await slack.postMessage("send-to-slack-2", { -// channel: "test-integrations", -// text: `This is test message 2/2 from "Post to Slack twice after 10 second delays" workflow ${event.id}`, -// }); - -// return response.message; -// }, -// }).listen(); - -// // Workflow that send a message to Slack after 1 hour - -// new Trigger({ -// id: "playground-5", -// name: "Post to Slack after 1 hour", -// apiKey: "trigger_dev_zC25mKNn6c0q", -// endpoint: "ws://localhost:8889/ws", -// on: customEvent({ -// name: "playground", -// schema: z.object({ -// id: z.string(), -// }), -// }), -// run: async (event, ctx) => { -// await ctx.logger.info( -// "This workflow posts a message to Slack after a 1 hour delay" -// ); - -// await ctx.waitUntil("wait-until", new Date(Date.now() + 1000 * 60 * 60)); - -// await ctx.logger.info("The workflow resumed after the 1 hour delay", { -// event, -// }); +// await ctx.logger.info("This log will appear on the Trigger.dev run page"); +// //send a message to the #new-users Slack channel with user details // const response = await slack.postMessage("send-to-slack", { -// channel: "test-integrations", -// text: `This message was sent after a 1 hour delay ${event.id}`, +// channel: "new-users", +// text: `New user: ${event.name} (${event.email}) signed up. ${ +// event.paidPlan ? "They are paying" : "They are on the free plan" +// }.`, // }); // return response.message; // }, -// }).listen(); +// }); -// // Workflow that send a message to Slack after 24 hours - -// new Trigger({ -// id: "playground-6", -// name: "Post to Slack after a 24 hour delay", -// apiKey: "trigger_dev_zC25mKNn6c0q", -// endpoint: "ws://localhost:8889/ws", -// on: customEvent({ -// name: "playground", -// schema: z.object({ -// id: z.string(), -// }), -// }), -// run: async (event, ctx) => { -// await ctx.logger.info( -// "This workflow posts a message to Slack after a 24 hour delay" -// ); - -// await ctx.waitUntil( -// "wait-until", -// new Date(Date.now() + 1000 * 60 * 60 * 24) -// ); - -// await ctx.logger.info("The workflow resumed after the 24 hour delay", { -// event, -// }); - -// const response = await slack.postMessage("send-to-slack", { -// channel: "test-integrations", -// text: `This message was sent after a 24 hour delay ${event.id}`, -// }); - -// return response.message; -// }, -// }).listen(); - -// // Workflow that send a message to Slack after 24 hours +// //this workflow will now connect and start listening for events +// postMessage.listen(); // new Trigger({ // id: "playground-7", @@ -366,68 +421,13 @@ new Trigger({ // }, // }).listen(); -const postMessage = new Trigger({ - id: "new-user", - name: "New user slack message", - apiKey: "trigger_development_lwlXEjyhSNF4", - endpoint: "ws://localhost:8889/ws", - logLevel: "info", - on: customEvent({ - name: "user.created", - schema: z.object({ - name: z.string(), - email: z.string(), - paidPlan: z.boolean(), - }), - }), - run: async (event, ctx) => { - await ctx.logger.info("This log will appear on the Trigger.dev run page"); - - //send a message to the #new-users Slack channel with user details - const response = await slack.postMessage("send-to-slack", { - channel: "new-users", - text: `New user: ${event.name} (${event.email}) signed up. ${ - event.paidPlan ? "They are paying" : "They are on the free plan" - }.`, - }); - - return response.message; - }, -}); - -//this workflow will now connect and start listening for events -postMessage.listen(); - -new Trigger({ - id: "playground-7", - name: "Post to Slack many times in a loop", - apiKey: "trigger_dev_zC25mKNn6c0q", - endpoint: "ws://localhost:8889/ws", - on: customEvent({ - name: "playground", - schema: z.object({ - id: z.string(), - }), - }), - run: async (event, ctx) => { - for (let index = 0; index < 4; index++) { - const response = await slack.postMessage(`send-to-slack-${index}`, { - channel: "test-integrations", - text: `This is a post to Slack many times in a loop ${index} ${event.id}`, - }); - } - - return {}; - }, -}).listen(); - new Trigger({ id: "scheduled-workflow", name: "Scheduled Workflow", apiKey: "trigger_dev_zC25mKNn6c0q", endpoint: "ws://localhost:8889/ws", logLevel: "debug", - on: scheduleEvent({ rateOf: { minutes: 5 } }), + on: scheduleEvent({ rateOf: { minutes: 4 } }), run: async (event, ctx) => { await ctx.logger.info("Received the scheduled event", { event, @@ -438,19 +438,19 @@ new Trigger({ }, }).listen(); -new Trigger({ - id: "cron-scheduled-workflow", - name: "Cron Scheduled Workflow", - apiKey: "trigger_dev_zC25mKNn6c0q", - endpoint: "ws://localhost:8889/ws", - logLevel: "debug", - on: scheduleEvent({ cron: "0 * * * *" }), - run: async (event, ctx) => { - await ctx.logger.info("Received the cron scheduled event", { - event, - wallTime: new Date(), - }); +// new Trigger({ +// id: "cron-scheduled-workflow", +// name: "Cron Scheduled Workflow", +// apiKey: "trigger_dev_zC25mKNn6c0q", +// endpoint: "ws://localhost:8889/ws", +// logLevel: "debug", +// on: scheduleEvent({ cron: "0 * * * *" }), +// run: async (event, ctx) => { +// await ctx.logger.info("Received the cron scheduled event", { +// event, +// wallTime: new Date(), +// }); - return { foo: "bar" }; - }, -}).listen(); +// return { foo: "bar" }; +// }, +// }).listen(); From 69a79963efee928a61bc679f65688ab66df9ace4 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Thu, 19 Jan 2023 13:45:06 -0800 Subject: [PATCH 25/45] Added staging env but not deploying it yet --- flightcontrol.json | 178 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 176 insertions(+), 2 deletions(-) diff --git a/flightcontrol.json b/flightcontrol.json index b42f57cb6..2e3102f41 100644 --- a/flightcontrol.json +++ b/flightcontrol.json @@ -62,7 +62,9 @@ "packages/internal-pulsar/src/**", "./pnpm-lock.yaml" ], - "dependsOn": ["p-db"], + "dependsOn": [ + "p-db" + ], "envVariables": { "FROM_EMAIL": "hello@email.trigger.dev", "REPLY_TO_EMAIL": "hello@trigger.dev", @@ -170,6 +172,178 @@ "connectionStringEnvVarName": "REDIS_URL" } ] + }, + { + "id": "staging", + "name": "Staging", + "region": "us-east-1", + "source": { + "branch": "dev", + "trigger": "manual" + }, + "envVariables": { + "PULSAR_ISSUER_URL": "https://auth.streamnative.cloud/", + "PULSAR_AUDIENCE": { + "fromParameterStore": "/Staging/Pulsar/Audience" + }, + "PULSAR_TENANT": "triggerdotdev", + "PULSAR_WORKFLOWS_NAMESPACE": "workflows", + "PULSAR_QUEUES_NAMESPACE": "queues", + "PULSAR_SERVICE_URL": { + "fromParameterStore": "/Staging/Pulsar/ServiceURL" + }, + "DOCKER_USERNAME": "maverickdotdev", + "DOCKER_PASSWORD": { + "fromParameterStore": "/Docker/Password" + } + }, + "services": [ + { + "id": "s-webapp", + "name": "Staging Webapp", + "type": "fargate", + "buildType": "docker", + "dockerfilePath": "./apps/webapp/Dockerfile", + "dockerContext": ".", + "cpu": 0.5, + "memory": 2, + "minInstances": 1, + "maxInstances": 2, + "domain": "app-staging.trigger.dev", + "port": 3000, + "healthCheckPath": "/healthcheck", + "watchPaths": [ + "apps/webapp/app/**", + "apps/webapp/public/**", + "apps/webapp/Dockerfile", + "apps/webapp/package.json", + "apps/webapp/server.ts", + "apps/webapp/remix.config.js", + "apps/webapp/tailwind.config.js", + "apps/webapp/tsconfig.json", + "apps/webapp/postcss.config.js", + "apps/webapp/styles/**", + "apps/webapp/prisma/**", + "apps/webapp/types/**", + "packages/internal-platform/src/**", + "packages/common-schemas/src/**", + "packages/internal-integrations/src/**", + "packages/trigger-providers/src/**", + "packages/emails/src/**", + "packages/emails/emails/**", + "packages/internal-pulsar/src/**", + "./pnpm-lock.yaml" + ], + "dependsOn": [ + "s-db" + ], + "envVariables": { + "FROM_EMAIL": "hello@email.trigger.dev", + "REPLY_TO_EMAIL": "hello@trigger.dev", + "RESEND_API_KEY": { + "fromParameterStore": "/Staging/webapp/RESEND_API_KEY" + }, + "SENTRY_DSN": { + "fromParameterStore": "SENTRY_DSN" + }, + "SESSION_SECRET": { + "fromParameterStore": "/Staging/webapp/SESSION_SECRET" + }, + "MAGIC_LINK_SECRET": { + "fromParameterStore": "/Staging/webapp/MAGIC_LINK_SECRET" + }, + "GITHUB_CLIENT_ID": "724e56fcebe940eb5b22", + "GITHUB_SECRET": { + "fromParameterStore": "/Staging/webapp/GITHUB_SECRET" + }, + "DATABASE_URL": { + "fromService": { + "id": "s-db", + "value": "dbConnectionString" + } + }, + "PULSAR_CLIENT_ID": { + "fromParameterStore": "/Staging/Pulsar/webapp/ClientId" + }, + "PULSAR_CLIENT_SECRET": { + "fromParameterStore": "/Staging/Pulsar/webapp/ClientSecret" + }, + "PULSAR_ROLE": { + "fromParameterStore": "/Staging/Pulsar/webapp/Role" + }, + "PIZZLY_HOST": "https://staging-auth.trigger.dev", + "PIZZLY_SECRET_KEY": { + "fromParameterStore": "/Staging/webapp/PIZZLY_SECRET_KEY" + }, + "TRIGGER_LOG_LEVEL": "debug", + "PULSAR_ENABLED": "1", + "PULSAR_DEBUG": true + } + }, + { + "id": "s-wss", + "name": "Staging WSS", + "type": "fargate", + "buildType": "docker", + "dockerfilePath": "./apps/wss/Dockerfile", + "dockerContext": ".", + "cpu": 1, + "memory": 2, + "minInstances": 1, + "maxInstances": 2, + "domain": "wss-staging.trigger.dev", + "port": 8889, + "healthCheckPath": "/healthcheck", + "watchPaths": [ + "apps/wss/src/**", + "apps/wss/tsconfig.json", + "apps/wss/tsup.config.ts", + "apps/wss/package.json", + "packages/internal-platform/src/**", + "packages/internal-bridge/src/**", + "packages/common-schemas/src/**", + "packages/internal-pulsar/src/**", + "./pnpm-lock.yaml" + ], + "envVariables": { + "PULSAR_CLIENT_ID": { + "fromParameterStore": "/Staging/Pulsar/wss/ClientId" + }, + "PULSAR_CLIENT_SECRET": { + "fromParameterStore": "/Staging/Pulsar/wss/ClientSecret" + }, + "PULSAR_ROLE": { + "fromParameterStore": "/Staging/Pulsar/wss/Role" + }, + "PLATFORM_API_URL": "https://app.trigger.dev", + "TRIGGER_LOG_LEVEL": "debug" + } + }, + { + "id": "s-db", + "name": "Staging Database", + "type": "rds", + "engine": "postgres", + "applyChangesImmediately": true, + "engineVersion": "14", + "autoUpgradeMinorVersions": true, + "instanceSize": "db.t3.large", + "storage": 100, + "maxStorage": 400, + "private": false, + "deletionProtection": true, + "port": 5432 + }, + { + "id": "s-redis", + "name": "Staging Redis", + "type": "elasticache", + "engine": "redis", + "engineVersion": "7.0", + "instanceSize": "cache.t3.small", + "connectionStringEnvVarName": "REDIS_URL" + } + ] } ] -} +} \ No newline at end of file From 39f3a7df0c58880a7720b178d778df3be4659879 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Wed, 18 Jan 2023 20:18:49 -0800 Subject: [PATCH 26/45] reduced header divider slashes --- apps/webapp/app/components/layout/Header.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webapp/app/components/layout/Header.tsx b/apps/webapp/app/components/layout/Header.tsx index 674282a96..9b93b16ec 100644 --- a/apps/webapp/app/components/layout/Header.tsx +++ b/apps/webapp/app/components/layout/Header.tsx @@ -60,7 +60,7 @@ export function BreadcrumbDivider() { y1="0.6286" x2="0.6286" y2="24.6762" - opacity={0.5} + opacity={0.3} stroke="white" strokeLinecap="round" /> From f0c6f83d6f728a7cb2f24dfb3bb17a0d0b62f110 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Thu, 19 Jan 2023 14:10:22 -0800 Subject: [PATCH 27/45] After archiving, redirect back to workflow list --- .../$organizationSlug/workflows/$workflowSlug/settings.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/settings.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/settings.tsx index 04aab2f2d..7fa8e28cf 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/settings.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/settings.tsx @@ -18,6 +18,7 @@ import { requireUserId } from "~/services/session.server"; import { redirectBackWithErrorMessage, redirectBackWithSuccessMessage, + redirectWithSuccessMessage, } from "~/models/message.server"; import { DisableWorkflow } from "~/services/workflows/disableWorkflow.server"; import { EnableWorkflow } from "~/services/workflows/enableWorkflow.server"; @@ -103,7 +104,8 @@ async function archiveAction( return redirectBackWithErrorMessage(request, result.message); } - return redirectBackWithSuccessMessage( + return redirectWithSuccessMessage( + `/orgs/${organizationSlug}`, request, "Workflow successfully archived." ); From 35f1000ab7377e3e92e062caadc066530615331e Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Thu, 19 Jan 2023 14:10:57 -0800 Subject: [PATCH 28/45] Spacing improvement --- apps/webapp/app/components/layout/Header.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webapp/app/components/layout/Header.tsx b/apps/webapp/app/components/layout/Header.tsx index 9b93b16ec..2da7913be 100644 --- a/apps/webapp/app/components/layout/Header.tsx +++ b/apps/webapp/app/components/layout/Header.tsx @@ -26,7 +26,7 @@ export function Header({ children }: HeaderProps) {
    {children}
    -
    +
    Docs From 4b1e24310a8413b4507cec47eb5c5135ea9b4086 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Thu, 19 Jan 2023 14:30:54 -0800 Subject: [PATCH 29/45] Fixed button sizing --- apps/webapp/app/components/primitives/Buttons.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webapp/app/components/primitives/Buttons.tsx b/apps/webapp/app/components/primitives/Buttons.tsx index e878f8eea..c4f0d8359 100644 --- a/apps/webapp/app/components/primitives/Buttons.tsx +++ b/apps/webapp/app/components/primitives/Buttons.tsx @@ -9,7 +9,7 @@ const primaryClasses = classnames( ); const secondaryClasses = classnames( commonClasses, - "px-4 py-2 bg-transparent border border-slate-700 text-white hover:bg-white/5 hover:border-slate-700 focus:ring-slate-300 gap-2" + "px-4 py-2 bg-transparent ring-1 ring-slate-700 ring-inset text-white hover:bg-white/5 hover:border-slate-700 focus:ring-slate-300 gap-2" ); const tertiaryClasses = classnames( commonClasses, From 0515ec9388f124c522cc1432d326cc48b320abac Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Thu, 19 Jan 2023 14:31:38 -0800 Subject: [PATCH 30/45] Some UX improvements and copy fixes to the archive/disable workflow --- .../workflows/$workflowSlug/settings.tsx | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/settings.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/settings.tsx index 7fa8e28cf..89acee4ea 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/settings.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/settings.tsx @@ -87,7 +87,7 @@ async function enableAction( return redirectBackWithSuccessMessage( request, - "Workflow successfully enabled. We will resuming triggering it for new events." + "Workflow successfully enabled. Triggering of new events will resume." ); } @@ -126,7 +126,7 @@ async function unarchiveAction( return redirectBackWithSuccessMessage( request, - "Workflow successfully unarchived. although it remains disabled. Please enable it to resuming triggering it for new events." + "Workflow successfully unarchived. Enable it to resume triggering of new events." ); } @@ -146,7 +146,7 @@ export default function Page() { return ( <> Settings - Workflow Status + Workflow status {panel} ); @@ -162,10 +162,10 @@ function WorkflowReadyPanel({
    - {workflow.title} is active. + {workflow.title} is active.
    -
    +
    @@ -183,7 +183,7 @@ function WorkflowReadyPanel({ method="post" onSubmit={(e) => !confirm( - "Archiving this workflow disable it and remove it from your list of workflows. Are you sure you want to archive this workflow?" + "Archiving this workflow disables it and removes it from your list of workflows. Are you sure you want to archive this workflow?" ) && e.preventDefault() } > @@ -206,10 +206,10 @@ function WorkflowDisabledPanel({
    - {workflow.title} is disabled. + {workflow.title} is disabled.
    -
    +
    @@ -218,9 +218,9 @@ function WorkflowDisabledPanel({ ) && e.preventDefault() } > - + Enable - +
    - - {workflow.title} is archived. + + {workflow.title} is archived.
    -
    +
    From f92c3d75bb468ff37a3e0bd2526ddad277509909 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Thu, 19 Jan 2023 14:39:12 -0800 Subject: [PATCH 31/45] Added GitHub pushes and pull request reviews --- examples/github-webhook/src/index.ts | 34 ++++++- .../src/integrations/github/events.ts | 60 +++++++++++ .../src/providers/github/schemas/index.ts | 11 ++- .../github/schemas/pullRequestComments.ts | 2 +- .../github/schemas/pullRequestReviews.ts | 79 +++++++++++++++ .../providers/github/schemas/pullRequests.ts | 12 +-- .../src/providers/github/schemas/pushes.ts | 27 +++++ .../src/providers/github/schemas/shared.ts | 99 +++++++++++++++++++ 8 files changed, 310 insertions(+), 14 deletions(-) create mode 100644 packages/trigger-providers/src/providers/github/schemas/pullRequestReviews.ts create mode 100644 packages/trigger-providers/src/providers/github/schemas/pushes.ts diff --git a/examples/github-webhook/src/index.ts b/examples/github-webhook/src/index.ts index 9203ae8ac..5e13f23e7 100644 --- a/examples/github-webhook/src/index.ts +++ b/examples/github-webhook/src/index.ts @@ -35,7 +35,7 @@ new Trigger({ new Trigger({ id: "github-webhook-pull_request_comment", - name: "GitHub PR comment: triggerdotdev/trigger.dev-examples", + name: "GitHub PR review comment: triggerdotdev/trigger.dev-examples", apiKey: "trigger_dev_zC25mKNn6c0q", endpoint: "ws://localhost:8889/ws", logLevel: "debug", @@ -48,3 +48,35 @@ new Trigger({ return {}; }, }).listen(); + +new Trigger({ + id: "github-webhook-pull_request_review", + name: "GitHub PR review: triggerdotdev/trigger.dev-examples", + apiKey: "trigger_dev_zC25mKNn6c0q", + endpoint: "ws://localhost:8889/ws", + logLevel: "debug", + on: github.events.pullRequestReviewEvent({ + repo: "triggerdotdev/trigger.dev-examples", + }), + run: async (event, ctx) => { + await ctx.logger.info(`Action was ${event.action}`); + + return {}; + }, +}).listen(); + +new Trigger({ + id: "github-webhook-push", + name: "GitHub push: triggerdotdev/trigger.dev-examples", + apiKey: "trigger_dev_zC25mKNn6c0q", + endpoint: "ws://localhost:8889/ws", + logLevel: "debug", + on: github.events.pushEvent({ + repo: "triggerdotdev/trigger.dev-examples", + }), + run: async (event, ctx) => { + await ctx.logger.info(`Push with commits`, event.commits); + + return {}; + }, +}).listen(); diff --git a/packages/trigger-integrations/src/integrations/github/events.ts b/packages/trigger-integrations/src/integrations/github/events.ts index f91069708..c59d333d5 100644 --- a/packages/trigger-integrations/src/integrations/github/events.ts +++ b/packages/trigger-integrations/src/integrations/github/events.ts @@ -115,3 +115,63 @@ export function pullRequestCommentEvent(params: { github.schemas.pullRequestComments.pullRequestReviewCommentEventSchema, }; } + +export function pullRequestReviewEvent(params: { + repo: string; +}): TriggerEvent< + typeof github.schemas.pullRequestReviews.pullRequestReviewEventSchema +> { + return { + metadata: { + type: "WEBHOOK", + service: "github", + name: "pull_request_review", + filter: { + service: ["github"], + payload: { + repository: { + full_name: [params.repo], + }, + }, + event: ["pull_request_review"], + }, + source: github.schemas.WebhookSourceSchema.parse({ + subresource: "repository", + scopes: ["repo"], + repo: params.repo, + events: ["pull_request_review"], + }), + }, + schema: github.schemas.pullRequestReviews.pullRequestReviewEventSchema, + }; +} + +export function pushEvent(params: { + repo: string; +}): TriggerEvent< + typeof github.schemas.push.pushEventSchema +> { + return { + metadata: { + type: "WEBHOOK", + service: "github", + name: "push", + filter: { + service: ["github"], + payload: { + repository: { + full_name: [params.repo], + }, + }, + event: ["push"], + }, + source: github.schemas.WebhookSourceSchema.parse({ + subresource: "repository", + scopes: ["repo"], + repo: params.repo, + events: ["push"], + }), + }, + schema: github.schemas.push.pushEventSchema + }; +} diff --git a/packages/trigger-providers/src/providers/github/schemas/index.ts b/packages/trigger-providers/src/providers/github/schemas/index.ts index 7288300dd..551a809eb 100644 --- a/packages/trigger-providers/src/providers/github/schemas/index.ts +++ b/packages/trigger-providers/src/providers/github/schemas/index.ts @@ -3,6 +3,8 @@ import * as issues from "./issues"; import * as issuesComments from "./issueComments"; import * as pullRequest from "./pullRequests"; import * as pullRequestComments from "./pullRequestComments"; +import * as pullRequestReviews from "./pullRequestReviews"; +import * as push from "./pushes"; export const WebhookRepoSourceSchema = z.object({ subresource: z.literal("repository"), @@ -23,4 +25,11 @@ export const WebhookSourceSchema = z.union([ WebhookOrganizationSourceSchema, ]); -export { issues, issuesComments, pullRequest, pullRequestComments }; +export { + issues, + issuesComments, + pullRequest, + pullRequestComments, + pullRequestReviews, + push, +}; diff --git a/packages/trigger-providers/src/providers/github/schemas/pullRequestComments.ts b/packages/trigger-providers/src/providers/github/schemas/pullRequestComments.ts index a2a6b1b2c..f386dd2f2 100644 --- a/packages/trigger-providers/src/providers/github/schemas/pullRequestComments.ts +++ b/packages/trigger-providers/src/providers/github/schemas/pullRequestComments.ts @@ -1,5 +1,4 @@ import { z } from "zod"; -import { pullRequestAutoMergeSchema } from "./pullRequests"; import { userSchema, teamSchema, @@ -11,6 +10,7 @@ import { installationLiteSchema, organizationSchema, reactionsSchema, + pullRequestAutoMergeSchema, } from "./shared"; export const pullRequestReviewCommentSchema = z.object({ diff --git a/packages/trigger-providers/src/providers/github/schemas/pullRequestReviews.ts b/packages/trigger-providers/src/providers/github/schemas/pullRequestReviews.ts new file mode 100644 index 000000000..378ade5b4 --- /dev/null +++ b/packages/trigger-providers/src/providers/github/schemas/pullRequestReviews.ts @@ -0,0 +1,79 @@ +import { z } from "zod"; +import { + repositorySchema, + installationLiteSchema, + organizationSchema, + userSchema, + authorAssociationSchema, + linkSchema, + simplePullRequestSchema, +} from "./shared"; + +export const pullRequestReviewSchema = z.object({ + id: z.number(), + node_id: z.string(), + user: userSchema, + body: z.string().nullable(), + commit_id: z.string(), + submitted_at: z.string().nullable(), + state: z.union([ + z.literal("commented"), + z.literal("changes_requested"), + z.literal("approved"), + z.literal("dismissed"), + ]), + html_url: z.string(), + pull_request_url: z.string(), + author_association: authorAssociationSchema, + _links: z.object({ + html: linkSchema, + pull_request: linkSchema, + }), +}); + +export const pullRequestReviewDismissedEventSchema = z.object({ + action: z.literal("dismissed"), + review: pullRequestReviewSchema.and( + z.object({ + state: z.literal("dismissed"), + }) + ), + pull_request: simplePullRequestSchema, + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestReviewEditedEventSchema = z.object({ + action: z.literal("edited"), + changes: z.object({ + body: z + .object({ + from: z.string(), + }) + .optional(), + }), + review: pullRequestReviewSchema, + pull_request: simplePullRequestSchema, + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestReviewSubmittedEventSchema = z.object({ + action: z.literal("submitted"), + review: pullRequestReviewSchema, + pull_request: simplePullRequestSchema, + repository: repositorySchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), + sender: userSchema, +}); + +export const pullRequestReviewEventSchema = z.union([ + pullRequestReviewDismissedEventSchema, + pullRequestReviewEditedEventSchema, + pullRequestReviewSubmittedEventSchema, +]); diff --git a/packages/trigger-providers/src/providers/github/schemas/pullRequests.ts b/packages/trigger-providers/src/providers/github/schemas/pullRequests.ts index a55e85e81..448bd754b 100644 --- a/packages/trigger-providers/src/providers/github/schemas/pullRequests.ts +++ b/packages/trigger-providers/src/providers/github/schemas/pullRequests.ts @@ -6,22 +6,12 @@ import { linkSchema, milestoneSchema, organizationSchema, + pullRequestAutoMergeSchema, repositorySchema, teamSchema, userSchema, } from "./shared"; -export const pullRequestAutoMergeSchema = z.object({ - enabled_by: userSchema, - merge_method: z.union([ - z.literal("merge"), - z.literal("squash"), - z.literal("rebase"), - ]), - commit_title: z.string(), - commit_message: z.string(), -}); - export const pullRequestSchema = z.object({ url: z.string(), id: z.number(), diff --git a/packages/trigger-providers/src/providers/github/schemas/pushes.ts b/packages/trigger-providers/src/providers/github/schemas/pushes.ts new file mode 100644 index 000000000..d19965004 --- /dev/null +++ b/packages/trigger-providers/src/providers/github/schemas/pushes.ts @@ -0,0 +1,27 @@ +import { z } from "zod"; +import { + repositorySchema, + userSchema, + installationLiteSchema, + organizationSchema, + commitSchema, + committerSchema, +} from "./shared"; + +export const pushEventSchema = z.object({ + ref: z.string(), + before: z.string(), + after: z.string(), + created: z.boolean(), + deleted: z.boolean(), + forced: z.boolean(), + base_ref: z.string().nullable(), + compare: z.string(), + commits: z.array(commitSchema), + head_commit: commitSchema.nullable(), + repository: repositorySchema, + pusher: committerSchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); diff --git a/packages/trigger-providers/src/providers/github/schemas/shared.ts b/packages/trigger-providers/src/providers/github/schemas/shared.ts index 8c14efaac..d57901baa 100644 --- a/packages/trigger-providers/src/providers/github/schemas/shared.ts +++ b/packages/trigger-providers/src/providers/github/schemas/shared.ts @@ -406,3 +406,102 @@ export const labelSchema = z.object({ color: z.string(), default: z.boolean(), }); + +export const pullRequestAutoMergeSchema = z.object({ + enabled_by: userSchema, + merge_method: z.union([ + z.literal("merge"), + z.literal("squash"), + z.literal("rebase"), + ]), + commit_title: z.string(), + commit_message: z.string(), +}); + +export const simplePullRequestSchema = z.object({ + url: z.string(), + id: z.number(), + node_id: z.string(), + html_url: z.string(), + diff_url: z.string(), + patch_url: z.string(), + issue_url: z.string(), + number: z.number(), + state: z.union([z.literal("open"), z.literal("closed")]), + locked: z.boolean(), + title: z.string(), + user: userSchema, + body: z.string().nullable(), + created_at: z.string(), + updated_at: z.string(), + closed_at: z.string().nullable(), + merged_at: z.string().nullable(), + merge_commit_sha: z.string().nullable(), + assignee: userSchema.nullable(), + assignees: z.array(userSchema), + requested_reviewers: z.array(z.union([userSchema, teamSchema])), + requested_teams: z.array(teamSchema), + labels: z.array(labelSchema), + milestone: milestoneSchema.nullable(), + draft: z.boolean(), + commits_url: z.string(), + review_comments_url: z.string(), + review_comment_url: z.string(), + comments_url: z.string(), + statuses_url: z.string(), + head: z.object({ + label: z.string(), + ref: z.string(), + sha: z.string(), + user: userSchema, + repo: repositorySchema, + }), + base: z.object({ + label: z.string(), + ref: z.string(), + sha: z.string(), + user: userSchema, + repo: repositorySchema, + }), + _links: z.object({ + self: linkSchema, + html: linkSchema, + issue: linkSchema, + comments: linkSchema, + review_comments: linkSchema, + review_comment: linkSchema, + commits: linkSchema, + statuses: linkSchema, + }), + author_association: authorAssociationSchema, + auto_merge: pullRequestAutoMergeSchema.nullable(), + active_lock_reason: z + .union([ + z.literal("resolved"), + z.literal("off-topic"), + z.literal("too heated"), + z.literal("spam"), + ]) + .nullable(), +}); + +export const committerSchema = z.object({ + name: z.string(), + email: z.string().nullable(), + date: z.string().optional(), + username: z.string().optional(), +}); + +export const commitSchema = z.object({ + id: z.string(), + tree_id: z.string(), + distinct: z.boolean(), + message: z.string(), + timestamp: z.string(), + url: z.string(), + author: committerSchema, + committer: committerSchema, + added: z.array(z.string()), + modified: z.array(z.string()), + removed: z.array(z.string()), +}); From f8ad30b975dee62e1858d817a4d3bc54d5e64404 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Thu, 19 Jan 2023 15:03:24 -0800 Subject: [PATCH 32/45] Added GitHub commitComments webhook --- examples/github-webhook/src/index.ts | 16 +++++++++ .../src/integrations/github/events.ts | 36 ++++++++++++++++--- .../github/schemas/commitComments.ts | 31 ++++++++++++++++ .../src/providers/github/schemas/index.ts | 6 ++-- 4 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 packages/trigger-providers/src/providers/github/schemas/commitComments.ts diff --git a/examples/github-webhook/src/index.ts b/examples/github-webhook/src/index.ts index 5e13f23e7..aad099f82 100644 --- a/examples/github-webhook/src/index.ts +++ b/examples/github-webhook/src/index.ts @@ -80,3 +80,19 @@ new Trigger({ return {}; }, }).listen(); + +new Trigger({ + id: "github-webhook-commit_comment", + name: "GitHub commit comment: triggerdotdev/trigger.dev-examples", + apiKey: "trigger_dev_zC25mKNn6c0q", + endpoint: "ws://localhost:8889/ws", + logLevel: "debug", + on: github.events.commitCommentEvent({ + repo: "triggerdotdev/trigger.dev-examples", + }), + run: async (event, ctx) => { + await ctx.logger.info(`Push with action ${event.action}`); + + return {}; + }, +}).listen(); diff --git a/packages/trigger-integrations/src/integrations/github/events.ts b/packages/trigger-integrations/src/integrations/github/events.ts index c59d333d5..84d5b60fd 100644 --- a/packages/trigger-integrations/src/integrations/github/events.ts +++ b/packages/trigger-integrations/src/integrations/github/events.ts @@ -1,6 +1,36 @@ import { TriggerEvent } from "@trigger.dev/sdk"; import { github } from "@trigger.dev/providers"; +export function commitCommentEvent(params: { + repo: string; +}): TriggerEvent< + typeof github.schemas.commitComments.commitCommentEventSchema +> { + return { + metadata: { + type: "WEBHOOK", + service: "github", + name: "commit_comment", + filter: { + service: ["github"], + payload: { + repository: { + full_name: [params.repo], + }, + }, + event: ["commit_comment"], + }, + source: github.schemas.WebhookSourceSchema.parse({ + subresource: "repository", + scopes: ["repo"], + repo: params.repo, + events: ["commit_comment"], + }), + }, + schema: github.schemas.commitComments.commitCommentEventSchema, + }; +} + export function issueEvent(params: { repo: string; }): TriggerEvent { @@ -148,9 +178,7 @@ export function pullRequestReviewEvent(params: { export function pushEvent(params: { repo: string; -}): TriggerEvent< - typeof github.schemas.push.pushEventSchema -> { +}): TriggerEvent { return { metadata: { type: "WEBHOOK", @@ -172,6 +200,6 @@ export function pushEvent(params: { events: ["push"], }), }, - schema: github.schemas.push.pushEventSchema + schema: github.schemas.push.pushEventSchema, }; } diff --git a/packages/trigger-providers/src/providers/github/schemas/commitComments.ts b/packages/trigger-providers/src/providers/github/schemas/commitComments.ts new file mode 100644 index 000000000..6559778b4 --- /dev/null +++ b/packages/trigger-providers/src/providers/github/schemas/commitComments.ts @@ -0,0 +1,31 @@ +import { z } from "zod"; +import { + userSchema, + authorAssociationSchema, + repositorySchema, + installationLiteSchema, + organizationSchema, +} from "./shared"; + +export const commitCommentEventSchema = z.object({ + action: z.literal("created"), + comment: z.object({ + url: z.string(), + html_url: z.string(), + id: z.number(), + node_id: z.string(), + user: userSchema, + position: z.number().nullable(), + line: z.number().nullable(), + path: z.string().nullable(), + commit_id: z.string(), + created_at: z.string(), + updated_at: z.string(), + author_association: authorAssociationSchema, + body: z.string(), + }), + repository: repositorySchema, + sender: userSchema, + installation: installationLiteSchema.optional(), + organization: organizationSchema.optional(), +}); diff --git a/packages/trigger-providers/src/providers/github/schemas/index.ts b/packages/trigger-providers/src/providers/github/schemas/index.ts index 551a809eb..90f4d2639 100644 --- a/packages/trigger-providers/src/providers/github/schemas/index.ts +++ b/packages/trigger-providers/src/providers/github/schemas/index.ts @@ -1,9 +1,10 @@ import { z } from "zod"; -import * as issues from "./issues"; +import * as commitComments from "./commitComments"; import * as issuesComments from "./issueComments"; -import * as pullRequest from "./pullRequests"; +import * as issues from "./issues"; import * as pullRequestComments from "./pullRequestComments"; import * as pullRequestReviews from "./pullRequestReviews"; +import * as pullRequest from "./pullRequests"; import * as push from "./pushes"; export const WebhookRepoSourceSchema = z.object({ @@ -26,6 +27,7 @@ export const WebhookSourceSchema = z.union([ ]); export { + commitComments, issues, issuesComments, pullRequest, From cbedef660d6788d80ad6fbee78c3ebe59d35156c Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Thu, 19 Jan 2023 15:44:27 -0800 Subject: [PATCH 33/45] API key input field is now a password --- apps/webapp/app/components/integrations/AddApiKeyButton.tsx | 1 + apps/webapp/app/components/primitives/Input.tsx | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/webapp/app/components/integrations/AddApiKeyButton.tsx b/apps/webapp/app/components/integrations/AddApiKeyButton.tsx index 6c4b257d4..b59c77c02 100644 --- a/apps/webapp/app/components/integrations/AddApiKeyButton.tsx +++ b/apps/webapp/app/components/integrations/AddApiKeyButton.tsx @@ -117,6 +117,7 @@ export function AddApiKeyButton({ name="api_key" placeholder="" spellCheck={false} + type="password" /> {errors && } diff --git a/apps/webapp/app/components/primitives/Input.tsx b/apps/webapp/app/components/primitives/Input.tsx index 72c33c86a..7b69de9c5 100644 --- a/apps/webapp/app/components/primitives/Input.tsx +++ b/apps/webapp/app/components/primitives/Input.tsx @@ -6,10 +6,7 @@ const roundedStyles = { roundedFull: "rounded", }; -type InputProps = React.DetailedHTMLProps< - React.InputHTMLAttributes, - HTMLInputElement -> & { +type InputProps = JSX.IntrinsicElements["input"] & { roundedEdges?: "roundedLeft" | "roundedRight" | "roundedFull"; }; From a720e575081d2f699df435c7f7378ec581d0ea09 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Thu, 19 Jan 2023 16:46:08 -0800 Subject: [PATCH 34/45] Sort the workflow list dropdown menu, and dim the disabled ones --- apps/webapp/app/components/navigation/WorkflowMenu.tsx | 3 ++- apps/webapp/app/models/organization.server.ts | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/webapp/app/components/navigation/WorkflowMenu.tsx b/apps/webapp/app/components/navigation/WorkflowMenu.tsx index 153c3c7d4..abd5fb883 100644 --- a/apps/webapp/app/components/navigation/WorkflowMenu.tsx +++ b/apps/webapp/app/components/navigation/WorkflowMenu.tsx @@ -80,7 +80,8 @@ export function WorkflowMenu() { className={classNames( "flex items-center justify-between gap-1.5 mx-1 px-3 py-2 text-white rounded hover:bg-slate-800 transition", workflow.slug === currentWorkflow?.slug && - "!bg-slate-800" + "!bg-slate-800", + workflow.status === "DISABLED" && "opacity-50" )} >
    diff --git a/apps/webapp/app/models/organization.server.ts b/apps/webapp/app/models/organization.server.ts index f7b977a7d..43b6a96eb 100644 --- a/apps/webapp/app/models/organization.server.ts +++ b/apps/webapp/app/models/organization.server.ts @@ -28,6 +28,11 @@ export function getOrganizationFromSlug({ }, }, }, + where: { isArchived: false }, + orderBy: [ + { disabledAt: { sort: "asc", nulls: "first" } }, + { title: "asc" }, + ], }, environments: { select: { From 81f26d4ec395c1f330e79c0f77fd14bcde64a614 Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Thu, 19 Jan 2023 16:46:21 -0800 Subject: [PATCH 35/45] Slack example delay reduced to 5 seconds --- examples/send-to-slack/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/send-to-slack/src/index.ts b/examples/send-to-slack/src/index.ts index 374c22f78..9429f9e98 100644 --- a/examples/send-to-slack/src/index.ts +++ b/examples/send-to-slack/src/index.ts @@ -21,11 +21,11 @@ const trigger = new Trigger({ "Received domain.created event, waiting for 1 minutes..." ); - await ctx.waitFor("initial-wait", { minutes: 1 }); + await ctx.waitFor("initial-wait", { seconds: 5 }); const response = await slack.postMessage("send-to-slack", { channel: "test-integrations", - text: `New domain created: ${event.domain} by customer ${event.customerId}`, + text: `New domain created: ${event.domain} by customer ${event.customerId} cc @Eric #general`, }); await ctx.logger.debug("Debug message"); From a7d64470746ea0d759b547ab7fb5c40fc95b4add Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Thu, 19 Jan 2023 16:46:44 -0800 Subject: [PATCH 36/45] =?UTF-8?q?Auto-parse=20Slack=20channel=20names=20in?= =?UTF-8?q?=20message=20text,=20doesn=E2=80=99t=20work=20for=20usernames?= =?UTF-8?q?=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/internal-integrations/src/slack/index.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/internal-integrations/src/slack/index.ts b/packages/internal-integrations/src/slack/index.ts index 840df33e2..6bc91e075 100644 --- a/packages/internal-integrations/src/slack/index.ts +++ b/packages/internal-integrations/src/slack/index.ts @@ -11,9 +11,15 @@ import { slack } from "@trigger.dev/providers"; import debug from "debug"; import { getAccessToken } from "../accessInfo"; +import { z } from "zod"; const log = debug("trigger:integrations:slack"); +const SendSlackMessageRequestBodySchema = + slack.schemas.PostMessageBodySchema.extend({ + link_names: z.literal(1), + }); + class SlackRequestIntegration implements RequestIntegration { #joinChannelEndpoint = new HttpEndpoint< typeof slack.schemas.JoinConversationResponseSchema, @@ -32,7 +38,7 @@ class SlackRequestIntegration implements RequestIntegration { #postMessageEndpoint = new HttpEndpoint< typeof slack.schemas.PostMessageResponseSchema, - typeof slack.schemas.PostMessageBodySchema + typeof SendSlackMessageRequestBodySchema >({ response: slack.schemas.PostMessageResponseSchema, method: "POST", @@ -101,6 +107,7 @@ class SlackRequestIntegration implements RequestIntegration { const response = await service.performRequest(this.#postMessageEndpoint, { ...parsedParams, + link_names: 1, channel, }); From 769de0f87595bc0f8a7df1a8d0913038ed8b0f5f Mon Sep 17 00:00:00 2001 From: D-K-P Date: Thu, 19 Jan 2023 10:58:03 -0800 Subject: [PATCH 37/45] Added line numbers to code block --- apps/webapp/app/components/code/CodeBlock.tsx | 23 +- .../webapp/app/styles/prism-trigger-theme.css | 490 +++++++++++++++--- pnpm-lock.yaml | 59 ++- 3 files changed, 478 insertions(+), 94 deletions(-) diff --git a/apps/webapp/app/components/code/CodeBlock.tsx b/apps/webapp/app/components/code/CodeBlock.tsx index ff97d4d9c..e52721afd 100644 --- a/apps/webapp/app/components/code/CodeBlock.tsx +++ b/apps/webapp/app/components/code/CodeBlock.tsx @@ -1,7 +1,9 @@ -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import Prism from "prismjs"; import "prismjs/components/prism-typescript"; import "prismjs/components/prism-json"; +import "prismjs/plugins/line-numbers/prism-line-numbers"; +import "prismjs/plugins/line-numbers/prism-line-numbers.css"; import { CopyTextButton } from "../CopyTextButton"; import classNames from "classnames"; @@ -13,6 +15,7 @@ type CodeBlockProps = { showCopyButton?: boolean; align?: "top" | "center"; maxHeight?: string; + showLineNumbers?: boolean; className?: string; }; @@ -22,16 +25,19 @@ export default function CodeBlock({ showCopyButton = true, align = "center", maxHeight, + showLineNumbers = true, className, }: CodeBlockProps) { - const [codeHtml, setCodeHtml] = useState(code); + const codeRef = useRef(null); const [isCollapsed, setIsCollapsed] = useState( maxHeight === undefined ? false : true ); useEffect(() => { - const val = Prism.highlight(code, Prism.languages[language], language); - setCodeHtml(val); - }, [code, language]); + if (!codeRef.current) { + return; + } + Prism.highlightAllUnder(codeRef.current); + }, [code, language, codeRef]); return (
    -
    +      
             
           
    {showCopyButton === true && ( diff --git a/apps/webapp/app/styles/prism-trigger-theme.css b/apps/webapp/app/styles/prism-trigger-theme.css index 89a19d464..afbfa6232 100644 --- a/apps/webapp/app/styles/prism-trigger-theme.css +++ b/apps/webapp/app/styles/prism-trigger-theme.css @@ -1,17 +1,208 @@ -/* PrismJS 1.29.0 -https://prismjs.com/download.html#themes=prism-tomorrow */ +/* PrismJS 1.14.0 +http://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+abap+actionscript+ada+apacheconf+apl+applescript+c+arff+asciidoc+asm6502+aspnet+autohotkey+autoit+bash+basic+batch+bison+brainfuck+bro+cpp+csharp+arduino+coffeescript+clojure+ruby+csp+css-extras+d+dart+diff+django+docker+eiffel+elixir+elm+markup-templating+erlang+fsharp+flow+fortran+gedcom+gherkin+git+glsl+go+graphql+groovy+haml+handlebars+haskell+haxe+http+hpkp+hsts+ichigojam+icon+inform7+ini+io+j+java+jolie+json+julia+keyman+kotlin+latex+less+liquid+lisp+livescript+lolcode+lua+makefile+markdown+erb+matlab+mel+mizar+monkey+n4js+nasm+nginx+nim+nix+nsis+objectivec+ocaml+opencl+oz+parigp+parser+pascal+perl+php+php-extras+sql+powershell+processing+prolog+properties+protobuf+pug+puppet+pure+python+q+qore+r+jsx+typescript+renpy+reason+rest+rip+roboconf+crystal+rust+sas+sass+scss+scala+scheme+smalltalk+smarty+plsql+soy+stylus+swift+tcl+textile+twig+tsx+vbnet+velocity+verilog+vhdl+vim+visual-basic+wasm+wiki+xeora+xojo+yaml&plugins=line-numbers+toolbar+show-language */ /** - * prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML - * Based on https://github.com/chriskempson/tomorrow-theme - * @author Rose Pritchard + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou */ +/* +* Dracula Theme for Prism.JS +* +* @author Gustavo Costa +* e-mail: gusbemacbe@gmail.com +* +* @contributor Jon Leopard +* e-mail: jonlprd@gmail.com +* +* @license MIT 2016-2020 +*/ + +/* Scrollbars */ + +:root { + --background: #0f172a; + --comment: #6272a4; + --foreground: #f8f8f2; + --selection: #44475a; + + --cyan: #8be9fd; + --green: #50fa7b; + --orange: #ffb86c; + --pink: #ff79c6; + --purple: #bd93f9; + --red: #ff5555; + --yellow: #f1fa8c; + + /* Transparency */ + + /** 30% of transparency **/ + --background-30: #282a3633; + --comment-30: #6272a433; + --foreground-30: #f8f8f233; + --selection-30: #44475a33; + + --cyan-30: #8be9fd33; + --green-30: #50fa7b33; + --orange-30: #ffb86c33; + --pink-30: #ff79c633; + --purple-30: #bd93f933; + --red-30: #ff555533; + --yellow-30: #f1fa8c33; + + /** 40% of transparency **/ + --background-40: #282a3666; + --comment-40: #6272a466; + --foreground-40: #f8f8f266; + --selection-40: #44475a66; + + --cyan-40: #8be9fd66; + --green-40: #50fa7b66; + --orange-40: #ffb86c66; + --pink-40: #ff79c666; + --purple-40: #bd93f966; + --red-40: #ff555566; + --yellow-40: #f1fa8c66; +} + +/* Selection */ + +pre[class*="language-"]::-moz-selection, +pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, +code[class*="language-"] ::-moz-selection { + text-shadow: none; + background-color: var(--selection); +} + +pre[class*="language-"]::selection, +pre[class*="language-"] ::selection, +code[class*="language-"]::selection, +code[class*="language-"] ::selection { + text-shadow: none; + background-color: var(--selection); +} + +/* Line numbers */ + +pre.line-numbers { + position: relative; + padding-left: 3.8em; + counter-reset: linenumber; +} + +pre.line-numbers > code { + position: relative; + white-space: inherit; +} + +.line-numbers .line-numbers-rows { + position: absolute; + pointer-events: none; + top: 0; + font-size: 100%; + left: -3.8em; + width: 3em; /* works for line-numbers below 1000 lines */ + letter-spacing: -1px; + border-right: 1px solid rgb(153, 153, 153); + + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.line-numbers-rows > span { + pointer-events: none; + display: block; + counter-increment: linenumber; +} + +.line-numbers-rows > span:before { + content: counter(linenumber); + color: #999; + display: block; + padding-right: 0.8em; + text-align: right; +} + +/* Toolbar for copying */ + +div.code-toolbar { + position: relative; +} + +div.code-toolbar > .toolbar { + position: absolute; + top: 0.3em; + right: 0.2em; + transition: opacity 0.3s ease-in-out; + opacity: 0; +} + +div.code-toolbar:hover > .toolbar { + opacity: 1; +} + +div.code-toolbar > .toolbar .toolbar-item { + display: inline-block; + padding-right: 20px; +} + +div.code-toolbar > .toolbar a { + cursor: pointer; +} + +div.code-toolbar > .toolbar button { + background: none; + border: 0; + color: inherit; + font: inherit; + line-height: normal; + overflow: visible; + padding: 0; + -webkit-user-select: none; /* for button */ + -moz-user-select: none; + -ms-user-select: none; +} + +div.code-toolbar > .toolbar a, +div.code-toolbar > .toolbar button, +div.code-toolbar > .toolbar span { + color: var(--foreground); + font-size: 0.8em; + padding: 0.5em; + background: var(--comment); + border-radius: 0.5em; +} + +div.code-toolbar > .toolbar a:hover, +div.code-toolbar > .toolbar a:focus, +div.code-toolbar > .toolbar button:hover, +div.code-toolbar > .toolbar button:focus, +div.code-toolbar > .toolbar span:hover, +div.code-toolbar > .toolbar span:focus { + color: inherit; + text-decoration: none; + background-color: var(--green); +} + +/* Remove text shadow for printing */ + +@media print { + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + code[class*="language-"], pre[class*="language-"] { - color: #ccc; - background: none; - font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; - font-size: 0.95em; + color: var(--foreground); + background: var(--background); + text-shadow: none; + font-family: PT Mono, Consolas, Monaco, "Andale Mono", "Ubuntu Mono", + monospace; text-align: left; white-space: pre; word-spacing: normal; @@ -19,9 +210,9 @@ pre[class*="language-"] { word-wrap: normal; line-height: 1.5; - -moz-tab-size: 2; - -o-tab-size: 2; - tab-size: 2; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; -webkit-hyphens: none; -moz-hyphens: none; @@ -30,94 +221,273 @@ pre[class*="language-"] { } /* Code blocks */ + pre[class*="language-"] { - padding: 0.5em; + background: var(--background); + border-radius: 0.5em; + padding: 1em; margin: 0.5em 0; overflow: auto; + height: auto; } :not(pre) > code[class*="language-"], pre[class*="language-"] { - background: #0f172a; + background: var(--background); } /* Inline code */ :not(pre) > code[class*="language-"] { - padding: 0.1em; + padding: 4px 7px; border-radius: 0.3em; white-space: normal; } -.token.comment, -.token.block-comment, -.token.prolog, -.token.doctype, -.token.cdata { - color: #999; +/* Code box limit */ + +.limit-300 { + height: 300px !important; } -.token.punctuation { - color: #ccc; +.limit-300 { + height: 400px !important; } -.token.tag, -.token.attr-name, -.token.namespace, -.token.deleted { - color: #e2777a; +.limit-500 { + height: 500px !important; } -.token.function-name { - color: #6196cc; +.limit-600 { + height: 600px !important; } -.token.boolean, -.token.number, -.token.function { - color: #f08d49; +.limit-700 { + height: 700px !important; } -.token.property, -.token.class-name, -.token.constant, -.token.symbol { - color: #f8c555; +.limit-800 { + height: 800px !important; } -.token.selector, -.token.important, -.token.atrule, -.token.keyword, -.token.builtin { - color: #cc99cd; +.language-css { + color: var(--purple); } -.token.string, -.token.char, -.token.attr-value, -.token.regex, -.token.variable { - color: #7ec699; +.token { + color: var(--pink); } -.token.operator, -.token.entity, -.token.url { - color: #67cdcc; +.language-css .token { + color: var(--pink); +} + +.token.script { + color: var(--foreground); } -.token.important, .token.bold { font-weight: bold; } + .token.italic { font-style: italic; } -.token.entity { - cursor: help; +.token.atrule, +.token.attr-name, +.token.attr-value { + color: var(--green); } -.token.inserted { - color: green; +.language-css .token.atrule { + color: var(--purple); +} + +.language-html .token.attr-value, +.language-markup .token.attr-value { + color: var(--yellow); +} + +.token.boolean { + color: var(--purple); +} + +.token.builtin, +.token.class-name { + color: var(--cyan); +} + +.token.comment { + color: var(--comment); +} + +.token.constant { + color: var(--purple); +} + +.language-javascript .token.constant { + color: var(--orange); + font-style: italic; +} + +.token.entity { + color: var(--pink); +} + +.language-css .token.entity { + color: var(--green); +} + +.language-html .token.entity.named-entity { + color: var(--purple); +} + +.language-html .token.entity:not(.named-entity) { + color: var(--pink); +} + +.language-markup .token.entity.named-entity { + color: var(--purple); +} + +.language-markup .token.entity:not(.named-entity) { + color: var(--pink); +} + +.token.function { + color: var(--green); +} + +.language-css .token.function { + color: var(--cyan); +} + +.token.important, +.token.keyword { + color: var(--pink); +} + +.token.prolog { + color: var(--foreground); +} + +.token.property { + color: var(--orange); +} + +.language-css .token.property { + color: var(--cyan); +} + +.token.punctuation { + color: var(--pink); +} + +.language-css .token.punctuation { + color: var(--orange); +} + +.language-html .token.punctuation, +.language-markup .token.punctuation { + color: var(--foreground); +} + +.token.selector { + color: var(--pink); +} + +.language-css .token.selector { + color: var(--green); +} + +.token.regex { + color: var(--red); +} + +.language-css .token.rule:not(.atrule) { + color: var(--foreground); +} + +.token.string { + color: var(--yellow); +} + +.token.tag { + color: var(--pink); +} + +.token.url { + color: var(--cyan); +} + +.language-css .token.url { + color: var(--orange); +} + +.token.variable { + color: var(--comment); +} + +.token.number { + color: rgba(189, 147, 249, 1); +} + +.token.operator { + color: rgba(139, 233, 253, 1); +} + +.token.char { + color: rgba(255, 135, 157, 1); +} + +.token.symbol { + color: rgba(255, 184, 108, 1); +} + +.token.deleted { + color: #e2777a; +} + +.token.namespace { + color: #e2777a; +} + +/* Line Highlighter */ + +.highlight-line { + color: inherit; + display: inline-block; + text-decoration: none; + + border-radius: 4px; + padding: 2px 10px; +} + +.highlight-line:empty:before { + content: " "; +} + +.highlight-line:not(:last-child) { + min-width: 100%; +} + +.highlight-line .highlight-line:not(:last-child) { + min-width: 0; +} + +.highlight-line-isdir { + color: var(--foreground); + background-color: var(--selection-30); +} + +.highlight-line-active { + background-color: var(--comment-30); +} + +.highlight-line-add { + background-color: var(--green-30); +} + +.highlight-line-remove { + background-color: var(--red-30); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ddffbbcef..44bf91fe2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -179,7 +179,7 @@ importers: '@aws-sdk/client-s3': 3.245.0 '@aws-sdk/s3-request-presigner': 3.245.0 '@cfworker/json-schema': 1.12.5 - '@codemirror/autocomplete': 6.4.0_czcfkg2f66rxeiodoti7r2gulu + '@codemirror/autocomplete': 6.4.0_eo6pz6bvsllvatnnwfprpuflde '@codemirror/commands': 6.1.3 '@codemirror/lang-javascript': 6.1.2 '@codemirror/lang-json': 6.0.1 @@ -204,7 +204,7 @@ importers: '@tanstack/react-table': 8.7.6_biqbaboplfbrettd7655fr4n2y '@trigger.dev/common-schemas': link:../../packages/common-schemas '@trigger.dev/providers': link:../../packages/trigger-providers - '@uiw/react-codemirror': 4.19.5_k4ec5g7vuuzzonc3d6xbjnmmle + '@uiw/react-codemirror': 4.19.5_aguurb4bmecpxzejz52amioxne bcryptjs: 2.4.3 classnames: 2.3.2 clsx: 1.2.1 @@ -647,7 +647,7 @@ importers: '@urql/core': 3.1.1_graphql@16.6.0 debug: 4.3.4 graphql: 16.6.0 - urql: 3.0.3_graphql@16.6.0 + urql: 3.0.3_onqnqwb3ubg5opvemcqf7c2qhy zod: 3.20.2 devDependencies: '@trigger.dev/providers': link:../trigger-providers @@ -3397,12 +3397,13 @@ packages: prettier: 2.8.2 dev: false - /@codemirror/autocomplete/6.4.0_czcfkg2f66rxeiodoti7r2gulu: + /@codemirror/autocomplete/6.4.0_eo6pz6bvsllvatnnwfprpuflde: resolution: {integrity: sha512-HLF2PnZAm1s4kGs30EiqKMgD7XsYaQ0XJnMR0rofEWQ5t5D60SfqpDIkIh1ze5tiEbyUWm8+VJ6W1/erVvBMIA==} peerDependencies: '@codemirror/language': ^6.0.0 '@codemirror/state': ^6.0.0 '@codemirror/view': ^6.0.0 + '@lezer/common': ^1.0.0 dependencies: '@codemirror/language': 6.3.2 '@codemirror/state': 6.2.0 @@ -3422,7 +3423,7 @@ packages: /@codemirror/lang-javascript/6.1.2: resolution: {integrity: sha512-OcwLfZXdQ1OHrLiIcKCn7MqZ7nx205CMKlhe+vL88pe2ymhT9+2P+QhwkYGxMICj8TDHyp8HFKVwpiisUT7iEQ==} dependencies: - '@codemirror/autocomplete': 6.4.0_czcfkg2f66rxeiodoti7r2gulu + '@codemirror/autocomplete': 6.4.0_eo6pz6bvsllvatnnwfprpuflde '@codemirror/language': 6.3.2 '@codemirror/lint': 6.1.0 '@codemirror/state': 6.2.0 @@ -4660,7 +4661,7 @@ packages: eslint: 8.31.0 eslint-import-resolver-node: 0.3.6 eslint-import-resolver-typescript: 3.5.3_hnftvkj7qg3s6bbigj4pr6djxy - eslint-plugin-import: 2.27.4_2ac3tknkazjoq5fxmuugu665ny + eslint-plugin-import: 2.27.4_qdjeohovcytra7xto5vgmxssaq eslint-plugin-jest: 26.9.0_ohsifnwenhmxgcp7mend4dnv74 eslint-plugin-jest-dom: 4.0.3_eslint@8.31.0 eslint-plugin-jsx-a11y: 6.7.1_eslint@8.31.0 @@ -5792,17 +5793,18 @@ packages: eslint-visitor-keys: 3.3.0 dev: true - /@uiw/codemirror-extensions-basic-setup/4.19.5_wd2tsis3in55bkaiwnc2c46tom: + /@uiw/codemirror-extensions-basic-setup/4.19.5_tbeldtdcrf45b35pezgkzq2u4e: resolution: {integrity: sha512-1zt7ZPJ01xKkSW/KDy0FZNga0bngN1fC594wCVG7FBi60ehfcAucpooQ+JSPScKXopxcb+ugPKZvVLzr9/OfzA==} peerDependencies: '@codemirror/autocomplete': '>=6.0.0' '@codemirror/commands': '>=6.0.0' '@codemirror/language': '>=6.0.0' + '@codemirror/lint': '>=6.0.0' '@codemirror/search': '>=6.0.0' '@codemirror/state': '>=6.0.0' '@codemirror/view': '>=6.0.0' dependencies: - '@codemirror/autocomplete': 6.4.0_czcfkg2f66rxeiodoti7r2gulu + '@codemirror/autocomplete': 6.4.0_eo6pz6bvsllvatnnwfprpuflde '@codemirror/commands': 6.1.3 '@codemirror/language': 6.3.2 '@codemirror/lint': 6.1.0 @@ -5811,11 +5813,14 @@ packages: '@codemirror/view': 6.7.2 dev: false - /@uiw/react-codemirror/4.19.5_k4ec5g7vuuzzonc3d6xbjnmmle: + /@uiw/react-codemirror/4.19.5_aguurb4bmecpxzejz52amioxne: resolution: {integrity: sha512-ZCHh8d7beXbF8/t7F1+yHht6A9Y6CdKeOkZq4A09lxJEnyTQrj1FMf2zvfaqc7K23KNjkTCtSlbqKKbVDgrWaw==} peerDependencies: + '@babel/runtime': '>=7.11.0' '@codemirror/state': '>=6.0.0' + '@codemirror/theme-one-dark': '>=6.0.0' '@codemirror/view': '>=6.0.0' + codemirror: '>=6.0.0' react: '>=16.8.0' react-dom: '>=16.8.0' dependencies: @@ -5824,13 +5829,14 @@ packages: '@codemirror/state': 6.2.0 '@codemirror/theme-one-dark': 6.1.0 '@codemirror/view': 6.7.2 - '@uiw/codemirror-extensions-basic-setup': 4.19.5_wd2tsis3in55bkaiwnc2c46tom - codemirror: 6.0.1 + '@uiw/codemirror-extensions-basic-setup': 4.19.5_tbeldtdcrf45b35pezgkzq2u4e + codemirror: 6.0.1_@lezer+common@1.0.2 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 transitivePeerDependencies: - '@codemirror/autocomplete' - '@codemirror/language' + - '@codemirror/lint' - '@codemirror/search' dev: false @@ -7156,16 +7162,18 @@ packages: engines: {node: '>=0.10.0'} dev: false - /codemirror/6.0.1: + /codemirror/6.0.1_@lezer+common@1.0.2: resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==} dependencies: - '@codemirror/autocomplete': 6.4.0_czcfkg2f66rxeiodoti7r2gulu + '@codemirror/autocomplete': 6.4.0_eo6pz6bvsllvatnnwfprpuflde '@codemirror/commands': 6.1.3 '@codemirror/language': 6.3.2 '@codemirror/lint': 6.1.0 '@codemirror/search': 6.2.3 '@codemirror/state': 6.2.0 '@codemirror/view': 6.7.2 + transitivePeerDependencies: + - '@lezer/common' dev: false /collection-visit/1.0.0: @@ -8479,7 +8487,7 @@ packages: eslint: 8.31.0 eslint-import-resolver-node: 0.3.7 eslint-import-resolver-typescript: 2.7.1_hnftvkj7qg3s6bbigj4pr6djxy - eslint-plugin-import: 2.27.4_2es4x7ly2gmvaoztzzbbx3tgsy + eslint-plugin-import: 2.27.4_eslint@8.31.0 eslint-plugin-jsx-a11y: 6.7.1_eslint@8.31.0 eslint-plugin-react: 7.31.8_eslint@8.31.0 eslint-plugin-react-hooks: 4.6.0_eslint@8.31.0 @@ -8535,7 +8543,7 @@ packages: dependencies: debug: 4.3.4 eslint: 8.31.0 - eslint-plugin-import: 2.27.4_2es4x7ly2gmvaoztzzbbx3tgsy + eslint-plugin-import: 2.27.4_eslint@8.31.0 glob: 7.2.3 is-glob: 4.0.3 resolve: 1.22.1 @@ -8554,7 +8562,7 @@ packages: debug: 4.3.4 enhanced-resolve: 5.12.0 eslint: 8.31.0 - eslint-plugin-import: 2.27.4_2ac3tknkazjoq5fxmuugu665ny + eslint-plugin-import: 2.27.4_qdjeohovcytra7xto5vgmxssaq get-tsconfig: 4.3.0 globby: 13.1.3 is-core-module: 2.11.0 @@ -8564,7 +8572,7 @@ packages: - supports-color dev: true - /eslint-module-utils/2.7.4_co4ldsxivxjwelu6vt6qmibemq: + /eslint-module-utils/2.7.4_py7h5widiydke2tta7oabbx7ru: resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} engines: {node: '>=4'} peerDependencies: @@ -8585,16 +8593,14 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.48.1_iukboom6ndih5an6iafl45j2fe debug: 3.2.7 eslint: 8.31.0 eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 2.7.1_hnftvkj7qg3s6bbigj4pr6djxy transitivePeerDependencies: - supports-color dev: true - /eslint-module-utils/2.7.4_v73lhamtbyinynmwa5fn7kpmfq: + /eslint-module-utils/2.7.4_sqt5xxn4ciiurbqrzlaarm6ama: resolution: {integrity: sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==} engines: {node: '>=4'} peerDependencies: @@ -8619,7 +8625,6 @@ packages: debug: 3.2.7 eslint: 8.31.0 eslint-import-resolver-node: 0.3.7 - eslint-import-resolver-typescript: 3.5.3_hnftvkj7qg3s6bbigj4pr6djxy transitivePeerDependencies: - supports-color dev: true @@ -8644,7 +8649,7 @@ packages: regexpp: 3.2.0 dev: true - /eslint-plugin-import/2.27.4_2ac3tknkazjoq5fxmuugu665ny: + /eslint-plugin-import/2.27.4_eslint@8.31.0: resolution: {integrity: sha512-Z1jVt1EGKia1X9CnBCkpAOhWy8FgQ7OmJ/IblEkT82yrFU/xJaxwujaTzLWqigewwynRQ9mmHfX9MtAfhxm0sA==} engines: {node: '>=4'} peerDependencies: @@ -8654,7 +8659,6 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.48.1_iukboom6ndih5an6iafl45j2fe array-includes: 3.1.6 array.prototype.flat: 1.3.1 array.prototype.flatmap: 1.3.1 @@ -8662,7 +8666,7 @@ packages: doctrine: 2.1.0 eslint: 8.31.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.7.4_v73lhamtbyinynmwa5fn7kpmfq + eslint-module-utils: 2.7.4_py7h5widiydke2tta7oabbx7ru has: 1.0.3 is-core-module: 2.11.0 is-glob: 4.0.3 @@ -8677,7 +8681,7 @@ packages: - supports-color dev: true - /eslint-plugin-import/2.27.4_2es4x7ly2gmvaoztzzbbx3tgsy: + /eslint-plugin-import/2.27.4_qdjeohovcytra7xto5vgmxssaq: resolution: {integrity: sha512-Z1jVt1EGKia1X9CnBCkpAOhWy8FgQ7OmJ/IblEkT82yrFU/xJaxwujaTzLWqigewwynRQ9mmHfX9MtAfhxm0sA==} engines: {node: '>=4'} peerDependencies: @@ -8695,7 +8699,7 @@ packages: doctrine: 2.1.0 eslint: 8.31.0 eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.7.4_co4ldsxivxjwelu6vt6qmibemq + eslint-module-utils: 2.7.4_sqt5xxn4ciiurbqrzlaarm6ama has: 1.0.3 is-core-module: 2.11.0 is-glob: 4.0.3 @@ -15934,7 +15938,7 @@ packages: prepend-http: 2.0.0 dev: true - /urql/3.0.3_graphql@16.6.0: + /urql/3.0.3_onqnqwb3ubg5opvemcqf7c2qhy: resolution: {integrity: sha512-aVUAMRLdc5AOk239DxgXt6ZxTl/fEmjr7oyU5OGo8uvpqu42FkeJErzd2qBzhAQ3DyusoZIbqbBLPlnKo/yy2A==} peerDependencies: graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 @@ -15942,6 +15946,7 @@ packages: dependencies: '@urql/core': 3.1.1_graphql@16.6.0 graphql: 16.6.0 + react: 18.2.0 wonka: 6.1.2 dev: false From 07b8f76842b1907bd2b3f3bece15d9e96e0d9437 Mon Sep 17 00:00:00 2001 From: D-K-P Date: Thu, 19 Jan 2023 17:02:25 -0800 Subject: [PATCH 38/45] Replaced the favicon --- apps/webapp/public/favicon.ico | Bin 15086 -> 15406 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/webapp/public/favicon.ico b/apps/webapp/public/favicon.ico index f350848be3d488c762057f7f2f52f1462a57e9aa..b64cf3a11c75fd2903607e2706039128a7862d22 100644 GIT binary patch literal 15406 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 literal 15086 zcmeI230zlI7RUb!e-_z6L1g<28iE_*0=NN!3bOeJv{YPM(9}#bw^Fe*mufy^E@Ndn zEoM5ErIt=wQE4TPrH*FJq%xVgrj{)(=;?Rf`@hS3k3EHOJ{>+^&b{y6d(L^w|Gm2z z3`T?55FKrRwi;e(X)ts#7z|dcJ?~{O9KkU!Xf7Y^U@$y}3awBFO$;LdAD5(stp5Dp zAg_QKQAL1EFtCBrA91wau9EC3uc-9o&CSlvNa zaPk(4Te-$RA!dHgCxL59{N_ex@`!RJkJfJ0z;)^ui>HGUyP}sP?&;vV62D83GbQeb zrOdO*lzF-rW!T|#8A(Vdp-k4CR+Di$5&4>ufVvfvp&UPxKz$CzlV>|OC2mEpLfmQK zf)YRL+a`xo-nDGXqsquaS!HCRtfpk3ZdIhCJe>xhPW0j^GMbG_+^jEyF~AMH3PhQc zpA#kR?1i=iDHpBPnCl6)vUoPsE{V6TVj^v;;2X$pQt-y6t|I>%uTa~-^ zI}vjLU`YUHfjEcYJ3`6!%OJGQuYw^cs|kZquR7RQ1zfuW&qFH_mp#T`p$7p)_}jGv zhdQ#rEr+6g((g#AA<*pOZvTkxa!w@h&nt z7%6&565^Ny9~14|56w)(JF0-9X2(*C@$IR_gj#Z6d%A5(45c0!0s9kR?>-HDEPg{e zIk;K$@h?M+=%rRD{Cn61L>rGNUrIeZ8a}4O{!|UN%abW|cw13d-L58Szus2LI>&K8 zSdPrIB0@2nvz@2*S;%9k2{LCvy)SMK; zAVbtwx2-H~9odm=mkVL*Q4M)C7thp0eB1r>_IAUT(q}`mN=ZiPIUsC=&623Tn2wj@jNY|lzr34Ix3bd9?qhkx=m&2$eF2VzZiD3 zEcvCh(fcWZ4<0Yxh>S$z%Rq0tR(S_^7wWoc24dfUxL?(fORrPR(* z+j>C1t+T85qsf~r7-twYeO)})y{P}CHxTz`6`OEAZ{hc_3VnO4#s>U-2KfHp4CxCa z=YigbCeLyE_w7s_S5LwFyDT2(X1tkhw}WXe3h1;53QDSFuh_6XBK;z5F&xcx0&ZoYIH{km2Mr_5<#gy^=Hfo#@gK>uy{U5|n6NvNMYB)}5$99ufhjQPleXtn{>O<1uVM@J zV&`+Hny5#n==QAx5vXP{D*)z2VI)i3FARSmf;9avmS*!pAXNe^87a6 zr-m#%x19%y0AKIZpcvc(_;-G*2+vzYf;ND^5r4}6;T0fB@B`E7B0g8F>J_p^4@IMK zNXjc(RbGT++)mLYoJa4Hf6zi>W`rlid9hc7fCUx;wjx(Mz# zujmrE;k=^Zn0vegM~V*RJRBydH5|_7yrMZRQK)F6oG&tKb%~RlSM&u>Ie#fs)};xu z<_pX=wYrtTV6cKjAogi_A=5C$g0T3VkO-3M81}(y2zYuVal`+)fcqG~)Dvgmhu5{a zuf^|g_KUk%uW(=WA@BaJwenQu&xZbJP_rzV00h_WHrY8r6c~rKKL*B!|`#7}pL%t%?DDQe2MLry^zRrB^JYGM_{vCZB zspq?iXnzOBD7UNN{T>ye-t!^v{gC&7C_6cnT^_6lU~Mm%Tmr?Ofm86=8?ej-Qd|L9 z!PNiaQ25jCQQ$oXdDz<^YfkIo^&f@dXs<8~>0y*}IiIW}H0Qn;KJiACgy-{lUY|_P zX6o`%Z`c}-c4Gxu7w)3IUk$E+K{%%3%RyNG=h(G-8M?pS59^6yocc9<@heI#qmYr- zV|=;j-9yPe(ubV<8mu z&U?b9s2$tWJg>eLNMpLT3sYcY4%+itav|1pXHi%|XT?UPIxshi`v1&(3d+{vJFd)7 zuK|4uA5HB(^(qwyegNbe?n8aPn2Z>BE%RKrf_z z-%Ka3=l~^N?&}WGJ5}W!kzNI)`r+dfD7SPO)>Y?Y?jZy7Yu<{T3U!MKNt9E{^F&Jo zQH5#hx^KcZzi+FO7!-aB$f=1t)^IY(Cq4}CC-Kfu+Y@QG7XH-d5U<~^0?*;xS5wN~ zLiJkk_pr&|mg)q(4r$M+Y+JWphBDz(Npoy%ALsp4>o6~{R>F4WC2D?mlDh74)OP?i zkx*3H57h0;(uhetNA45X3is$m_}>KI8=*I2&U6l0yj+zwM`4Tm+qIDbRMyPrBad|v zS9~L~DSG)t*mslWp1Qou`$XP{@9p4S%&Bdnrs-W2-`wSzQ5(nhy57*+fn`zQAO3HTUs?802-OvEMD5dH-lfp(j2_QTHs>htABtPi}3^uKY8*OK;Qj&VDAcZyQZo!@7}YwJ{1pZh2;0?A&>Nu$Vi zjpvcSwxg__a9%#%dfp`UoYY%r%j2yZ5uOMA4&WLzri7hu;(U1y^|+1`nA^h~d~>n4 z={(vf-?tm_&%ej{?4Jo5m_^yYl)$%kEraLe!p4kHt~={z0rx9!hJ?Wg&r6cIkqgDW z_8hK3*?I)0d%X7=`VU@;|No(G`=4}eeo~_WcO8k zv!1PLce9e*S1kh4+*SLh=EC?pf1D)qdd=tzCcUrd!(4s zPaPni*cin(zpw793&(bwJU_PR~ zq}8QkyKNKnLZDo8MX$s$?myIr_`%6iz`wI&;O`&=)Ti*dS^>Ws_&t3~iac*XRETK; zMmY&i7s;4QaDEI=c`%sr2xagxO5kY?1`mNe4N-{c42GvOceC*o6lH zJS|}bVxFSnUK0QIxDUhMquRh@eOE9S{j}rQ>wm^*Hq;Q*H5vKa_ZRTFM;mwQ{9-i4 zt&gF&b!s{wuVZ~Qby?G%ykew1k6)qvREwwOa`!Ds7)cJpXmF?mrw1afFaZ1R2VhVA zKpbrbpNM_B*%UFY1GSnFBM~vR1I5455AWm&7(;OMm@dXVlMVZeFrFBUeiHVC*?-HtN3h#l9NS|!&s-i0^1Q_D+|Vx0 z&}%%~1!5o5?c@^&`>Ou2b!0cnxw;%W20-Kcmw7$0f+0+AbQ0eX+wRayIY#-$A{iV?pJj-a!rnoKl9pr?D}Ug zHgv|%(vV+WBUL&7rq;PfgkxV_1fTat;HE0CNwO0vam;%_*P1lY)#3Ze Date: Fri, 20 Jan 2023 08:39:58 -0800 Subject: [PATCH 39/45] =?UTF-8?q?Added=20ability=20to=20timeout=20triggers?= =?UTF-8?q?=20so=20they=20don=E2=80=99t=20run=20too=20late=20after=20the?= =?UTF-8?q?=20event=20time?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/good-pillows-drum.md | 5 ++ apps/webapp/app/components/runs/runStatus.tsx | 13 ++++++ apps/webapp/app/models/workflowRun.server.ts | 1 + .../app/services/messageBroker.server.ts | 12 +++++ .../services/runs/runTriggerTimeout.server.ts | 37 +++++++++++++++ .../scheduler/deliverScheduledEvent.server.ts | 1 + .../workflows/registerWorkflow.server.ts | 2 + .../migration.sql | 2 + .../migration.sql | 2 + .../migration.sql | 3 ++ apps/webapp/prisma/schema.prisma | 6 +++ apps/wss/src/server.ts | 30 +++++++++++- examples/playground/src/index.ts | 1 + examples/smoke-test/src/index.ts | 1 + .../internal-bridge/src/schemas/server.ts | 1 + .../src/messages/catalogs/triggers.ts | 5 +- .../src/messages/schemas/workflowRuns.ts | 8 ++++ .../src/messages/zodPublisher.ts | 2 + .../src/messages/zodSubscriber.ts | 46 +++++++++++++++++-- .../src/schemas/workflows.ts | 1 + packages/trigger-sdk/src/client.ts | 1 + packages/trigger-sdk/src/trigger/index.ts | 7 +++ 22 files changed, 181 insertions(+), 6 deletions(-) create mode 100644 .changeset/good-pillows-drum.md create mode 100644 apps/webapp/app/services/runs/runTriggerTimeout.server.ts create mode 100644 apps/webapp/prisma/migrations/20230120010921_add_trigger_ttl_in_seconds/migration.sql create mode 100644 apps/webapp/prisma/migrations/20230120014606_add_timed_out_status_for_workflow_runs/migration.sql create mode 100644 apps/webapp/prisma/migrations/20230120014641_add_timed_out_columns/migration.sql diff --git a/.changeset/good-pillows-drum.md b/.changeset/good-pillows-drum.md new file mode 100644 index 000000000..c31981c06 --- /dev/null +++ b/.changeset/good-pillows-drum.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/sdk": patch +--- + +Added triggerTTL option that prevents old events from running a workflow diff --git a/apps/webapp/app/components/runs/runStatus.tsx b/apps/webapp/app/components/runs/runStatus.tsx index a87280da8..7f2c66de4 100644 --- a/apps/webapp/app/components/runs/runStatus.tsx +++ b/apps/webapp/app/components/runs/runStatus.tsx @@ -21,6 +21,8 @@ export function runStatusTitle(status: WorkflowRunStatus): string { return "Disconnected"; case "ERROR": return "Error"; + case "TIMED_OUT": + return "Timed out"; } } @@ -36,6 +38,8 @@ export function runStatusLabel(status: WorkflowRunStatus): ReactNode { return {runStatusTitle(status)}; case "ERROR": return {runStatusTitle(status)}; + case "TIMED_OUT": + return {runStatusTitle(status)}; } } @@ -91,5 +95,14 @@ export function runStatusIcon( )} /> ); + case "TIMED_OUT": + return ( + + ); } } diff --git a/apps/webapp/app/models/workflowRun.server.ts b/apps/webapp/app/models/workflowRun.server.ts index 663e00bac..aaa619493 100644 --- a/apps/webapp/app/models/workflowRun.server.ts +++ b/apps/webapp/app/models/workflowRun.server.ts @@ -19,6 +19,7 @@ export async function findWorklowRunById(id: string) { include: { event: true, environment: true, + workflow: true, }, }); } diff --git a/apps/webapp/app/services/messageBroker.server.ts b/apps/webapp/app/services/messageBroker.server.ts index 08f17a71e..da0b67b6b 100644 --- a/apps/webapp/app/services/messageBroker.server.ts +++ b/apps/webapp/app/services/messageBroker.server.ts @@ -43,6 +43,7 @@ import { WaitForConnection } from "./requests/waitForConnection.server"; import { WorkflowRunDisconnected } from "./runs/runDisconnected.server"; import { DeliverScheduledEvent } from "./scheduler/deliverScheduledEvent.server"; import { RegisterSchedulerSource } from "./scheduler/registerSchedulerSource.server"; +import { WorkflowRunTriggerTimeout } from "./runs/runTriggerTimeout.server"; let pulsarClient: PulsarClient; let triggerPublisher: ZodPublisher; @@ -206,6 +207,13 @@ function createCommandSubscriber() { return !!success; }, + WORKFLOW_RUN_TRIGGER_TIMEOUT: async (id, data, properties) => { + const service = new WorkflowRunTriggerTimeout(); + + await service.call(data); + + return true; + }, SEND_INTEGRATION_REQUEST: async (id, data, properties) => { const service = new CreateIntegrationRequest(); @@ -526,6 +534,10 @@ function createTaskQueue() { "x-workflow-id": run.workflowId, "x-env": run.environment.slug, "x-workflow-run-id": run.id, + "x-ttl": run.workflow.triggerTtlInSeconds, + }, + { + eventTimestamp: run.event.timestamp.getTime(), } ); diff --git a/apps/webapp/app/services/runs/runTriggerTimeout.server.ts b/apps/webapp/app/services/runs/runTriggerTimeout.server.ts new file mode 100644 index 000000000..db7464e3d --- /dev/null +++ b/apps/webapp/app/services/runs/runTriggerTimeout.server.ts @@ -0,0 +1,37 @@ +import type { PrismaClient } from "~/db.server"; +import { prisma } from "~/db.server"; + +export class WorkflowRunTriggerTimeout { + #prismaClient: PrismaClient; + + constructor(prismaClient: PrismaClient = prisma) { + this.#prismaClient = prismaClient; + } + + async call(data: { id: string; ttl: number; elapsedSeconds: number }) { + const workflowRun = await this.#prismaClient.workflowRun.findUnique({ + where: { id: data.id }, + include: { + event: true, + environment: true, + }, + }); + + if (!workflowRun) { + throw new Error("Workflow run not found"); + } + + if (workflowRun.status !== "PENDING") { + return; + } + + await this.#prismaClient.workflowRun.update({ + where: { id: data.id }, + data: { + status: "TIMED_OUT", + timedOutAt: new Date(), + timedOutReason: `Trigger timed out after ${data.elapsedSeconds}s because it exceeded the TTL of ${data.ttl}s`, + }, + }); + } +} diff --git a/apps/webapp/app/services/scheduler/deliverScheduledEvent.server.ts b/apps/webapp/app/services/scheduler/deliverScheduledEvent.server.ts index 52f420440..190400195 100644 --- a/apps/webapp/app/services/scheduler/deliverScheduledEvent.server.ts +++ b/apps/webapp/app/services/scheduler/deliverScheduledEvent.server.ts @@ -67,6 +67,7 @@ export class DeliverScheduledEvent { context: {}, organizationId: schedulerSource.organizationId, environmentId: schedulerSource.environmentId, + timestamp: payload.scheduledTime, }, }); diff --git a/apps/webapp/app/services/workflows/registerWorkflow.server.ts b/apps/webapp/app/services/workflows/registerWorkflow.server.ts index d642e434f..4232ebb65 100644 --- a/apps/webapp/app/services/workflows/registerWorkflow.server.ts +++ b/apps/webapp/app/services/workflows/registerWorkflow.server.ts @@ -110,6 +110,7 @@ export class RegisterWorkflow { type: payload.trigger.type, service: payload.trigger.service, eventNames: payload.trigger.name, + triggerTtlInSeconds: payload.triggerTTL, }, create: { organizationId: organization.id, @@ -120,6 +121,7 @@ export class RegisterWorkflow { status: payload.trigger.service === "trigger" ? "READY" : "CREATED", service: payload.trigger.service, eventNames: payload.trigger.name, + triggerTtlInSeconds: payload.triggerTTL, }, include: { externalSource: true, diff --git a/apps/webapp/prisma/migrations/20230120010921_add_trigger_ttl_in_seconds/migration.sql b/apps/webapp/prisma/migrations/20230120010921_add_trigger_ttl_in_seconds/migration.sql new file mode 100644 index 000000000..56814bce0 --- /dev/null +++ b/apps/webapp/prisma/migrations/20230120010921_add_trigger_ttl_in_seconds/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Workflow" ADD COLUMN "triggerTtlInSeconds" INTEGER NOT NULL DEFAULT 3600; diff --git a/apps/webapp/prisma/migrations/20230120014606_add_timed_out_status_for_workflow_runs/migration.sql b/apps/webapp/prisma/migrations/20230120014606_add_timed_out_status_for_workflow_runs/migration.sql new file mode 100644 index 000000000..e76af3ece --- /dev/null +++ b/apps/webapp/prisma/migrations/20230120014606_add_timed_out_status_for_workflow_runs/migration.sql @@ -0,0 +1,2 @@ +-- AlterEnum +ALTER TYPE "WorkflowRunStatus" ADD VALUE 'TIMED_OUT'; diff --git a/apps/webapp/prisma/migrations/20230120014641_add_timed_out_columns/migration.sql b/apps/webapp/prisma/migrations/20230120014641_add_timed_out_columns/migration.sql new file mode 100644 index 000000000..90dafe50a --- /dev/null +++ b/apps/webapp/prisma/migrations/20230120014641_add_timed_out_columns/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "WorkflowRun" ADD COLUMN "timedOutAt" TIMESTAMP(3), +ADD COLUMN "timedOutReason" TEXT; diff --git a/apps/webapp/prisma/schema.prisma b/apps/webapp/prisma/schema.prisma index 9c9a96a4b..a2bf756e2 100644 --- a/apps/webapp/prisma/schema.prisma +++ b/apps/webapp/prisma/schema.prisma @@ -142,6 +142,8 @@ model Workflow { archivedAt DateTime? isArchived Boolean @default(false) + triggerTtlInSeconds Int @default(3600) + @@unique([organizationId, slug]) } @@ -406,6 +408,9 @@ model WorkflowRun { startedAt DateTime? finishedAt DateTime? + timedOutAt DateTime? + timedOutReason String? + isTest Boolean @default(false) requests IntegrationRequest[] delays DurableDelay[] @@ -417,6 +422,7 @@ enum WorkflowRunStatus { DISCONNECTED SUCCESS ERROR + TIMED_OUT } model WorkflowRunStep { diff --git a/apps/wss/src/server.ts b/apps/wss/src/server.ts index 72a48c664..30bff325f 100644 --- a/apps/wss/src/server.ts +++ b/apps/wss/src/server.ts @@ -324,6 +324,7 @@ export class TriggerServer { name: data.packageName, version: data.packageVersion, }, + triggerTTL: data.triggerTTL, }); this.#workflowId = response.id; @@ -340,7 +341,7 @@ export class TriggerServer { subscriptionInitialPosition: "Earliest", }, handlers: { - TRIGGER_WORKFLOW: async (id, data, properties) => { + TRIGGER_WORKFLOW: async (id, data, properties, messageAttributes) => { this.#logger.debug("Received trigger", id, data, properties); // If the API keys don't match, then we should ignore it // This ensures the workflow is triggered for the correct environment @@ -366,6 +367,33 @@ export class TriggerServer { ); } + if (properties["x-ttl"] && messageAttributes.eventTimestamp) { + const ttl = properties["x-ttl"]; + const eventTimestamp = messageAttributes.eventTimestamp; + const now = Date.now(); + const elapsedMilliseconds = now - eventTimestamp.getTime(); + const elapsedSeconds = elapsedMilliseconds / 1000; + + if (elapsedSeconds > ttl) { + this.#logger.debug("Message is expired, ignoring", { + messageAttributes, + properties, + }); + + await this.#commandPublisher.publish( + "WORKFLOW_RUN_TRIGGER_TIMEOUT", + { + id: data.id, + ttl, + elapsedSeconds, + }, + { ...properties, "x-timestamp": String(Date.now()) } + ); + + return true; + } + } + this.#logger.debug("Triggering workflow", data, properties); const runController = new WorkflowRunController({ diff --git a/examples/playground/src/index.ts b/examples/playground/src/index.ts index b4ff4d52e..de93347aa 100644 --- a/examples/playground/src/index.ts +++ b/examples/playground/src/index.ts @@ -427,6 +427,7 @@ new Trigger({ apiKey: "trigger_dev_zC25mKNn6c0q", endpoint: "ws://localhost:8889/ws", logLevel: "debug", + triggerTTL: 5, on: scheduleEvent({ rateOf: { minutes: 4 } }), run: async (event, ctx) => { await ctx.logger.info("Received the scheduled event", { diff --git a/examples/smoke-test/src/index.ts b/examples/smoke-test/src/index.ts index 16a628954..98707bff2 100644 --- a/examples/smoke-test/src/index.ts +++ b/examples/smoke-test/src/index.ts @@ -12,6 +12,7 @@ const trigger = new Trigger({ apiKey: "trigger_dev_zC25mKNn6c0q", endpoint: "ws://localhost:8889/ws", logLevel: "log", + triggerTTL: 60 * 60 * 24, on: customEvent({ name: "user.created", schema: userCreatedEvent }), run: async (event, ctx) => { await ctx.logger.info("Inside the smoke test workflow, received event", { diff --git a/packages/internal-bridge/src/schemas/server.ts b/packages/internal-bridge/src/schemas/server.ts index 7adc906ce..78e95cc2c 100644 --- a/packages/internal-bridge/src/schemas/server.ts +++ b/packages/internal-bridge/src/schemas/server.ts @@ -58,6 +58,7 @@ export const ServerRPCSchema = { trigger: TriggerMetadataSchema, packageVersion: z.string(), packageName: z.string(), + triggerTTL: z.number().optional(), }), response: z .discriminatedUnion("type", [ diff --git a/packages/internal-platform/src/messages/catalogs/triggers.ts b/packages/internal-platform/src/messages/catalogs/triggers.ts index 18f3a2802..057308d93 100644 --- a/packages/internal-platform/src/messages/catalogs/triggers.ts +++ b/packages/internal-platform/src/messages/catalogs/triggers.ts @@ -1,10 +1,13 @@ +import { z } from "zod"; import { TriggerWorkflowMessageSchema } from "../schemas/workflows"; import { WorkflowRunEventPropertiesSchema } from "../sharedSchemas"; const Catalog = { TRIGGER_WORKFLOW: { data: TriggerWorkflowMessageSchema, - properties: WorkflowRunEventPropertiesSchema, + properties: WorkflowRunEventPropertiesSchema.extend({ + "x-ttl": z.coerce.number().optional(), + }), }, }; diff --git a/packages/internal-platform/src/messages/schemas/workflowRuns.ts b/packages/internal-platform/src/messages/schemas/workflowRuns.ts index a619c73f2..e3c83f9b2 100644 --- a/packages/internal-platform/src/messages/schemas/workflowRuns.ts +++ b/packages/internal-platform/src/messages/schemas/workflowRuns.ts @@ -27,4 +27,12 @@ export const commands = { }), properties: WorkflowSendRunEventPropertiesSchema, }, + WORKFLOW_RUN_TRIGGER_TIMEOUT: { + data: z.object({ + id: z.string(), + ttl: z.number(), + elapsedSeconds: z.number(), + }), + properties: WorkflowSendRunEventPropertiesSchema, + }, }; diff --git a/packages/internal-platform/src/messages/zodPublisher.ts b/packages/internal-platform/src/messages/zodPublisher.ts index 3ba1497ba..a60c4a3d8 100644 --- a/packages/internal-platform/src/messages/zodPublisher.ts +++ b/packages/internal-platform/src/messages/zodPublisher.ts @@ -16,6 +16,7 @@ export type PublishOptions = { partitionKey?: string; orderingKey?: string; id?: string; + eventTimestamp?: number; }; type PendingMessages = Array<{ @@ -216,6 +217,7 @@ export class ZodPublisher { deliverAt: options?.deliverAt, partitionKey: options?.partitionKey, orderingKey: options?.orderingKey, + eventTimestamp: options?.eventTimestamp, }); return response.toString(); diff --git a/packages/internal-platform/src/messages/zodSubscriber.ts b/packages/internal-platform/src/messages/zodSubscriber.ts index 055d1cb19..9688aba7f 100644 --- a/packages/internal-platform/src/messages/zodSubscriber.ts +++ b/packages/internal-platform/src/messages/zodSubscriber.ts @@ -14,13 +14,21 @@ import { import { z, ZodError } from "zod"; import { ZodPubSubStatus } from "./types"; +export type SubscriberMessageAttributes = { + messageId: string; + eventTimestamp?: Date; + publishedTimestamp: Date; + redeliveryCount: number; +}; + export type ZodSubscriberHandlers< TConsumerSchema extends MessageCatalogSchema > = { [K in keyof TConsumerSchema]: ( id: string, data: z.infer, - properties: z.infer + properties: z.infer, + attributes: SubscriberMessageAttributes ) => Promise; }; @@ -109,8 +117,32 @@ export class ZodSubscriber { const properties = this.#getRawProperties(msg); + const messageId = msg.getMessageId(); + const publishedTimestamp = msg.getPublishTimestamp(); + const eventTimestamp = msg.getEventTimestamp(); + const redeliveryCount = msg.getRedeliveryCount(); + + this.#logger.debug("#onMessage", { + messageId, + publishedTimestamp, + eventTimestamp, + redeliveryCount, + }); + + const messageAttributes = { + eventTimestamp: + eventTimestamp === 0 ? undefined : new Date(eventTimestamp), + messageId: messageId.toString(), + publishedTimestamp: new Date(publishedTimestamp), + redeliveryCount, + }; + try { - const wasHandled = await this.#handleMessage(messageData, properties); + const wasHandled = await this.#handleMessage( + messageData, + properties, + messageAttributes + ); if (wasHandled) { await consumer.acknowledge(msg); @@ -134,7 +166,8 @@ export class ZodSubscriber { async #handleMessage( rawMessage: MessageData, - rawProperties: Record = {} + rawProperties: Record = {}, + messageAttributes: SubscriberMessageAttributes ): Promise { const subscriberSchema = this.#schema; type TypeKeys = keyof typeof subscriberSchema; @@ -160,7 +193,12 @@ export class ZodSubscriber { const handler = this.#handlers[typeName]; - const returnValue = await handler(rawMessage.id, message, properties); + const returnValue = await handler( + rawMessage.id, + message, + properties, + messageAttributes + ); return returnValue; } diff --git a/packages/internal-platform/src/schemas/workflows.ts b/packages/internal-platform/src/schemas/workflows.ts index bec510812..b9024b021 100644 --- a/packages/internal-platform/src/schemas/workflows.ts +++ b/packages/internal-platform/src/schemas/workflows.ts @@ -11,6 +11,7 @@ export const WorkflowMetadataSchema = z.object({ name: z.string(), trigger: TriggerMetadataSchema, package: PackageMetadataSchema, + triggerTTL: z.number().optional(), }); export type WorkflowMetadata = z.infer; diff --git a/packages/trigger-sdk/src/client.ts b/packages/trigger-sdk/src/client.ts index 720a2c065..b97093c63 100644 --- a/packages/trigger-sdk/src/client.ts +++ b/packages/trigger-sdk/src/client.ts @@ -443,6 +443,7 @@ export class TriggerClient { trigger: this.#trigger.on.metadata, packageVersion: pkg.version, packageName: pkg.name, + triggerTTL: this.#options.triggerTTL, }); if (response?.type === "error") { diff --git a/packages/trigger-sdk/src/trigger/index.ts b/packages/trigger-sdk/src/trigger/index.ts index 78b653062..1c31970bd 100644 --- a/packages/trigger-sdk/src/trigger/index.ts +++ b/packages/trigger-sdk/src/trigger/index.ts @@ -12,6 +12,13 @@ export type TriggerOptions = { apiKey?: string; endpoint?: string; logLevel?: LogLevel; + + /** + * The TTL for the trigger in seconds. If the trigger is not run within this time, it will be aborted. Defaults to 3600 seconds (1 hour). + * @type {number} + */ + triggerTTL?: number; + run: (event: z.infer, ctx: TriggerContext) => Promise; }; From 71b4cf3aa53ceae2111a7300c36d642d566e1be1 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Fri, 20 Jan 2023 11:00:28 -0800 Subject: [PATCH 40/45] Added a timed out status final step to the runs page --- .../app/models/workflowRunPresenter.server.ts | 1 + .../workflows/$workflowSlug/runs/$runId.tsx | 25 ++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/apps/webapp/app/models/workflowRunPresenter.server.ts b/apps/webapp/app/models/workflowRunPresenter.server.ts index d889f6457..7dc75d4e5 100644 --- a/apps/webapp/app/models/workflowRunPresenter.server.ts +++ b/apps/webapp/app/models/workflowRunPresenter.server.ts @@ -62,6 +62,7 @@ export class WorkflowRunPresenter { error: workflowRun.error ? await ErrorSchema.parseAsync(workflowRun.error) : undefined, + timedOutReason: workflowRun.timedOutReason, }; } } diff --git a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/runs/$runId.tsx b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/runs/$runId.tsx index 5f0f209bb..2450c7c04 100644 --- a/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/runs/$runId.tsx +++ b/apps/webapp/app/routes/__app/orgs/$organizationSlug/workflows/$workflowSlug/runs/$runId.tsx @@ -40,7 +40,7 @@ import { TriggerTypeIcon } from "~/components/triggers/TriggerIcons"; import { triggerLabel } from "~/components/triggers/triggerLabel"; import { useCurrentOrganization } from "~/hooks/useOrganizations"; import { useCurrentWorkflow } from "~/hooks/useWorkflows"; -import type { WorkflowRunStatus } from "~/models/workflowRun.server"; +import { WorkflowRunStatus } from "~/models/workflowRun.server"; import { WorkflowRunPresenter } from "~/models/workflowRunPresenter.server"; import { requireUserId } from "~/services/session.server"; import { dateDifference, formatDateTime } from "~/utils"; @@ -218,6 +218,29 @@ export default function Page() {
    )} + {run.status === "TIMED_OUT" && ( + <> +
    + + + } + title="Run timed out" + runId={run.id} + /> +
    + + Reason + + + {run.timedOutReason} + +
    +
    + + )} + {run.error && } ); From 2d185e9b8e2485eeec88d6b379c8fcc847627ba6 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Fri, 20 Jan 2023 11:00:41 -0800 Subject: [PATCH 41/45] =?UTF-8?q?Updated=20the=20icon=20sizes=20so=20they?= =?UTF-8?q?=E2=80=99re=20more=20visible?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../assets/images/triggers/event-bridge.png | Bin 42186 -> 41646 bytes .../assets/images/triggers/http-endpoint.png | Bin 42515 -> 42514 bytes .../assets/images/triggers/http-polling.png | Bin 43895 -> 44000 bytes .../app/assets/images/triggers/schedule.png | Bin 33707 -> 33241 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/webapp/app/assets/images/triggers/event-bridge.png b/apps/webapp/app/assets/images/triggers/event-bridge.png index c76a3ebbf5dc571bcac8ee781b53a01a64061152..f150c79dceef71f2e54cc0d63c5afc72a5b153b5 100644 GIT binary patch literal 41646 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 O00005LpW7(d6rh3k?RrP20 zIV8_aP0g88)uN{DvdgYAsj}O0RkcN`+Lq+9ML?28%A`bqTDXYYBQ5R=zzyqrH|FLN z5x>ZL9|(L{2vA7!;k!%T%*f1$_(eqK&C7Hm9rH;<=KCN0o2$QP)MGu@H2((0E!5u~ z^-r(8T+g@F-vwq~pcCnsPE04#F_C&9O@g?-{>}G)=fUw&BEtSrqSC(${qI8mdoOj} z0x@r++1YKqaPOcK=}k{31o%xzRaiGt5zdd(8wjdAzn$duUDQ4P6v?jsw?C(yyGUN^ zkJIn-FOdqKys%yC=w)qCFHPRQwha2otBFrpouA|W+T?Bh^^;fSWA5)w-nNFg|1)_O zX@3{`f46ng-8&)LLaSDt5b8G}oeP$BURb+@r)cSk zBCJdIJ=&w{OXVI_!K#9k&+Bt2cqqJCgsS`zTw|xDqA>_8-qTyJeIlGqy_Gw!D-hDm7 zK1oaWKT0S}E=1)TlM0GTXMclb=%dzDo&tf@Yan>E%sHo=S~k-%bx1g7s3`+?Z$ncL z{F95%=gGUzq1pA968FEndX+yfw4<+g%hHm3d4`s5<27qenA?XToe)`|f?&ESBC^O^jbG)n!mHiy>9hme8EFye$eh6a|ds5n&%w0A_=u5OXcB zuS7~^BNnz*wNtkEfw&oT(k)}>31bB)B=5Z2S z6WXQw9;1Cd$}aXon|)U@jLrWt92?gI$R_`_pe?zi1glpl3UiUDBJ^a(mB{?87Ucmg zp#d4~b>2UMV}4g8ip*eDfRD{*uIUB&y)=9ERW!L^9h&Mp`dqhBxA+Bq``fqBiF8ou zga994s%ZP0{oijK`$zSGGaC*VUNnP%7P~#=t-C_?1lLw8)$OjI#gB*UAJ`O%+k^p zKKtwiI+5Nmoe};j7%hP0PL`zUumRWiOTkDI7(HE65v zXijg*ODN0@T2(Z&DIrq;CZgnmpgu4`ok|1bwJ23nN@E(=3yey@2qrIXFm(k=2EtmA zsm!bglRD^-T6NNS7o114AHJ&pTW@6b;eu^l*L{BWU1#4yC(k(sd1(%vt(+K~5x3YWba;6e3-S3m9!V zYi{en=b!yhZ(je>#$J%;l)eS&x*KQTb3%|)`l%P-o`0_=5Zq)Y@BY%}ey;Yhn6EVr zTLA)dHKlPOtzo4s43+VvF|DJd5$&hW@hK%ZZ~fDT#x}Wr5^5viemYcIQDIO<)*{Fh zqT?aB1#KKEhG9ErK6qsne0j^Qy zB)I99y2qcPU4Qh|J}|YQIh>CeK&wT>a6a^Tjqw_!X?^6dDhdS1t8jmN%nHDjR07xS zzB%4dc5IZ<9TI992zr@PYWgBj-XedXY2Ll^)#;!VklOu6U!%R>Oyi9R21wceO;S9Wg=rb{v8g4TapPaAEERvT z@jasr3Uow-mE{6i%TD(|;H;qwk<EoC(b(;$0E~IP&N9(P4d2z zVp{+F&k@flBGWCCiQL!=@&f%7q@M}_Mn5-4mB02K+SC70ADjyFnA!3ZU-yrK)n%!J z{&KH!&zdIy?fzn3z}1Ap4^YG1@S0eN3)n*D{JquFbJMo)K4h;HKZ*WE~O@${A! z;A}1n-PE`8%5=W(yAROrKmN<|J}o2PHfAgMbwmvk!d&KoEw*SP(NZI{Fa?D|>=GvK z6Ju&k`7wkVv_ZVoMp($_emKbp1eoG2)off*D3hMR(yMuQ9{+F|#I}}Q?>5XQ3 zr4giZJZh>cuhxu%d0FzQ92%TUOZ>o$EsGuqA8Xfnb((SkvmJ`iqi9@P|3zfh z{8iAf4_Y7Ip3RZyNu0ZuxNbYNuW9sPkw`#&_PX6Cx4G}hB^S|(PyRwNtFs{?H_?jO zWLCfB-hg_`32+Tk-Q54pr}I5u`bIXH%R1$TQ5&RECiU#bpQ+{X!qQTA-OBf0^p*;8y`==WXNnZ0DyZ8>mv{c#|3XVoJVQY+ z!DpipT}X!Q;{xAEwdv5&(?f$cGxhcCG@*z}$vHfF{QFKI?*hX+$EF zaDn?6fN^96>IVTHZx=;cl#7nz}zH+2!AVBT&#_;=@N{aq?rB#&>OasNt zLLd_(G_<~Wy(Tn_8b`v5kaeI4RhiZ68kjT04+_GMTO~JU9T7yK>kS^b1~lUOG{@eK zYC)_ygI4}u|55dgX~z@T84qX@8Rf>}l|Ceagw;xz1 zR3$fxrU%QKpq7^_d8%$WS2k+uFBj#HLR_B;E{afWTz&SWl^KmEAG%8Wu$0#$py_P3ZL%J* z!I+XF?>C~MaO*YAU)lwWIjcgmdH4e4r2W3)BOjodpS!vj=#$dDg-O>>H*GlnOzPuK zfNPZbR!S!G3op~_|KdO7X`+#wc7*0$92Jvb{h_0x={@dy`?Fqn_x~;N8K-9bQXE$7N8y!dKxDw#HqUwfpy!Tu8((XV0 zueOcNI5N53ZiPgeyyN~Sh)>aBa%2GYczMPUUnQg;9_E!a7Cw~ zW%gJGoH863`jhnojXXP;x3wIu2$ru?TsrMGws%-3b_EJ9HOFmzG3+3D=ghRGdUE-t z<-Ctl&uqB-MmkQ?aU;MuMYQ`%-|WriZ;qOFA^201Gj1aRvdqGak`q`N7&Dl(_}DmO z`}v46sios;t#a82b5!(jt^3$6Fts!pFg77x8LDBA)PDmq6OCF9K%*-tF8+06a%&rN znZ>LIqNJ&(F^#Z(iAsWKOd$g58iB3Q(lV*kyW(d)N|T@YNG>t?#WNeO_$(a<>6jPb z8mvkt^Tvd>yZ`8~Xz$;ox%dVhIx*ii4c6$~Bq_tvfT@vpLeMJCbwusHU!T;Y(avOk z9(*?ySVN#vUu(W|wI5xyB1$bcAFH{8KuhCV5H+1Z!R=@e))Yo;JE{-20JN{lw3ruY zfi>ulm?ZYGKC1nx+IqQXy&?j%t$=KAA3yUi}Eg}TyIYz04Mtoy%9~z3i zOoB2A6tDJkEz@;;+a9T!rq(leI<5FepC&&2v~+15fhR^7ATrlLpa5Axy9Xo><h=Pg0A|{r8)72oi zZO?0>eHXHB~f7bkvuw(hQ9k@s5ZiyWteTMNP6Jpu4@r&3=L8J6tr?`x>~oJ z!(Kl{AI=21ntnVC#v!fNURtidh{h0DP0zB>Dk44xv`jfAIwx+`NCyfAA1)>b!XQNj zla3jb!5fStAswg?+h-V*5cHubc2Gzst)$t%cP&lMUzcw0=z1Yu`LT~3Q!hgtQv%!y z@6O-<3kv2=Hjj!y+WNg=8UX~r8Gwff%oG?;Z-jlHYFMBt5)au*7o zfX&H)4rpSi-gozz$X`Rho4l~Jf;{f8;^ zWq@*meibCDyyRfxB(YssWZBBr9%ohHM6M`W&r0G0r5zLleM^7&bz1t-1L=HjLbIFq ze*4?ubFpt`VmbyA3h(YOeT(+o`ZogvO9C)+K}bsJT~U8~nuv!N_IbX-ce@p6Ln~qIrq+dw#F8^-G-yIHnO4bMv!z zB}y#YqRXf>b*W=eibR9HAc}R4pl-0Hhz3?kYmrf7DYO=bX`5g{K!C3QoB7BGY38FJ zOnG*6+#M50{H7x?M4L16?z#2bjqp__2BuKPKnyX1Op4o8d@nHZmJ!~XMbsSh(D8sy z5a`1Cc#S}KOa4k$qaMWmLu&jPHTO}dRbHX-NSA1yJSD1V#xaU*c8CgUXj>s{D7@s_ zQ2dmv9%xj$JgE@am9NU=e3NQ*FxKmql_{cn*`!5=1lIOqmEJ!DI=ado>N^gd$^KLu z+#qOM=}@z-7XS9Uv~=Htsq(o=H`%;*^P7*vZ>j)C;r&5xHva|b1Usp3T zW0ZrUVxGi4B=o9M`B}=@6T=lt}Gb>LBTqn>Qu32gEuhORf52m=q++;$x%I3}U z^d_b^O@MKpci(p($^k0_7#|%~%w&?KLMV=AuGNtTWTZ}_ffvoH9O|z#nkcg>QHqYr zkkXi-p>~N=9b7p>pnZb%sz1jnU{J6b!t8t&bqO|w9CkS~KO@+%1B=lO&&oh-o5C4R z5w#{9%5#%|JV2Lz5x9faV$JTR6{Ct|D1DqtoP7?8(~98wK@3;GrU=nmshx{Jj5RL) z^%m+L_;IRlzMHJv{ALRAnGBh11W8vgQOBFWX4L3UR5r3 z0!?$vjv&hj22{lnPgT0Be}P}GJw zhuD;hIZiUD1~A=9h6EgiKAnt60^U=zz$3Z@<~5tojee~AZUm+wRfcp-HuG4Vagd2I z!W$Z;=oK$>B!7(U#jM0wAjPiZp%CD-Qln9s6%F^(1tjzMgs-n7l=^W(Gm!A#LOy@40SyQv+P`axJh>JrF~!ibCOqH zO=WLf`uaCNM@KmwRRNazg@lB-NqFUjSK18aha8`st^YyUfrA)MXONpVBS{; zO(a~*7H?occtM{UW~tXLHpQ_#f$|wRNyr&jSN&p?MSzKokohmDMCF1tXc4FwUEwHCfJ-F~ z4g;huH^rC<6$$~ZO&jy29)TCX_SZck@9FJ?rQcfm`ZunpqnM7G089De{SE1O=kI@o z5(0OFo^(!(;X((2;35eqP5Vb$MVfQ#49A3rcL_{covsSES<7x`PKjFNjLM*?qY@yb zQIQz#E~rYzIe8gMKIZ|gA*x&3ZWw6X$DuEsL-jHJxAq$s7gYRalY-9yNF3Bn(_k1H z=Ad|0jhO3Rqj&b0IefZ*Y{LhD16I{m)1ow_^j%E!G= ze8axK+PsmDQaUOEEdF6J+Wn<()4uQh7=#WOPW!F)FC&yyAyShRlx<1Ox=w(GdfQB4 zD2zh^I>3%D#1c?B)aF^>4h%V=mgdHC2z23*LbzHA9!1j_*BBYzeym3!gCoV^sH%z5 zp9rty9YuZMVw}y*3A} z)isvIFguwk!%ZJn)wT5CL$dVUt;)#7dN*T-RF+d0miUU@J(WPgKVfu$jtwWYQrOV5nF<*5Mr$#m&u9 z(MPCCiV!zf^m(DkgAHJs0wh4OWxIyagl0utw=&3x!#3m}FlRZ;?bL{S@J-ksf~vxO zQ|fFDbp-dU(rN)5M^O5ishBBhUvCtvLP7ql=n}h^0Pu;st@l#*(8K9uZfS3~S#CY@ zk?Es>z}NSGIpVzQkH4NTAfG8BnhV0MF~(*drH8T)H z%b^H7T$L~kq%A1Xx@zQ1JPOr7QlzGMzSeNU2s=Jvax5-swqlDd7DTm!y!jp)52h%# z*MyFI0Z1D}#5PD26K?ZhFe?`^sWA!2$AfxnAJ=Q0z77qFN&{vpjHniPK#<LsBZFGQlA2@ZY*LlMPn!v8PqHU zvNyB{8`2TM60ypBx|~wikq`&7Qe^^#UnG!2G;rq0o;NGR#7sa3EEqtF!4evmx=fdehk60su1It^IEQr9DJch9{!&kRkjm#=BJe*`Cn{MgreefcURndVPNoq2^hsBjHH9H zI8%%HZ|KvN>{aIiIukAv6Q@JvRZQC97wAKVRw1a&A_4gW*(hlUO(k>yLAlTiYM9`> zD|>;2e&|@(z)Z?|;;2}gry>73fSHOsMO1}Xcc`pw^cPPcE~5Y7z(f7iAf}IZqNh`9 zHz*wr78p_JOmgA0*#I{a%xV{S(v_c(x>JhA>Gi|z{s&WzxvrZWu|*zCMc17P8OxHj*_kt|289aVkc6%aL6 zut&iQvC_apIoZ9qm+pV|33}$0?XMmhx`z)^z;JXa6lFAs#RxjWkvnFo<3#IGo~pvdSb5OcJ4^maT(Po_8S?8@T1^ zE0%$StQANe+14 zk|gM@+P8U1JeE~Zah`~eCYV@DW1on}57V zpm-O>fTpQ{&h@jcdT?I%P7gLQXj{F~RtIjY&kp$jZr}MT{q7zA<&cCor8^&gfIj!l z+cJu)(&I+Oi4tgjB8U5W$PE^HqhoNW?F)<~v;7O8AFcr`Hl2q4cR)BU^ymANP*IWK zdOs#FU({M3)faBD^NTYpuBXG8m=05_QRm0f@ynn6{k&AN-XZOdBB~P39OH35L%*1PW299# zzVjr|3lf-^8lJMerYODB-2ZM7*exLI2D5m^kxG?q(V}QkQN7QtUVRGHcy>)T$RteNAPPySMAQYV{MT=x&Q&|yf2 z6M;9Ue|GoC+l7}DI1C`Q!dtbf25cHGt$B&iRIYMal_A+rFv`IPOaFEw!ke#zu&38o z^siSW6fCOV$K6A>9A@E7X~(YD=@0MzE810kXlZGN2G`kf@@W^t?aDa;2`hkuML|9e zCJ;e*J+C7rt7SUqPK$as0wLsmnUOJ_PdGHmro^%QEKaiJoLduIhzHc zVkcfsI`3pqS)kWooD@}!M4erb5yjI)@44H)JtJ|=eAwPvIxGTQqt5f)^RLkE+lt?t z5*jPet)?6E`M{)wV-c5BHJz;g8B8^Cfn!(J7~OTvImQ_rnNeI|kuuk~$>+Vdn!ATT zB_Y1`2Y1$fnlz?A=o@CzDGvswXX7BjHmK$#XlDEs$E|k$){X^*9&Rwf^bnO5V$xJr z2Z6&m1vD2KDp?9mC=$c95N|`7KEf+W(=gfer1v0$l3}dC7Xd-&Pa91mvwuK4pt?9n zc~d8}^uU88uf3kiU%&6qzI+26hQxG;shZ9ya5eY(O}Ej$dmbr!A=N3CtSn*B6-}f0 zA%T)%H<1mvYCbh#;$%K47fMut5(>cxCz?xR%rtiX^eZpY?|%Eg(2-8R{^4Kjr<>kR zs?3TWO$`21(358hx&sM-W8Iaoiodcjntk(QMzfv$sa}<*Bpn{q;4g zPNuU@JA=;Z|I)E4moj7mUX7@V<`AgNZE!*LV{BX7R-v&n$}yPGvdq+6fLLGx6mnE7 zfe*vwS{k~2fpFq?yq#u${^LnTcFgYWHt;{bZh;OV&C;Q#8z^r|_e0wIy@$&HG3?n^ zKYqp72pZg)a6Dh9O-p9V9N8k=ShC@B+=Pt&n>RtWJ+I0hme>K zA>o|w%m3^TsC)k95S0@QZ0xsE2yO|?)dghqU<(K;MRfOI)&4&{+0X=<*25lST2`{p z`?+uYXF6Ibq42u3@1QsrDN%_bg6eO7WB1ZN&M3L_b=uV<M{EKPz$`xfgJF8HbFazs~>NWgKmKpG(3I&=5jZaq%YiHXav=15 z+F~Z7aijTF%R-2OG*1DQ`dP`!r2alj6y7i&+=+dJEs1i!hN*$$wvYHB-@RPmh*& zEIdfto_dUrv|t`+y5osQ=&|iD(Esg2KSL+=OsmqXvJuQ*4*Jyl-tA&?JG|a9S1L+h zFs#}1ef=bpnzvYq1V!T0YG6_~+}y?rROzdu#ukY$=#aC$zLS=I{1DAtdT|os>#6J! zH&vQCh}9GInf|BD`QCa*xMv-}__dHIE2v~GK3IQ!csQ0QYzi1CDJBh(q7(yA3J>7Rb=AJC=?*VB1x-FHNrra#{Lw?6JwSnYEt)ymQ_ZoI16 z0pzAEn%Clj$kmEs)b?Q%aT;u7sfrLe<^nGvG7&5yf`Av2?3NHTLR=4ZKYo~G@80y0 z^NoxD@qhbOnlc?q6gp`R+Vy956l&WD9ecmWqyrWKa8mu#64jqDcDIO+AO@D=VuYZQ z?aY}T#gH|dUDF{q1%aAas%hQ-F1+*%9Z1@A!TMfc*Q%~C3R4+Lm_PBp4<1lCNhtR$ zE>f5`75fRDe)HY8_kyyWj@6V^{i(7t6nR-j9+xCNMWtX@1rb$x?Frx3?zxfdv zH)*7WIedM<1dcc~9jxTWBoTGvvS6Dq#Yhg6Q-&qfRcBDxMZf;~znHoT0Roy^|^mqYdX?0A~^~AW^m9T^AfM3MZeHoG6jT)Jq5VJoVTyhq`0w_D3Gfk!$VSlmeI{ za1w!_YQ&4<0yKsblr>PAI&~?dl0*#%NJ2%+qnsF`XM%Odgk!AADXdR```zPzN8OIJ zs#50neDOcuKvSe?5%~J_PhBo4L*gjNh-)XGlg((`Nw7?OVM4iP>snO|MX!Z%C0LW? z_ZW4#FIFgZ>sS4tyV1uaYD}V$_U$L7w~bcKt_0Q2{wWe$P*L(7NYce3rCsbZn##QW z_3u3X038o0MYq57qpfBRr=-YLCXaoVAt#krW`-g6t$Hw!Vshdbi6e(rZ7WoQ!Wqqh zQhBAN#^0J!jZ|{OqB0>9@pAmrm(|_-gQ6cqep7C~`JlJpIcNb!^xgM2_dyE;e6{{~ zLd-nWIyP7oO<)yj4$wly9L5MNJXeI3hr;K>17AmQ^dDU;Ku(yzpDK}YcVIus8icBY zf}%O7;@BpD1Fd@a^ecyLef$wmkG)h)X(DtHtJr~HnTcI{Kk|V|c0k0U9%*E9*-9>d zs02}Fhdhhc7+Ptnei+*zRrxDVRvR7pN4#j37ha_FM`)nJz^>Qt-T$~jH(Ba5@K4gOX9$3KPBJx znO9yqpz;z@2NN(8=cT@U9GK2+Qd8R0+dzQC|K%ubCO#2kN(e_5Qm_i@^G| zeYYRP+e>R!pE@SFq!pwWAXcqdLB~^4QyLjs`&=s5$EH@{3YiJU>y2%scHBc$U;U

    7QL+x--C&1F_ou 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 i{=FL(=tMfE)Bhj!NzNsd@_3Q}00008Nr diff --git a/apps/webapp/app/assets/images/triggers/http-endpoint.png b/apps/webapp/app/assets/images/triggers/http-endpoint.png index 8fd442a4161c933229fd4a29c570993bf9bfd246..33041623607f1f2088ccbdb53dab7a1b4d0ac486 100644 GIT binary patch literal 42514 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

    K@Q_%{RH-R5A~ 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 diff --git a/apps/webapp/app/assets/images/triggers/http-polling.png b/apps/webapp/app/assets/images/triggers/http-polling.png index 4d68dbb798b691968e480ccccfecf6f17d151a1e..3a967ec9e0c4976d16acd2598bb450cc9f19e1ae 100644 GIT binary patch literal 44000 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 literal 43895 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 blrH}pA;%0SKP8`a00000NkvXXu0mjfjL$7> diff --git a/apps/webapp/app/assets/images/triggers/schedule.png b/apps/webapp/app/assets/images/triggers/schedule.png index 9475a8abf156eafe697ed27a3fd2076e58f99422..5a385f20917d18382c0b89bba313aca45736204f 100644 GIT binary patch literal 33241 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