feat: BYO Auth (#491)
* feat: BYO Auth Define client-side auth resolvers to be able to supply custom authentication credentials for integrations before a run is performed - Added new defineAuthResolver - Update all integrations to support the new auth resolvers - Strip internal symbols from .d.ts in integrations and trigger-sdk - Added BYO Auth docs - Update Dynamic Schedule to support associated account IDs - Create external accounts just-in-time - Added Account ID field to test job when there are external auth integrations - Show Account ID on run dashboard - Added new Run error state called “Unresolved auth” * Added changeset * Remove @internal from TriggerIntegration public methods * Add void to the result union * DynamicTriggers now work with the new BYO auth system, and added a bunch of docs and docs changes * Add additional key material for registering dynamic trigger task * Add new define* instance methods to the overview
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
---
|
||||
"@trigger.dev/airtable": patch
|
||||
"@trigger.dev/sendgrid": patch
|
||||
"@trigger.dev/supabase": patch
|
||||
"@trigger.dev/typeform": patch
|
||||
"@trigger.dev/sdk": patch
|
||||
"@trigger.dev/github": patch
|
||||
"@trigger.dev/openai": patch
|
||||
"@trigger.dev/resend": patch
|
||||
"@trigger.dev/stripe": patch
|
||||
"@trigger.dev/plain": patch
|
||||
"@trigger.dev/slack": patch
|
||||
"@trigger.dev/core": patch
|
||||
---
|
||||
|
||||
Add support for Bring Your Own Auth
|
||||
Vendored
+9
@@ -19,6 +19,15 @@
|
||||
"name": "Chrome webapp",
|
||||
"url": "http://localhost:3030",
|
||||
"webRoot": "${workspaceFolder}/apps/webapp/app"
|
||||
},
|
||||
{
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"name": "Debug BYO Auth",
|
||||
"command": "pnpm run byo-auth",
|
||||
"envFile": "${workspaceFolder}/references/job-catalog/.env",
|
||||
"cwd": "${workspaceFolder}/references/job-catalog",
|
||||
"sourceMaps": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -197,6 +197,7 @@ function classForJobStatus(status: JobRunStatus) {
|
||||
case "TIMED_OUT":
|
||||
case "WAITING_ON_CONNECTIONS":
|
||||
case "PENDING":
|
||||
case "UNRESOLVED_AUTH":
|
||||
return "text-rose-500";
|
||||
default:
|
||||
return "";
|
||||
|
||||
@@ -167,7 +167,13 @@ export function RunOverview({ run, trigger, showRerun, paths }: RunOverviewProps
|
||||
<RunPanelHeader icon={trigger.icon} title={trigger.title} />
|
||||
<RunPanelBody>
|
||||
<RunPanelProperties
|
||||
properties={[{ label: "Event name", text: run.event.name }, ...run.properties]}
|
||||
properties={[{ label: "Event name", text: run.event.name }]
|
||||
.concat(
|
||||
run.event.externalAccount
|
||||
? [{ label: "Account ID", text: run.event.externalAccount.identifier }]
|
||||
: []
|
||||
)
|
||||
.concat(run.properties)}
|
||||
/>
|
||||
</RunPanelBody>
|
||||
</RunPanel>
|
||||
|
||||
@@ -45,6 +45,13 @@ export function TriggerDetail({
|
||||
/>
|
||||
)}
|
||||
<RunPanelIconProperty icon="id" label="Event name" value={name} />
|
||||
{trigger.externalAccount && (
|
||||
<RunPanelIconProperty
|
||||
icon="account"
|
||||
label="Account ID"
|
||||
value={trigger.externalAccount.identifier}
|
||||
/>
|
||||
)}
|
||||
</RunPanelIconSection>
|
||||
<RunPanelDivider />
|
||||
<div className="mt-4 flex flex-col gap-2">
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import type { JobRunExecution, JobRunStatus } from "@trigger.dev/database";
|
||||
import { NoSymbolIcon } from "@heroicons/react/20/solid";
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
ClockIcon,
|
||||
ExclamationTriangleIcon,
|
||||
StopIcon,
|
||||
WrenchIcon,
|
||||
XCircleIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
import type { JobRunStatus } from "@trigger.dev/database";
|
||||
import { cn } from "~/utils/cn";
|
||||
import { Spinner } from "../primitives/Spinner";
|
||||
import { HandRaisedIcon, NoSymbolIcon } from "@heroicons/react/20/solid";
|
||||
|
||||
export function hasFinished(status: JobRunStatus): boolean {
|
||||
return (
|
||||
@@ -17,7 +16,8 @@ export function hasFinished(status: JobRunStatus): boolean {
|
||||
status === "FAILURE" ||
|
||||
status === "ABORTED" ||
|
||||
status === "TIMED_OUT" ||
|
||||
status === "CANCELED"
|
||||
status === "CANCELED" ||
|
||||
status === "UNRESOLVED_AUTH"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,8 @@ export function RunStatusIcon({ status, className }: { status: JobRunStatus; cla
|
||||
return <XCircleIcon className={cn(runStatusClassNameColor(status), className)} />;
|
||||
case "TIMED_OUT":
|
||||
return <ExclamationTriangleIcon className={cn(runStatusClassNameColor(status), className)} />;
|
||||
case "UNRESOLVED_AUTH":
|
||||
return <XCircleIcon className={cn(runStatusClassNameColor(status), className)} />;
|
||||
case "WAITING_ON_CONNECTIONS":
|
||||
return <WrenchIcon className={cn(runStatusClassNameColor(status), className)} />;
|
||||
case "ABORTED":
|
||||
@@ -63,26 +65,25 @@ export type RunBasicStatus = "WAITING" | "PENDING" | "RUNNING" | "COMPLETED" | "
|
||||
|
||||
export function runBasicStatus(status: JobRunStatus): RunBasicStatus {
|
||||
switch (status) {
|
||||
case "SUCCESS":
|
||||
return "COMPLETED";
|
||||
case "WAITING_ON_CONNECTIONS":
|
||||
case "QUEUED":
|
||||
case "PREPROCESSING":
|
||||
case "PENDING":
|
||||
return "PENDING";
|
||||
case "STARTED":
|
||||
return "RUNNING";
|
||||
case "QUEUED":
|
||||
return "PENDING";
|
||||
case "FAILURE":
|
||||
return "FAILED";
|
||||
case "TIMED_OUT":
|
||||
return "FAILED";
|
||||
case "WAITING_ON_CONNECTIONS":
|
||||
return "PENDING";
|
||||
case "UNRESOLVED_AUTH":
|
||||
case "CANCELED":
|
||||
case "ABORTED":
|
||||
return "FAILED";
|
||||
case "PREPROCESSING":
|
||||
return "PENDING";
|
||||
case "CANCELED":
|
||||
return "FAILED";
|
||||
case "SUCCESS":
|
||||
return "COMPLETED";
|
||||
default: {
|
||||
const _exhaustiveCheck: never = status;
|
||||
throw new Error(`Non-exhaustive match for value: ${status}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +109,12 @@ export function runStatusTitle(status: JobRunStatus): string {
|
||||
return "Preprocessing";
|
||||
case "CANCELED":
|
||||
return "Canceled";
|
||||
case "UNRESOLVED_AUTH":
|
||||
return "Unresolved auth";
|
||||
default: {
|
||||
const _exhaustiveCheck: never = status;
|
||||
throw new Error(`Non-exhaustive match for value: ${status}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +129,7 @@ export function runStatusClassNameColor(status: JobRunStatus): string {
|
||||
case "QUEUED":
|
||||
return "text-amber-300";
|
||||
case "FAILURE":
|
||||
case "UNRESOLVED_AUTH":
|
||||
return "text-rose-500";
|
||||
case "TIMED_OUT":
|
||||
return "text-amber-300";
|
||||
|
||||
@@ -16,7 +16,7 @@ export async function resolveRunConnections(
|
||||
const result: Record<string, ConnectionAuth> = {};
|
||||
|
||||
for (const connection of connections) {
|
||||
if (connection.integration.authSource === "LOCAL") {
|
||||
if (connection.integration.authSource !== "HOSTED") {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -120,8 +120,12 @@ export class IntegrationClientPresenter {
|
||||
icon: integration.definition.icon,
|
||||
},
|
||||
authMethod: {
|
||||
type: integration.authMethod?.type ?? "local",
|
||||
name: integration.authMethod?.name ?? "Local Auth",
|
||||
type:
|
||||
integration.authMethod?.type ?? integration.authSource === "RESOLVER" ? "local" : "local",
|
||||
name:
|
||||
integration.authMethod?.name ?? integration.authSource === "RESOLVER"
|
||||
? "Auth Resolver"
|
||||
: "Local Auth",
|
||||
},
|
||||
help,
|
||||
};
|
||||
|
||||
@@ -125,8 +125,9 @@ export class IntegrationsPresenter {
|
||||
name: c.definition.name,
|
||||
},
|
||||
authMethod: {
|
||||
type: c.authMethod?.type ?? "local",
|
||||
name: c.authMethod?.name ?? "Local Only",
|
||||
type: c.authMethod?.type ?? c.authSource === "RESOLVER" ? "resolver" : "local",
|
||||
name:
|
||||
c.authMethod?.name ?? c.authSource === "RESOLVER" ? "Auth Resolver" : "Local Only",
|
||||
},
|
||||
authSource: c.authSource,
|
||||
setupStatus: c.setupStatus,
|
||||
|
||||
@@ -115,6 +115,11 @@ export class RunPresenter {
|
||||
payload: true,
|
||||
timestamp: true,
|
||||
deliveredAt: true,
|
||||
externalAccount: {
|
||||
select: {
|
||||
identifier: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
tasks: {
|
||||
|
||||
@@ -39,6 +39,15 @@ export class TestJobPresenter {
|
||||
payload: true,
|
||||
},
|
||||
},
|
||||
integrations: {
|
||||
select: {
|
||||
integration: {
|
||||
select: {
|
||||
authSource: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
environment: {
|
||||
@@ -99,6 +108,9 @@ export class TestJobPresenter {
|
||||
...example,
|
||||
payload: JSON.stringify(example.payload, exampleReplacer, 2),
|
||||
})),
|
||||
hasAuthResolver: alias.version.integrations.some(
|
||||
(i) => i.integration.authSource === "RESOLVER"
|
||||
),
|
||||
})),
|
||||
hasTestRuns: job._count.runs > 0,
|
||||
};
|
||||
|
||||
@@ -22,6 +22,11 @@ export class TriggerDetailsPresenter {
|
||||
payload: true,
|
||||
timestamp: true,
|
||||
deliveredAt: true,
|
||||
externalAccount: {
|
||||
select: {
|
||||
identifier: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+36
-17
@@ -1,4 +1,4 @@
|
||||
import { useForm } from "@conform-to/react";
|
||||
import { conform, useForm } from "@conform-to/react";
|
||||
import { parse } from "@conform-to/zod";
|
||||
import { PopoverTrigger } from "@radix-ui/react-popover";
|
||||
import { Form, useActionData, useSubmit } from "@remix-run/react";
|
||||
@@ -14,6 +14,9 @@ import { Button, ButtonContent } from "~/components/primitives/Buttons";
|
||||
import { Callout } from "~/components/primitives/Callout";
|
||||
import { FormError } from "~/components/primitives/FormError";
|
||||
import { Help, HelpContent, HelpTrigger } from "~/components/primitives/Help";
|
||||
import { Input } from "~/components/primitives/Input";
|
||||
import { InputGroup } from "~/components/primitives/InputGroup";
|
||||
import { Label } from "~/components/primitives/Label";
|
||||
import { Popover, PopoverContent } from "~/components/primitives/Popover";
|
||||
import {
|
||||
Select,
|
||||
@@ -69,6 +72,7 @@ const schema = z.object({
|
||||
}),
|
||||
environmentId: z.string(),
|
||||
versionId: z.string(),
|
||||
accountId: z.string().optional(),
|
||||
});
|
||||
|
||||
//todo save the chosen environment to a cookie (for that user), use it to default the env dropdown
|
||||
@@ -84,11 +88,7 @@ export const action: ActionFunction = async ({ request, params }) => {
|
||||
}
|
||||
|
||||
const testService = new TestJobService();
|
||||
const run = await testService.call({
|
||||
environmentId: submission.value.environmentId,
|
||||
payload: submission.value.payload,
|
||||
versionId: submission.value.versionId,
|
||||
});
|
||||
const run = await testService.call(submission.value);
|
||||
|
||||
if (!run) {
|
||||
return redirectBackWithErrorMessage(
|
||||
@@ -124,6 +124,7 @@ export default function Page() {
|
||||
const [defaultJson, setDefaultJson] = useState<string>(startingJson);
|
||||
const currentJson = useRef<string>(defaultJson);
|
||||
const [selectedEnvironmentId, setSelectedEnvironmentId] = useState<string>(environments[0].id);
|
||||
const [currentAccountId, setCurrentAccountId] = useState<string | undefined>(undefined);
|
||||
|
||||
const selectedEnvironment = environments.find((e) => e.id === selectedEnvironmentId);
|
||||
|
||||
@@ -139,6 +140,7 @@ export default function Page() {
|
||||
payload: currentJson.current,
|
||||
environmentId: selectedEnvironmentId,
|
||||
versionId: selectedEnvironment?.versionId ?? "",
|
||||
...(currentAccountId ? { accountId: currentAccountId } : {}),
|
||||
},
|
||||
{
|
||||
action: "",
|
||||
@@ -147,10 +149,10 @@ export default function Page() {
|
||||
);
|
||||
e.preventDefault();
|
||||
},
|
||||
[currentJson, selectedEnvironmentId]
|
||||
[currentJson, selectedEnvironmentId, currentAccountId]
|
||||
);
|
||||
|
||||
const [form, { environmentId, payload }] = useForm({
|
||||
const [form, { environmentId, payload, accountId }] = useForm({
|
||||
id: "test-job",
|
||||
lastSubmission,
|
||||
onValidate({ formData }) {
|
||||
@@ -234,15 +236,32 @@ export default function Page() {
|
||||
</div>
|
||||
<HelpTrigger title="How do I run a test?" />
|
||||
</div>
|
||||
<div className="flex-1 overflow-auto rounded border border-slate-850 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700">
|
||||
<JSONEditor
|
||||
defaultValue={defaultJson}
|
||||
readOnly={false}
|
||||
basicSetup
|
||||
onChange={(v) => (currentJson.current = v)}
|
||||
minHeight="150px"
|
||||
/>
|
||||
</div>
|
||||
<InputGroup fullWidth>
|
||||
<Label variant="small">Payload</Label>
|
||||
<div className="flex-1 overflow-auto rounded border border-slate-850 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-slate-700">
|
||||
<JSONEditor
|
||||
defaultValue={defaultJson}
|
||||
readOnly={false}
|
||||
basicSetup
|
||||
onChange={(v) => (currentJson.current = v)}
|
||||
minHeight="150px"
|
||||
/>
|
||||
</div>
|
||||
</InputGroup>
|
||||
|
||||
{selectedEnvironment?.hasAuthResolver && (
|
||||
<InputGroup fullWidth className="mb-4 mt-4">
|
||||
<Label variant="small">Account ID</Label>
|
||||
<Input
|
||||
type="text"
|
||||
fullWidth
|
||||
value={currentAccountId}
|
||||
placeholder={`e.g. abc_1234`}
|
||||
onChange={(e) => setCurrentAccountId(e.target.value)}
|
||||
/>
|
||||
<FormError>{accountId.error}</FormError>
|
||||
</InputGroup>
|
||||
)}
|
||||
<div className="flex flex-none items-center justify-between">
|
||||
{payload.error ? (
|
||||
<FormError id={payload.errorId}>{payload.error}</FormError>
|
||||
|
||||
@@ -81,13 +81,19 @@ class CreateExternalConnectionService {
|
||||
environment: AuthenticatedEnvironment,
|
||||
payload: CreateExternalConnectionBody
|
||||
) {
|
||||
const externalAccount = await this.#prismaClient.externalAccount.findUniqueOrThrow({
|
||||
const externalAccount = await this.#prismaClient.externalAccount.upsert({
|
||||
where: {
|
||||
environmentId_identifier: {
|
||||
environmentId: environment.id,
|
||||
identifier: accountIdentifier,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
environmentId: environment.id,
|
||||
organizationId: environment.organizationId,
|
||||
identifier: accountIdentifier,
|
||||
},
|
||||
update: {},
|
||||
});
|
||||
|
||||
const integration = await this.#prismaClient.integration.findUniqueOrThrow({
|
||||
|
||||
@@ -7,10 +7,7 @@ import { logger } from "../logger.server";
|
||||
export class IngestSendEvent {
|
||||
#prismaClient: PrismaClientOrTransaction;
|
||||
|
||||
constructor(
|
||||
prismaClient: PrismaClientOrTransaction = prisma,
|
||||
private deliverEvents = true
|
||||
) {
|
||||
constructor(prismaClient: PrismaClientOrTransaction = prisma, private deliverEvents = true) {
|
||||
this.#prismaClient = prismaClient;
|
||||
}
|
||||
|
||||
@@ -41,13 +38,19 @@ export class IngestSendEvent {
|
||||
this.#prismaClient,
|
||||
async (tx) => {
|
||||
const externalAccount = options?.accountId
|
||||
? await tx.externalAccount.findUniqueOrThrow({
|
||||
? await tx.externalAccount.upsert({
|
||||
where: {
|
||||
environmentId_identifier: {
|
||||
environmentId: environment.id,
|
||||
identifier: options.accountId,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
environmentId: environment.id,
|
||||
organizationId: environment.organizationId,
|
||||
identifier: options.accountId,
|
||||
},
|
||||
update: {},
|
||||
})
|
||||
: undefined;
|
||||
|
||||
|
||||
@@ -4,7 +4,14 @@ import {
|
||||
SCHEDULED_EVENT,
|
||||
TriggerMetadata,
|
||||
} from "@trigger.dev/core";
|
||||
import type { Endpoint, Integration, Job, JobIntegration, JobVersion } from "@trigger.dev/database";
|
||||
import type {
|
||||
Endpoint,
|
||||
Integration,
|
||||
Job,
|
||||
JobIntegration,
|
||||
JobIntegrationPayload,
|
||||
JobVersion,
|
||||
} from "@trigger.dev/database";
|
||||
import { DEFAULT_MAX_CONCURRENT_RUNS } from "~/consts";
|
||||
import type { PrismaClient } from "~/db.server";
|
||||
import { prisma } from "~/db.server";
|
||||
@@ -62,83 +69,7 @@ export class RegisterJobService {
|
||||
});
|
||||
|
||||
if (!integration) {
|
||||
if (jobIntegration.authSource === "LOCAL") {
|
||||
integration = await this.#prismaClient.integration.upsert({
|
||||
where: {
|
||||
organizationId_slug: {
|
||||
organizationId: environment.organizationId,
|
||||
slug: jobIntegration.id,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
slug: jobIntegration.id,
|
||||
title: jobIntegration.metadata.name,
|
||||
authSource: "LOCAL",
|
||||
connectionType: "DEVELOPER",
|
||||
organization: {
|
||||
connect: {
|
||||
id: environment.organizationId,
|
||||
},
|
||||
},
|
||||
definition: {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
id: jobIntegration.metadata.id,
|
||||
},
|
||||
create: {
|
||||
id: jobIntegration.metadata.id,
|
||||
name: jobIntegration.metadata.name,
|
||||
instructions: jobIntegration.metadata.instructions,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
update: {
|
||||
title: jobIntegration.metadata.name,
|
||||
authSource: "LOCAL",
|
||||
connectionType: "DEVELOPER",
|
||||
definition: {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
id: jobIntegration.metadata.id,
|
||||
},
|
||||
create: {
|
||||
id: jobIntegration.metadata.id,
|
||||
name: jobIntegration.metadata.name,
|
||||
instructions: jobIntegration.metadata.instructions,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
integration = await this.#prismaClient.integration.create({
|
||||
data: {
|
||||
slug: jobIntegration.id,
|
||||
title: jobIntegration.id,
|
||||
authSource: "HOSTED",
|
||||
setupStatus: "MISSING_FIELDS",
|
||||
connectionType: "DEVELOPER",
|
||||
organization: {
|
||||
connect: {
|
||||
id: environment.organizationId,
|
||||
},
|
||||
},
|
||||
definition: {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
id: jobIntegration.metadata.id,
|
||||
},
|
||||
create: {
|
||||
id: jobIntegration.metadata.id,
|
||||
name: jobIntegration.metadata.name,
|
||||
instructions: jobIntegration.metadata.instructions,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
integration = await this.#upsertIntegrationForJobIntegration(environment, jobIntegration);
|
||||
}
|
||||
|
||||
integrations.set(jobIntegration.id, integration);
|
||||
@@ -472,6 +403,7 @@ export class RegisterJobService {
|
||||
key: job.id,
|
||||
dispatcher: eventDispatcher,
|
||||
schedule: trigger.schedule,
|
||||
organizationId: job.organizationId,
|
||||
});
|
||||
|
||||
break;
|
||||
@@ -479,6 +411,145 @@ export class RegisterJobService {
|
||||
}
|
||||
}
|
||||
|
||||
async #upsertIntegrationForJobIntegration(
|
||||
environment: AuthenticatedEnvironment,
|
||||
jobIntegration: IntegrationConfig
|
||||
): Promise<Integration> {
|
||||
switch (jobIntegration.authSource) {
|
||||
case "LOCAL": {
|
||||
return await this.#prismaClient.integration.upsert({
|
||||
where: {
|
||||
organizationId_slug: {
|
||||
organizationId: environment.organizationId,
|
||||
slug: jobIntegration.id,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
slug: jobIntegration.id,
|
||||
title: jobIntegration.metadata.name,
|
||||
authSource: "LOCAL",
|
||||
connectionType: "DEVELOPER",
|
||||
organization: {
|
||||
connect: {
|
||||
id: environment.organizationId,
|
||||
},
|
||||
},
|
||||
definition: {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
id: jobIntegration.metadata.id,
|
||||
},
|
||||
create: {
|
||||
id: jobIntegration.metadata.id,
|
||||
name: jobIntegration.metadata.name,
|
||||
instructions: jobIntegration.metadata.instructions,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
update: {
|
||||
title: jobIntegration.metadata.name,
|
||||
authSource: "LOCAL",
|
||||
connectionType: "DEVELOPER",
|
||||
definition: {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
id: jobIntegration.metadata.id,
|
||||
},
|
||||
create: {
|
||||
id: jobIntegration.metadata.id,
|
||||
name: jobIntegration.metadata.name,
|
||||
instructions: jobIntegration.metadata.instructions,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
case "HOSTED": {
|
||||
return await this.#prismaClient.integration.create({
|
||||
data: {
|
||||
slug: jobIntegration.id,
|
||||
title: jobIntegration.id,
|
||||
authSource: "HOSTED",
|
||||
setupStatus: "MISSING_FIELDS",
|
||||
connectionType: "DEVELOPER",
|
||||
organization: {
|
||||
connect: {
|
||||
id: environment.organizationId,
|
||||
},
|
||||
},
|
||||
definition: {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
id: jobIntegration.metadata.id,
|
||||
},
|
||||
create: {
|
||||
id: jobIntegration.metadata.id,
|
||||
name: jobIntegration.metadata.name,
|
||||
instructions: jobIntegration.metadata.instructions,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
case "RESOLVER": {
|
||||
return await this.#prismaClient.integration.upsert({
|
||||
where: {
|
||||
organizationId_slug: {
|
||||
organizationId: environment.organizationId,
|
||||
slug: jobIntegration.id,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
slug: jobIntegration.id,
|
||||
title: jobIntegration.metadata.name,
|
||||
authSource: "RESOLVER",
|
||||
connectionType: "EXTERNAL",
|
||||
organization: {
|
||||
connect: {
|
||||
id: environment.organizationId,
|
||||
},
|
||||
},
|
||||
definition: {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
id: jobIntegration.metadata.id,
|
||||
},
|
||||
create: {
|
||||
id: jobIntegration.metadata.id,
|
||||
name: jobIntegration.metadata.name,
|
||||
instructions: jobIntegration.metadata.instructions,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
update: {
|
||||
title: jobIntegration.metadata.name,
|
||||
authSource: "RESOLVER",
|
||||
connectionType: "EXTERNAL",
|
||||
definition: {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
id: jobIntegration.metadata.id,
|
||||
},
|
||||
create: {
|
||||
id: jobIntegration.metadata.id,
|
||||
name: jobIntegration.metadata.name,
|
||||
instructions: jobIntegration.metadata.instructions,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(jobIntegration.authSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async #upsertJobIntegration(
|
||||
job: Job & {
|
||||
integrations: Array<JobIntegration & { integration: Integration | null }>;
|
||||
@@ -572,3 +643,7 @@ export class RegisterJobService {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function assertExhaustive(x: never): never {
|
||||
throw new Error("Unexpected object: " + x);
|
||||
}
|
||||
|
||||
@@ -13,10 +13,12 @@ export class TestJobService {
|
||||
environmentId,
|
||||
versionId,
|
||||
payload,
|
||||
accountId,
|
||||
}: {
|
||||
environmentId: string;
|
||||
versionId: string;
|
||||
payload: any;
|
||||
payload?: any;
|
||||
accountId?: string;
|
||||
}) {
|
||||
return await $transaction(
|
||||
this.#prismaClient,
|
||||
@@ -41,10 +43,27 @@ export class TestJobService {
|
||||
},
|
||||
});
|
||||
|
||||
const externalAccount = accountId
|
||||
? await tx.externalAccount.upsert({
|
||||
where: {
|
||||
environmentId_identifier: {
|
||||
environmentId: environment.id,
|
||||
identifier: accountId,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
environmentId: environment.id,
|
||||
organizationId: environment.organizationId,
|
||||
identifier: accountId,
|
||||
},
|
||||
update: {},
|
||||
})
|
||||
: undefined;
|
||||
|
||||
const event = EventSpecificationSchema.parse(version.eventSpecification);
|
||||
const eventName = Array.isArray(event.name) ? event.name[0] : event.name;
|
||||
|
||||
const eventLog = await this.#prismaClient.eventRecord.create({
|
||||
const eventLog = await tx.eventRecord.create({
|
||||
data: {
|
||||
organization: {
|
||||
connect: {
|
||||
@@ -61,6 +80,13 @@ export class TestJobService {
|
||||
id: environment.id,
|
||||
},
|
||||
},
|
||||
externalAccount: externalAccount
|
||||
? {
|
||||
connect: {
|
||||
id: externalAccount.id,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
eventId: `test:${eventName}:${new Date().getTime()}`,
|
||||
name: eventName,
|
||||
timestamp: new Date(),
|
||||
|
||||
@@ -2,7 +2,7 @@ import { RuntimeEnvironmentType } from "@trigger.dev/database";
|
||||
import { $transaction, Prisma, PrismaClient, prisma } from "~/db.server";
|
||||
import { enqueueRunExecutionV2 } from "~/models/jobRunExecution.server";
|
||||
|
||||
const RESUMABLE_STATUSES = ["FAILURE", "TIMED_OUT", "ABORTED", "CANCELED"];
|
||||
const RESUMABLE_STATUSES = ["FAILURE", "TIMED_OUT", "UNRESOLVED_AUTH", "ABORTED", "CANCELED"];
|
||||
|
||||
export class ContinueRunService {
|
||||
#prismaClient: PrismaClient;
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
RunJobResumeWithTask,
|
||||
RunJobRetryWithTask,
|
||||
RunJobSuccess,
|
||||
RunJobUnresolvedAuthError,
|
||||
RunSourceContextSchema,
|
||||
} from "@trigger.dev/core";
|
||||
import type { Task } from "@trigger.dev/database";
|
||||
@@ -342,6 +343,11 @@ export class PerformRunExecutionV1Service {
|
||||
await this.#cancelExecution(execution);
|
||||
break;
|
||||
}
|
||||
case "UNRESOLVED_AUTH_ERROR": {
|
||||
await this.#failRunWithUnresolvedAuthError(execution, safeBody.data);
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const _exhaustiveCheck: never = status;
|
||||
throw new Error(`Non-exhaustive match for value: ${status}`);
|
||||
@@ -438,6 +444,15 @@ export class PerformRunExecutionV1Service {
|
||||
});
|
||||
}
|
||||
|
||||
async #failRunWithUnresolvedAuthError(
|
||||
execution: FoundRunExecution,
|
||||
data: RunJobUnresolvedAuthError
|
||||
) {
|
||||
return await $transaction(this.#prismaClient, async (tx) => {
|
||||
await this.#failRunExecution(tx, execution, data.issues, "UNRESOLVED_AUTH");
|
||||
});
|
||||
}
|
||||
|
||||
async #retryRunWithTask(execution: FoundRunExecution, data: RunJobRetryWithTask) {
|
||||
const { run } = execution;
|
||||
|
||||
@@ -557,7 +572,7 @@ export class PerformRunExecutionV1Service {
|
||||
prisma: PrismaClientOrTransaction,
|
||||
execution: FoundRunExecution,
|
||||
output: Record<string, any>,
|
||||
status: "FAILURE" | "ABORTED" = "FAILURE"
|
||||
status: "FAILURE" | "ABORTED" | "UNRESOLVED_AUTH" = "FAILURE"
|
||||
): Promise<void> {
|
||||
const { run } = execution;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
RunJobResumeWithTask,
|
||||
RunJobRetryWithTask,
|
||||
RunJobSuccess,
|
||||
RunJobUnresolvedAuthError,
|
||||
RunSourceContextSchema,
|
||||
} from "@trigger.dev/core";
|
||||
import { RuntimeEnvironmentType, type Task } from "@trigger.dev/database";
|
||||
@@ -354,6 +355,11 @@ export class PerformRunExecutionV2Service {
|
||||
await this.#cancelExecution(run);
|
||||
break;
|
||||
}
|
||||
case "UNRESOLVED_AUTH_ERROR": {
|
||||
await this.#failRunWithUnresolvedAuthError(run, safeBody.data, durationInMs);
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
const _exhaustiveCheck: never = status;
|
||||
throw new Error(`Non-exhaustive match for value: ${status}`);
|
||||
@@ -432,6 +438,23 @@ export class PerformRunExecutionV2Service {
|
||||
});
|
||||
}
|
||||
|
||||
async #failRunWithUnresolvedAuthError(
|
||||
execution: FoundRun,
|
||||
data: RunJobUnresolvedAuthError,
|
||||
durationInMs: number
|
||||
) {
|
||||
return await $transaction(this.#prismaClient, async (tx) => {
|
||||
await this.#failRunExecution(
|
||||
tx,
|
||||
"EXECUTE_JOB",
|
||||
execution,
|
||||
data.issues,
|
||||
"UNRESOLVED_AUTH",
|
||||
durationInMs
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async #retryRunWithTask(
|
||||
run: FoundRun,
|
||||
data: RunJobRetryWithTask,
|
||||
@@ -556,7 +579,7 @@ export class PerformRunExecutionV2Service {
|
||||
reason: "EXECUTE_JOB" | "PREPROCESS",
|
||||
run: FoundRun,
|
||||
output: Record<string, any>,
|
||||
status: "FAILURE" | "ABORTED" | "TIMED_OUT" = "FAILURE",
|
||||
status: "FAILURE" | "ABORTED" | "TIMED_OUT" | "UNRESOLVED_AUTH" = "FAILURE",
|
||||
durationInMs: number = 0
|
||||
): Promise<void> {
|
||||
await $transaction(prisma, async (tx) => {
|
||||
|
||||
@@ -20,6 +20,7 @@ export class ReRunService {
|
||||
version: true,
|
||||
job: true,
|
||||
event: true,
|
||||
externalAccount: true,
|
||||
},
|
||||
where: {
|
||||
id: runId,
|
||||
@@ -43,6 +44,13 @@ export class ReRunService {
|
||||
id: existingRun.environment.id,
|
||||
},
|
||||
},
|
||||
externalAccount: existingRun.externalAccount
|
||||
? {
|
||||
connect: {
|
||||
id: existingRun.externalAccount.id,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
eventId: `${existingRun.event.eventId}:retry:${new Date().getTime()}`,
|
||||
name: existingRun.event.name,
|
||||
timestamp: new Date(),
|
||||
|
||||
@@ -50,11 +50,11 @@ export class StartRunService {
|
||||
integrationId: runConnection.integration.id,
|
||||
authSource: "HOSTED",
|
||||
} as const)
|
||||
: runConnection.result === "resolvedLocal"
|
||||
: runConnection.result === "resolvedLocal" || runConnection.result === "resolvedResolver"
|
||||
? ({
|
||||
key,
|
||||
integrationId: runConnection.integration.id,
|
||||
authSource: "LOCAL",
|
||||
authSource: runConnection.result === "resolvedLocal" ? "LOCAL" : "RESOLVER",
|
||||
} as const)
|
||||
: undefined
|
||||
)
|
||||
@@ -173,6 +173,7 @@ async function createRunConnections(tx: PrismaClientOrTransaction, run: FoundRun
|
||||
integration: Integration;
|
||||
}
|
||||
| { result: "resolvedLocal"; integration: Integration }
|
||||
| { result: "resolvedResolver"; integration: Integration }
|
||||
| {
|
||||
result: "missing";
|
||||
connectionType: ConnectionType;
|
||||
@@ -190,6 +191,11 @@ async function createRunConnections(tx: PrismaClientOrTransaction, run: FoundRun
|
||||
result: "resolvedLocal",
|
||||
integration: jobIntegration.integration,
|
||||
};
|
||||
} else if (jobIntegration.integration.authSource === "RESOLVER") {
|
||||
acc[jobIntegration.key] = {
|
||||
result: "resolvedResolver",
|
||||
integration: jobIntegration.integration,
|
||||
};
|
||||
} else {
|
||||
const connection = run.externalAccountId
|
||||
? await tx.integrationConnection.findFirst({
|
||||
|
||||
@@ -59,6 +59,7 @@ export class RegisterScheduleService {
|
||||
schedule: payload,
|
||||
accountId: payload.accountId,
|
||||
dynamicTrigger,
|
||||
organizationId: environment.organizationId,
|
||||
});
|
||||
|
||||
return registration;
|
||||
|
||||
@@ -16,24 +16,32 @@ export class RegisterScheduleSourceService {
|
||||
schedule,
|
||||
accountId,
|
||||
dynamicTrigger,
|
||||
organizationId,
|
||||
}: {
|
||||
key: string;
|
||||
dispatcher: EventDispatcher;
|
||||
schedule: ScheduleMetadata;
|
||||
accountId?: string;
|
||||
dynamicTrigger?: DynamicTrigger;
|
||||
organizationId: string;
|
||||
}) {
|
||||
const validatedSchedule = validateSchedule(schedule);
|
||||
|
||||
return await $transaction(this.#prismaClient, async (tx) => {
|
||||
const externalAccount = accountId
|
||||
? await tx.externalAccount.findUniqueOrThrow({
|
||||
? await tx.externalAccount.upsert({
|
||||
where: {
|
||||
environmentId_identifier: {
|
||||
environmentId: dispatcher.environmentId,
|
||||
identifier: accountId,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
environmentId: dispatcher.environmentId,
|
||||
organizationId: organizationId,
|
||||
identifier: accountId,
|
||||
},
|
||||
update: {},
|
||||
})
|
||||
: undefined;
|
||||
|
||||
|
||||
@@ -71,13 +71,19 @@ export class RegisterSourceServiceV1 {
|
||||
}
|
||||
|
||||
const externalAccount = accountId
|
||||
? await tx.externalAccount.findUniqueOrThrow({
|
||||
? await tx.externalAccount.upsert({
|
||||
where: {
|
||||
environmentId_identifier: {
|
||||
environmentId: environment.id,
|
||||
identifier: accountId,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
environmentId: environment.id,
|
||||
organizationId: environment.organizationId,
|
||||
identifier: accountId,
|
||||
},
|
||||
update: {},
|
||||
})
|
||||
: undefined;
|
||||
|
||||
|
||||
@@ -71,13 +71,19 @@ export class RegisterSourceServiceV2 {
|
||||
}
|
||||
|
||||
const externalAccount = accountId
|
||||
? await tx.externalAccount.findUniqueOrThrow({
|
||||
? await tx.externalAccount.upsert({
|
||||
where: {
|
||||
environmentId_identifier: {
|
||||
environmentId: environment.id,
|
||||
identifier: accountId,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
environmentId: environment.id,
|
||||
organizationId: environment.organizationId,
|
||||
identifier: accountId,
|
||||
},
|
||||
update: {},
|
||||
})
|
||||
: undefined;
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ export class RegisterTriggerSourceServiceV2 {
|
||||
endpointSlug,
|
||||
id,
|
||||
key,
|
||||
accountId,
|
||||
registrationMetadata,
|
||||
}: {
|
||||
environment: AuthenticatedEnvironment;
|
||||
@@ -32,7 +31,6 @@ export class RegisterTriggerSourceServiceV2 {
|
||||
id: string;
|
||||
endpointSlug: string;
|
||||
key: string;
|
||||
accountId?: string;
|
||||
registrationMetadata?: any;
|
||||
}): Promise<RegisterSourceEventV2 | undefined> {
|
||||
const endpoint = await this.#prismaClient.endpoint.findUniqueOrThrow({
|
||||
@@ -63,7 +61,7 @@ export class RegisterTriggerSourceServiceV2 {
|
||||
endpoint.id,
|
||||
payload.source,
|
||||
dynamicTrigger.id,
|
||||
accountId,
|
||||
payload.accountId,
|
||||
{ id: key, metadata: registrationMetadata }
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<CodeGroup>
|
||||
|
||||
```bash npm
|
||||
npm install @trigger.dev/slack@latest
|
||||
```
|
||||
|
||||
```bash pnpm
|
||||
pnpm install @trigger.dev/slack@latest
|
||||
```
|
||||
|
||||
```bash yarn
|
||||
yarn add @trigger.dev/slack@latest
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
@@ -0,0 +1,49 @@
|
||||
<ParamField body="options" type="object" required>
|
||||
<Expandable title="properties" defaultOpen>
|
||||
<ParamField body="id" type="string" required>
|
||||
The `id` property is used to uniquely identify the Job. Only change this if you want to create a new Job.
|
||||
</ParamField>
|
||||
<ParamField body="name" type="string" required>
|
||||
The `name` of the Job that you want to appear in the dashboard and logs. You can change this without creating a new Job.
|
||||
</ParamField>
|
||||
<ParamField body="version" type="string" required>
|
||||
The `version` property is used to version your Job. A new version will be created if you change this property. We recommend using [semantic versioning](https://www.baeldung.com/cs/semantic-versioning), e.g. `1.0.3`.
|
||||
</ParamField>
|
||||
<ParamField body="trigger" type="object" required>
|
||||
The `trigger` property is used to define when the Job should run. There are currently the following Trigger types:
|
||||
- [cronTrigger](/sdk/crontrigger)
|
||||
- [intervalTrigger](/sdk/intervaltrigger)
|
||||
- [eventTrigger](/sdk/eventtrigger)
|
||||
- [DynamicTrigger](/sdk/dynamictrigger)
|
||||
- [DynamicSchedule](/sdk/dynamicschedule)
|
||||
- integration Triggers, like webhooks. See the [integrations](/integrations) page for more information.
|
||||
</ParamField>
|
||||
<ParamField body="run" type="function" required>
|
||||
This function gets called automatically when a Run is Triggered. It has three parameters:
|
||||
1. `payload` – The payload that was sent to the Trigger API.
|
||||
2. [io](/sdk/io) – An object that contains the integrations that you specified in the `integrations` property and other useful functions like delays and running Tasks.
|
||||
3. [context](/sdk/context) – An object that contains information about the Organization, Job, Run and more.
|
||||
|
||||
This is where you put the code you want to run for a Job. You can use normal code in here and you can also use Tasks.
|
||||
|
||||
You can return a value from this function and it will be sent back to the Trigger API.
|
||||
</ParamField>
|
||||
<ParamField body="integrations" type="object">
|
||||
Imports the specified integrations into the Job. The integrations will be available on the `io` object in the `run()` function with the same name as the key. For example:
|
||||
<Snippet file="how-to-pass-integrations.mdx" />
|
||||
</ParamField>
|
||||
<ParamField body="enabled" type="boolean">
|
||||
The `enabled` property is an optional property that specifies whether the Job is enabled or not. The Job will be enabled by default if you omit this property. When a job is disabled, no new runs will be triggered or resumed. In progress runs will continue to run until they are finished or delayed by using `io.wait`.
|
||||
</ParamField>
|
||||
<ParamField body="logLevel" type="log | error | warn | info | debug">
|
||||
The `logLevel` property is an optional property that specifies the level of
|
||||
logging for the Job. The level is inherited from the client if you omit this property.
|
||||
- `log` - logs only essential messages
|
||||
- `error` - logs error messages
|
||||
- `warn` - logs errors and warning messages
|
||||
- `info` - logs errors, warnings and info messages
|
||||
- `debug` - logs everything with full verbosity
|
||||
</ParamField>
|
||||
|
||||
</Expandable>
|
||||
</ParamField>
|
||||
@@ -12,7 +12,7 @@ Sometimes you don't know when you write the code what the trigger or schedule wi
|
||||
|
||||
```typescript
|
||||
//1. create a DynamicSchedule
|
||||
const dynamicSchedule = new DynamicSchedule(client, {
|
||||
const dynamicSchedule = client.defineDynamicSchedule({
|
||||
id: "dynamicinterval",
|
||||
});
|
||||
|
||||
@@ -53,15 +53,18 @@ client.defineJob({
|
||||
}),
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
//6. Register the DynamicSchedule
|
||||
await io.registerInterval("📆", dynamicSchedule, payload.userId, {
|
||||
seconds: payload.seconds,
|
||||
//6. Register the DynamicSchedule (this will automatically create a task)
|
||||
await dynamicSchedule.register(userId, {
|
||||
type: "cron",
|
||||
options: {
|
||||
cron: userSchedule,
|
||||
},
|
||||
});
|
||||
|
||||
await io.wait("wait", 60);
|
||||
|
||||
//7. Unregister the DynamicSchedule if you want
|
||||
await io.unregisterInterval("❌📆", dynamicSchedule, payload.id);
|
||||
//7. Unregister the DynamicSchedule if you want (this will automatically create a task)
|
||||
await dynamicSchedule.unregister(userId);
|
||||
},
|
||||
});
|
||||
```
|
||||
@@ -70,7 +73,7 @@ client.defineJob({
|
||||
|
||||
```typescript
|
||||
//1. create a DynamicTrigger
|
||||
const dynamicOnIssueOpenedTrigger = new DynamicTrigger(client, {
|
||||
const dynamicOnIssueOpenedTrigger = client.defineDynamicTrigger({
|
||||
id: "github-issue-opened",
|
||||
event: events.onIssueOpened,
|
||||
source: github.sources.repo,
|
||||
@@ -96,7 +99,7 @@ client.defineJob({
|
||||
//3. Register the DynamicTrigger anywhere in your app
|
||||
async function registerRepo(owner: string, repo: string) {
|
||||
//the first param (key) should be unique
|
||||
await dynamicOnIssueOpenedTrigger.register(`${owner}/${repo}`, {
|
||||
await dynamicOnIssueOpenedTrigger.register(`${owner}-${repo}`, {
|
||||
owner,
|
||||
repo,
|
||||
});
|
||||
@@ -114,15 +117,10 @@ client.defineJob({
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
//6. Register the dynamic trigger so you get notified when an issue is opened
|
||||
return await io.registerTrigger(
|
||||
"register-repo",
|
||||
dynamicOnIssueOpenedTrigger,
|
||||
payload.repository.name,
|
||||
{
|
||||
owner: payload.repository.owner.login,
|
||||
repo: payload.repository.name,
|
||||
}
|
||||
);
|
||||
await dynamicOnIssueOpenedTrigger.register(`${owner}-${repo}`, {
|
||||
owner,
|
||||
repo,
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
title: "API Keys and Personal Access Tokens"
|
||||
description: "Lots of APIs use API Keys or Personal Access Tokens to authenticate. This guide will show you how to use them."
|
||||
sidebarTitle: "API Keys and PATs"
|
||||
---
|
||||
|
||||
## 1. Create an Integration client
|
||||
|
||||
@@ -0,0 +1,515 @@
|
||||
---
|
||||
title: "Bring Your Own Auth"
|
||||
description: "Use Auth Resolvers to provide custom authentication credentials"
|
||||
---
|
||||
|
||||
In the previous guides we've covered how you can use our integrations with [API Keys](/documentation/guides/using-integrations-apikeys) or [OAuth](/documentation/guides/using-integrations-oauth), but in both cases those authentication credentials belong **to you** the developer.
|
||||
|
||||
If you want to use our integrations using auth credentials of **your users** you can use an Auth Resolver which allows you to implement your own custom auth resolving using a third-party service like [Clerk](https://clerk.com/) or [Nango](https://www.nango.dev/)
|
||||
|
||||
In this guide we'll demonstrate how to use Clerk.com's [Social Connections](https://clerk.com/docs/authentication/social-connections/oauth) to allow you to make requests with your user's Slack credentials and the official Trigger.dev [Slack integration](/integrations/apis/slack)
|
||||
|
||||
<Note>
|
||||
We won't be covering how to setup Clerk.com and their Social Connections to get the auth. This
|
||||
guide assumes you already have all that setup.
|
||||
</Note>
|
||||
|
||||
## 1. Install the Slack integration package
|
||||
|
||||
<Snippet file="installs/slack.mdx" />
|
||||
|
||||
## 2. Create a Slack integration
|
||||
|
||||
```ts slack.ts
|
||||
import { Slack } from "@trigger.dev/slack";
|
||||
|
||||
const byoSlack = new Slack({
|
||||
id: "byo-slack",
|
||||
});
|
||||
```
|
||||
|
||||
## 3. Define an Auth Resolver
|
||||
|
||||
Using your `TriggerClient` instance, define a new Auth Resolver for the `slack` integration:
|
||||
|
||||
```ts slack.ts
|
||||
import { Slack } from "@trigger.dev/slack";
|
||||
// Import your TriggerClient instance. This is merely an example of how you could do it
|
||||
import { client } from "./trigger";
|
||||
|
||||
const byoSlack = new Slack({
|
||||
id: "byo-slack",
|
||||
});
|
||||
|
||||
client.defineAuthResolver(byoSlack, async (ctx) => {
|
||||
// this is where we'll use the clerk backend SDK
|
||||
});
|
||||
```
|
||||
|
||||
## 4. Define a job
|
||||
|
||||
Before we finish the Slack Auth Resolver, let's create an example job that uses the Slack integration:
|
||||
|
||||
```ts slack.ts
|
||||
import { z } from "zod";
|
||||
|
||||
client.defineJob({
|
||||
id: "post-a-message",
|
||||
name: "Post a Slack Message",
|
||||
version: "1.0.0",
|
||||
trigger: eventTrigger({
|
||||
name: "post.message",
|
||||
schema: z.object({
|
||||
text: z.string(),
|
||||
channel: z.string(),
|
||||
}),
|
||||
}),
|
||||
integrations: {
|
||||
slack: byoSlack,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
await io.slack.postMessage("💬", {
|
||||
channel: payload.channel,
|
||||
text: payload.text,
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
As you can see above, we're passing the `byoSlack` integration into the Job and using it by calling `io.slack.postMessage`.
|
||||
|
||||
## 5. Install the Clerk backend SDK
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash npm
|
||||
npm install @clerk/backend@latest
|
||||
```
|
||||
|
||||
```bash pnpm
|
||||
pnpm install @clerk/backend@latest
|
||||
```
|
||||
|
||||
```bash yarn
|
||||
yarn add @clerk/backend@latest
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## 6. Import and initialize the Clerk SDK
|
||||
|
||||
```ts slack.ts
|
||||
import { Clerk } from "@clerk/backend";
|
||||
|
||||
// Clerk is not a class so the omission of `new Clerk` here is on purpose
|
||||
const clerk = Clerk({ apiKey: process.env.CLERK_API_KEY });
|
||||
```
|
||||
|
||||
## 7. Implement the Auth Resolver
|
||||
|
||||
Now we'll implement the Auth Resolver to provide authentication credentials saved in Clerk.com for Job runs, depending on the account ID of the run.
|
||||
|
||||
```ts slack.ts
|
||||
client.defineAuthResolver(slack, async (ctx) => {
|
||||
if (!ctx.account?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tokens = await clerk.users.getUserOauthAccessToken(ctx.account.id, "oauth_slack");
|
||||
|
||||
if (tokens.length === 0) {
|
||||
throw new Error(`Could not find Slack auth for account ${ctx.account.id}`);
|
||||
}
|
||||
|
||||
return {
|
||||
type: "oauth",
|
||||
token: tokens[0].token,
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
The first parameter to the Auth Resolver callback is the run context ([reference docs](/sdk/context)), which optionally contains an associated account (more on this below).
|
||||
|
||||
<Warning>
|
||||
If the Auth Resolver returns undefined or throws an Error, any Job Run that uses the `byoSlack`
|
||||
integration will fail with an "Unresolved auth" error.
|
||||
</Warning>
|
||||
|
||||
## Bonus: Multiple Slack integration clients
|
||||
|
||||
If you want to also use Slack with your own authentication credentials, you can always create _another_ slack integration with a different `id`.
|
||||
|
||||
```ts slack.ts
|
||||
const ourSlack = new Slack({ id: "our-slack" });
|
||||
|
||||
client.defineJob({
|
||||
id: "post-a-message",
|
||||
name: "Post a Slack Message",
|
||||
version: "1.0.0",
|
||||
trigger: eventTrigger({
|
||||
name: "post.message",
|
||||
schema: z.object({
|
||||
text: z.string(),
|
||||
channel: z.string(),
|
||||
}),
|
||||
}),
|
||||
integrations: {
|
||||
byoSlack: byoSlack,
|
||||
ourSlack: ourSlack,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
await io.byoSlack.postMessage("💬", {
|
||||
channel: payload.channel,
|
||||
text: payload.text,
|
||||
});
|
||||
|
||||
await io.ourSlack.postMessage("📢", {
|
||||
channel: "C01234567",
|
||||
text: `We just sent the following message to ${ctx.account?.id}: ${payload.text}`,
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
# How to Trigger Job runs with an Account ID
|
||||
|
||||
Now that we have a working Clerk.com Auth Resolver for Slack we're ready to start triggering jobs with an associated account ID. The way you do this is different depending on the Trigger type.
|
||||
|
||||
## Event Triggers
|
||||
|
||||
Jobs that have [Event Triggers](/documentation/concepts/triggers/events) can be run with an associated account by providing an `accountId` when calling `sendEvent`:
|
||||
|
||||
```ts backend.ts
|
||||
// This is an instance of `TriggerClient`
|
||||
await client.sendEvent(
|
||||
{
|
||||
name: "post.created",
|
||||
payload: { id: "post_123" },
|
||||
},
|
||||
{
|
||||
accountId: "user_123",
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
The `accountId` value is completely arbitrary and doesn't map to anything inside Trigger.dev, but generally it should be a unique ID that can be used to lookup Auth credentials in your Auth Resolvers.
|
||||
|
||||
You can also send events with an associated account ID from the run of another job:
|
||||
|
||||
```ts anotherJob.ts
|
||||
client.defineJob({
|
||||
id: "event-1",
|
||||
name: "Run when the foo.bar event happens",
|
||||
version: "0.0.1",
|
||||
trigger: eventTrigger({
|
||||
name: "foo.bar",
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
//send an event using `io`
|
||||
await io.sendEvent(
|
||||
"🎫",
|
||||
{
|
||||
name: "post.created",
|
||||
payload: { id: "post_123" },
|
||||
},
|
||||
{
|
||||
accountId: "user_123",
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
When a run is triggered with an associated account ID, you'll see the account ID in the run dashboard:
|
||||
|
||||

|
||||
|
||||
## Scheduled Triggers
|
||||
|
||||
Running a job with an associated account ID that is triggered by a [Scheduled Trigger](/documentation/concepts/triggers/scheduled) works a bit differently than Event Triggers as you'll need to convert your normal `intervalTrigger` or `cronTrigger` into using a [Dynamic Schedule](/documentation/concepts/triggers/dynamic#dynamicschedule) and then registering schedules with an associated account ID.
|
||||
|
||||
### 1. Convert a job to using a Dynamic Schedule
|
||||
|
||||
First let's convert the following job from an `intervalTrigger` to a Dynamic Schedule:
|
||||
|
||||
```ts dynamicSchedule.ts
|
||||
// Before
|
||||
client.defineJob({
|
||||
id: "scheduled-job",
|
||||
name: "Scheduled Job",
|
||||
version: "1.0.0",
|
||||
trigger: intervalTrigger({
|
||||
seconds: 60,
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
await io.logger.info("This runs every 60 seconds");
|
||||
},
|
||||
});
|
||||
|
||||
// After
|
||||
export const dynamicInterval = client.defineDynamicSchedule({ id: "my-schedule" });
|
||||
|
||||
client.defineJob({
|
||||
id: "scheduled-job",
|
||||
name: "Scheduled Job",
|
||||
version: "1.0.0",
|
||||
trigger: dynamicInterval,
|
||||
run: async (payload, io, ctx) => {
|
||||
await io.logger.info("This runs dynamic schedules");
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
As you can see above, we've dropped the specific interval when defining the trigger as that will now be specific when registering schedules.
|
||||
|
||||
### 2. Register a schedule
|
||||
|
||||
You can now use the `dynamicInterval` instance to register a schedule, which will trigger the `scheduled-job`:
|
||||
|
||||
```ts backend.ts
|
||||
import { dynamicInterval } from "./dynamicSchedule";
|
||||
|
||||
// Somewhere in your backend
|
||||
await dynamicInterval.register("schedule_123", {
|
||||
type: "interval",
|
||||
options: { seconds: 60 },
|
||||
accountId: "user_123", // associate runs triggered by this schedule with user_123
|
||||
});
|
||||
```
|
||||
|
||||
As you can see above, we've associated this registered schedule with an `accountId`, so any runs triggered by this schedule will be associated with `"user_123"`
|
||||
|
||||
The first parameter above `"schedule_123"` is the Schedule ID and can be used to unregister the schedule at a later point:
|
||||
|
||||
```ts backend.ts
|
||||
import { dynamicInterval } from "./dynamicSchedule";
|
||||
|
||||
// Somewhere in your backend
|
||||
await dynamicInterval.unregister("schedule_123");
|
||||
```
|
||||
|
||||
You can also use register/unregister inside another job run and it will automatically create a [Task](/documentation/concepts/tasks):
|
||||
|
||||
```ts otherJob.ts
|
||||
import { dynamicInterval } from "./dynamicSchedule";
|
||||
|
||||
client.defineJob({
|
||||
id: "event-1",
|
||||
name: "Run when the foo.bar event happens",
|
||||
version: "0.0.1",
|
||||
trigger: eventTrigger({
|
||||
name: "foo.bar",
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
await dynamicInterval.register("schedule_123", {
|
||||
type: "interval",
|
||||
options: { seconds: 60 },
|
||||
accountId: "user_123", // associate runs triggered by this schedule with user_123
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Will produce the following run dashboard:
|
||||
|
||||

|
||||
|
||||
<Tip>
|
||||
If you will only ever add a single schedule for a user on a given Dynamic Schedule, you can just
|
||||
use the accountId as the Schedule ID
|
||||
|
||||
```ts
|
||||
const accountId = "user_123";
|
||||
await dynamicInterval.register(accountId, {
|
||||
type: "interval",
|
||||
options: { seconds: 60 },
|
||||
accountId,
|
||||
});
|
||||
```
|
||||
|
||||
</Tip>
|
||||
|
||||
## Webhook Triggers
|
||||
|
||||
Running a job with an associated account ID that is triggered by a [Webhook Trigger](/documentation/concepts/triggers/webhook) requires converting to the use of a [Dynamic Trigger](/documentation/concepts/triggers/dynamic#dynamictrigger)
|
||||
|
||||
Dynamic Trigger's work very similarly to Dynamic Schedules, but instead of registering schedules, you register triggers:
|
||||
|
||||
<Steps>
|
||||
<Step title="Create Dynamic Trigger">
|
||||
|
||||
Using the GitHub integration we'll create a Dynamic Trigger that is triggered by the `onIssueOpened` event:
|
||||
|
||||
```ts github.ts
|
||||
import { Github, events } from "@trigger.dev/github";
|
||||
|
||||
const github = new Github({
|
||||
id: "github",
|
||||
});
|
||||
|
||||
const dynamicOnIssueOpenedTrigger = client.defineDynamicTrigger({
|
||||
id: "github-issue-opened",
|
||||
event: events.onIssueOpened,
|
||||
source: github.sources.repo,
|
||||
});
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Use the Dynamic Trigger">
|
||||
|
||||
Now we'll use the Dynamic Trigger to define a Job that is triggered by it:
|
||||
|
||||
```ts github.ts
|
||||
client.defineJob({
|
||||
id: "listen-for-dynamic-trigger",
|
||||
name: "Listen for dynamic trigger",
|
||||
version: "0.1.1",
|
||||
trigger: dynamicOnIssueOpenedTrigger,
|
||||
integrations: {
|
||||
github,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
await io.github.issues.createComment("create-issue-comment", {
|
||||
owner: payload.repository.owner.login,
|
||||
repo: payload.repository.name,
|
||||
issueNumber: payload.issue.number,
|
||||
body: "First! 🥇",
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Define Auth Resolver">
|
||||
|
||||
Define an Auth Resolver to fetch the GitHub OAuth token from Clerk.com:
|
||||
|
||||
```ts github.ts
|
||||
client.defineAuthResolver(github, async (ctx) => {
|
||||
if (!ctx.account?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tokens = await clerk.users.getUserOauthAccessToken(ctx.account.id, "oauth_github");
|
||||
|
||||
if (tokens.length === 0) {
|
||||
throw new Error(`Could not find GitHub auth for account ${ctx.account.id}`);
|
||||
}
|
||||
|
||||
return {
|
||||
type: "oauth",
|
||||
token: tokens[0].token,
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
<Note>
|
||||
If you are using clerk, you'll probably want to [Add additional
|
||||
scopes](https://clerk.com/docs/authentication/social-connections/oauth#request-additional-o-auth-scopes-after-sign-up)
|
||||
to be able to do useful things with the GitHub integration. For example, if you plan on
|
||||
registering GitHub triggers you'll need `write:repo_hook` and `read:repo_hook` or just
|
||||
`admin:repo_hook`. If you want to create issues you'll need `repo` or `public_repo`.
|
||||
</Note>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Register a new trigger">
|
||||
|
||||
Finally, we can register a new Trigger at "runtime", either inside another Job run or in your backend:
|
||||
|
||||
```ts github.ts
|
||||
// Register inside another job run:
|
||||
client.defineJob({
|
||||
id: "register-issue-opened",
|
||||
name: "Register Issue Opened for Account",
|
||||
version: "0.0.1",
|
||||
trigger: eventTrigger({
|
||||
name: "register.issue.opened",
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
// This will automatically create a task in this run with the `payload.id` as the Task Key.
|
||||
await dynamicOnIssueOpenedTrigger.register(
|
||||
payload.id,
|
||||
{
|
||||
owner: payload.owner,
|
||||
repo: payload.repo,
|
||||
},
|
||||
{
|
||||
accountId: payload.accountId,
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// Register in your backend:
|
||||
// This skips creating a Task since it's outside a job and will just call our backend API directly
|
||||
async function registerIssueOpenedTrigger(
|
||||
id: string,
|
||||
owner: string,
|
||||
repo: string,
|
||||
accountId?: string
|
||||
) {
|
||||
return await dynamicOnIssueOpenedTrigger.register(
|
||||
id,
|
||||
{
|
||||
owner,
|
||||
repo,
|
||||
},
|
||||
{
|
||||
accountId,
|
||||
}
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
# Testing jobs with Account ID
|
||||
|
||||
If a job uses any integrations with an Auth Resolver that requires an account ID, you'll need to provide an account ID when testing the job:
|
||||
|
||||

|
||||
|
||||
# Auth Resolver reference
|
||||
|
||||
The Auth Resolver callback has the following signature:
|
||||
|
||||
```ts
|
||||
type TriggerAuthResolver = (
|
||||
ctx: TriggerContext,
|
||||
integration: TriggerIntegration
|
||||
) => Promise<AuthResolverResult | undefined>;
|
||||
|
||||
type AuthResolverResult = {
|
||||
type: "apiKey" | "oauth";
|
||||
token: string;
|
||||
additionalFields?: Record<string, string>;
|
||||
};
|
||||
```
|
||||
|
||||
The `ctx` parameter is the [TriggerContext](/sdk/context) for the run and the `integration` parameter is the [TriggerIntegration](/sdk/integrations) instance that the Auth Resolver is being called for. You can use the `integration` parameter to check the `id` of the integration to determine which integration the Auth Resolver is being called for:
|
||||
|
||||
```ts
|
||||
client.defineAuthResolver(slack, async (ctx, integration) => {
|
||||
if (integration.id === "byo-slack") {
|
||||
// do something
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
You can also return `additionalFields` in the Auth Resolver result which will be passed to the integration when making requests. This is useful if you need to provide additional fields to the integration that are not part of the standard integration options.
|
||||
|
||||
```ts
|
||||
client.defineAuthResolver(shopify, async (ctx, integration) => {
|
||||
return {
|
||||
type: "apiKey",
|
||||
token: "my-api-key",
|
||||
additionalFields: {
|
||||
shop: "my-shop-name",
|
||||
},
|
||||
};
|
||||
});
|
||||
```
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
title: "Using Integrations"
|
||||
description: "How to use Integrations"
|
||||
title: "Integrations Overview"
|
||||
description: "How to use Trigger.dev Integrations"
|
||||
sidebarTitle: "Overview"
|
||||
---
|
||||
|
||||
<Note>
|
||||
You can use any API in your Jobs by using existing Node.js SDKs or HTTP
|
||||
requests. Integrations just make it much easier especially when you want to
|
||||
use OAuth. And you get great logging.
|
||||
You can use any API in your Jobs by using existing Node.js SDKs or HTTP requests. Integrations
|
||||
just make it much easier especially when you want to use OAuth. And you get great logging.
|
||||
</Note>
|
||||
|
||||
[Integrations](/documentation/concepts/integrations) allow you to quickly use APIs, including webhooks and Tasks.
|
||||
@@ -37,6 +37,14 @@ There are two ways to authenticate Integrations, OAuth and API Keys/Access Token
|
||||
>
|
||||
Use OAuth to connect an Integration for your team or your users
|
||||
</Card>
|
||||
<Card
|
||||
title="Bring-your-own Auth"
|
||||
icon="user"
|
||||
href="/documentation/guides/using-integrations-byo-auth"
|
||||
>
|
||||
Use our integrations with your user’s auth credentials, using Clerk.com, Nango.dev, or rolling
|
||||
your own with our custom auth resolvers
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Using for Jobs & Tasks
|
||||
@@ -121,7 +129,7 @@ import { Stripe } from "@trigger.dev/stripe";
|
||||
|
||||
const stripe = new Stripe({
|
||||
id: "stripe",
|
||||
apiKey: process.env.STRIPE_SECRET_KEY!
|
||||
apiKey: process.env.STRIPE_SECRET_KEY!,
|
||||
});
|
||||
|
||||
async function createCustomer() {
|
||||
@@ -161,7 +169,6 @@ client.defineJob({
|
||||
Behind the scenes, our `@trigger.dev/github` integration will create a webhook on your repository that will call our API when a new push event is received. We will then start your Job with the payload from the push event.
|
||||
|
||||
<Note>
|
||||
If you are just using an integration to trigger a job but not using
|
||||
authenticated tasks inside the job run, there is no need to pass the
|
||||
integration in the job `integrations` option.
|
||||
If you are just using an integration to trigger a job but not using authenticated tasks inside the
|
||||
job run, there is no need to pass the integration in the job `integrations` option.
|
||||
</Note>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 291 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 153 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
+19
-7
@@ -151,7 +151,6 @@
|
||||
"documentation/guides/manual/fastify"
|
||||
]
|
||||
},
|
||||
|
||||
"documentation/guides/running-jobs",
|
||||
"documentation/guides/jobs/managing",
|
||||
{
|
||||
@@ -168,7 +167,8 @@
|
||||
"pages": [
|
||||
"documentation/guides/using-integrations",
|
||||
"documentation/guides/using-integrations-apikeys",
|
||||
"documentation/guides/using-integrations-oauth"
|
||||
"documentation/guides/using-integrations-oauth",
|
||||
"documentation/guides/using-integrations-byo-auth"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -277,7 +277,11 @@
|
||||
"sdk/triggerclient/instancemethods/sendevent",
|
||||
"sdk/triggerclient/instancemethods/getevent",
|
||||
"sdk/triggerclient/instancemethods/getruns",
|
||||
"sdk/triggerclient/instancemethods/getrun"
|
||||
"sdk/triggerclient/instancemethods/getrun",
|
||||
"sdk/triggerclient/instancemethods/define-job",
|
||||
"sdk/triggerclient/instancemethods/define-dynamic-trigger",
|
||||
"sdk/triggerclient/instancemethods/define-dynamic-schedule",
|
||||
"sdk/triggerclient/instancemethods/define-auth-resolver"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -311,7 +315,10 @@
|
||||
"sdk/dynamictrigger/constructor",
|
||||
{
|
||||
"group": "Instance methods",
|
||||
"pages": ["sdk/dynamictrigger/register", "sdk/dynamictrigger/unregister"]
|
||||
"pages": [
|
||||
"sdk/dynamictrigger/register",
|
||||
"sdk/dynamictrigger/unregister"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -322,7 +329,10 @@
|
||||
"sdk/dynamicschedule/constructor",
|
||||
{
|
||||
"group": "Instance methods",
|
||||
"pages": ["sdk/dynamicschedule/register", "sdk/dynamicschedule/unregister"]
|
||||
"pages": [
|
||||
"sdk/dynamicschedule/register",
|
||||
"sdk/dynamicschedule/unregister"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -343,7 +353,9 @@
|
||||
},
|
||||
{
|
||||
"group": "Overview",
|
||||
"pages": ["examples/introduction"]
|
||||
"pages": [
|
||||
"examples/introduction"
|
||||
]
|
||||
}
|
||||
],
|
||||
"footerSocials": {
|
||||
@@ -356,4 +368,4 @@
|
||||
"apiKey": "phc_hwYmedO564b3Ik8nhA4Csrb5SueY0EwFJWCbseGwWW"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ Use this method to unregister a schedule from the DynamicSchedule, using the id
|
||||
|
||||
```typescript
|
||||
//1. create a DynamicSchedule
|
||||
const dynamicSchedule = new DynamicSchedule(client, {
|
||||
const dynamicSchedule = client.defineDynamicSchedule({
|
||||
id: "dynamicinterval",
|
||||
});
|
||||
|
||||
@@ -76,14 +76,17 @@ client.defineJob({
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
//6. Register the DynamicSchedule
|
||||
await io.registerInterval("📆", dynamicSchedule, payload.userId, {
|
||||
seconds: payload.seconds,
|
||||
await dynamicSchedule.register(payload.userId, {
|
||||
type: "interval",
|
||||
options: {
|
||||
seconds: payload.seconds,
|
||||
},
|
||||
});
|
||||
|
||||
await io.wait("wait", 60);
|
||||
|
||||
//7. Unregister the DynamicSchedule if you want
|
||||
await io.unregisterInterval("❌📆", dynamicSchedule, payload.id);
|
||||
//7. Unregister the DynamicSchedule at some later date
|
||||
await dynamicSchedule.unregister(payload.userId);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
@@ -7,8 +7,8 @@ description: "Use this method to register a new schedule with the DynamicSchedul
|
||||
## Parameters
|
||||
|
||||
<ResponseField name="id" type="string" required>
|
||||
The id of the schedule to register. The identifier you use will be available
|
||||
in the `context.source.id` when the Job runs.
|
||||
The id of the schedule to register. The identifier you use will be available in the
|
||||
`context.source.id` when the Job runs.
|
||||
</ResponseField>
|
||||
<ResponseField name="schedule" type="Schedule" required>
|
||||
The schedule to register. It is either a `cron` or `interval` schedule.
|
||||
@@ -24,8 +24,13 @@ description: "Use this method to register a new schedule with the DynamicSchedul
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
<ResponseField name="metadata" type="any">
|
||||
Any additional data you wish to store with the schedule. This will be
|
||||
available in the `context.source.metadata` when the Job runs.
|
||||
Any additional data you wish to store with the schedule. This will be available in the
|
||||
`context.source.metadata` when the Job runs.
|
||||
</ResponseField>
|
||||
<ResponseField name="accountId" type="string">
|
||||
An optional account ID to use when running the job. This will be available in the Job
|
||||
[context](/sdk/context) and can be used in [auth
|
||||
resolvers](/sdk/triggerclient/instancemethods/define-auth-resolver)
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
<Expandable title="interval">
|
||||
@@ -40,8 +45,13 @@ description: "Use this method to register a new schedule with the DynamicSchedul
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
<ResponseField name="metadata" type="any">
|
||||
Any additional data you wish to store with the schedule. This will be
|
||||
available in the `context.source.metadata` when the Job runs.
|
||||
Any additional data you wish to store with the schedule. This will be available in the
|
||||
`context.source.metadata` when the Job runs.
|
||||
</ResponseField>
|
||||
<ResponseField name="accountId" type="string">
|
||||
An optional account ID to use when running the job. This will be available in the Job
|
||||
[context](/sdk/context) and can be used in [auth
|
||||
resolvers](/sdk/triggerclient/instancemethods/define-auth-resolver)
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
@@ -16,9 +16,8 @@ description: "The `DynamicTrigger()` constructor creates a new [DynamicTrigger](
|
||||
Used to uniquely identify a DynamicTrigger
|
||||
</ResponseField>
|
||||
<ResponseField name="event" type="event" required>
|
||||
An event from an [Integration](/integrations) package that you want to
|
||||
attach to the DynamicTrigger. The event types will come through to the
|
||||
payload in your Job's run.
|
||||
An event from an [Integration](/integrations) package that you want to attach to the
|
||||
DynamicTrigger. The event types will come through to the payload in your Job's run.
|
||||
</ResponseField>
|
||||
<ResponseField name="source" type="source" required>
|
||||
An external source fron an [Integration](/integrations) package
|
||||
|
||||
@@ -9,7 +9,7 @@ Sometimes you want to subscribe to a webhook but you don't know the exact config
|
||||
|
||||
### [DynamicTrigger()](/sdk/dynamictrigger/constructor)
|
||||
|
||||
Creates a new `DynamicTrigger` instance.
|
||||
Creates a new `DynamicTrigger` instance. You should use the [`TriggerClient.defineDynamicTrigger`]() method instead of calling this directly.
|
||||
|
||||
## Instance methods
|
||||
|
||||
@@ -33,7 +33,7 @@ Use this method to unregister a schedule from the DynamicTrigger, using the id y
|
||||
|
||||
```typescript DynamicTrigger
|
||||
//1. create a DynamicTrigger
|
||||
const dynamicOnIssueOpenedTrigger = new DynamicTrigger(client, {
|
||||
const dynamicOnIssueOpenedTrigger = client.defineDynamicTrigger({
|
||||
id: "github-issue-opened",
|
||||
event: events.onIssueOpened,
|
||||
source: github.sources.repo,
|
||||
@@ -59,7 +59,7 @@ client.defineJob({
|
||||
//3. Register the DynamicTrigger anywhere in your app
|
||||
async function registerRepo(owner: string, repo: string) {
|
||||
//the first param (key) should be unique
|
||||
await dynamicOnIssueOpenedTrigger.register(`${owner}/${repo}`, {
|
||||
await dynamicOnIssueOpenedTrigger.register(`${owner}-${repo}`, {
|
||||
owner,
|
||||
repo,
|
||||
});
|
||||
@@ -76,16 +76,13 @@ client.defineJob({
|
||||
org: "triggerdotdev",
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
//6. Register the dynamic trigger so you get notified when an issue is opened
|
||||
return await io.registerTrigger(
|
||||
"register-repo",
|
||||
dynamicOnIssueOpenedTrigger,
|
||||
payload.repository.name,
|
||||
{
|
||||
owner: payload.repository.owner.login,
|
||||
repo: payload.repository.name,
|
||||
}
|
||||
);
|
||||
const owner = payload.repository.owner.login;
|
||||
const repo = payload.repository.name;
|
||||
//6. Register the dynamic trigger so you get notified when an issue is opened. A task will automatically be created
|
||||
await dynamicOnIssueOpenedTrigger.register(`${owner}-${repo}`, {
|
||||
owner,
|
||||
repo,
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
@@ -7,12 +7,25 @@ description: "Use this method to register a new configuration with the DynamicTr
|
||||
## Parameters
|
||||
|
||||
<ResponseField name="id" type="string" required>
|
||||
The id of the registration. The identifier you use will be available in the
|
||||
`context.source.id` when the Job runs. It will also be used to unregister.
|
||||
The id of the registration. The identifier you use will be available in the `context.source.id`
|
||||
when the Job runs. It will also be used to unregister.
|
||||
</ResponseField>
|
||||
<ResponseField name="params" type="Object" required>
|
||||
The shape of this object will depend on the type of event you set when
|
||||
constructing the `DynamicTrigger`.
|
||||
The shape of this object will depend on the type of event you set when constructing the
|
||||
`DynamicTrigger`.
|
||||
</ResponseField>
|
||||
<ResponseField name="options" type="object">
|
||||
<Expandable title="fields" defaultOpen>
|
||||
<ResponseField name="accountId" type="string">
|
||||
An optional account ID to use when running the job. This will be available in the Job
|
||||
[context](/sdk/context) and can be used in [auth
|
||||
resolvers](/sdk/triggerclient/instancemethods/define-auth-resolver)
|
||||
</ResponseField>
|
||||
<ResponseField name="filter" type="EventFilter">
|
||||
An optional filter to apply to the event. See our [EventFilter
|
||||
guide](/documentation/guides/event-filter) for more
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
## Returns
|
||||
|
||||
@@ -4,6 +4,10 @@ sidebarTitle: "registerCron()"
|
||||
description: "`io.registerCron()` allows you to register a [DynamicSchedule](/sdk/dynamicschedule) that will trigger any jobs it's attached to on a regular CRON schedule."
|
||||
---
|
||||
|
||||
<Warning>
|
||||
This has been deprecated in favor of [DynamicSchedule.register](/sdk/dynamicschedule/register)
|
||||
</Warning>
|
||||
|
||||
## Parameters
|
||||
|
||||
<Snippet file="stable-key-param.mdx" />
|
||||
|
||||
@@ -4,6 +4,10 @@ sidebarTitle: "registerInterval()"
|
||||
description: "`io.registerInterval()` allows you to register a [DynamicSchedule](/sdk/dynamicschedule) that will trigger any jobs it's attached to on a regular interval."
|
||||
---
|
||||
|
||||
<Warning>
|
||||
This has been deprecated in favor of [DynamicSchedule.register](/sdk/dynamicschedule/register)
|
||||
</Warning>
|
||||
|
||||
## Parameters
|
||||
|
||||
<Snippet file="stable-key-param.mdx" />
|
||||
|
||||
@@ -4,6 +4,10 @@ sidebarTitle: "registerTrigger()"
|
||||
description: "`io.registerTrigger()` allows you to register a [DynamicTrigger](/sdk/dynamictrigger) with the specified trigger data."
|
||||
---
|
||||
|
||||
<Warning>
|
||||
This has been deprecated in favor of [DynamicTrigger.register](/sdk/dynamictrigger/register)
|
||||
</Warning>
|
||||
|
||||
## Parameters
|
||||
|
||||
<Snippet file="stable-key-param.mdx" />
|
||||
@@ -30,9 +34,9 @@ A Promise that resolves to an object with the following fields:
|
||||
|
||||
## Example
|
||||
|
||||
```typescript
|
||||
```ts
|
||||
//1. create a DynamicTrigger
|
||||
const dynamicOnIssueOpenedTrigger = new DynamicTrigger(client, {
|
||||
const dynamicOnIssueOpenedTrigger = client.defineDynamicTrigger({
|
||||
id: "github-issue-opened",
|
||||
event: events.onIssueOpened,
|
||||
source: github.sources.repo,
|
||||
|
||||
@@ -4,6 +4,10 @@ sidebarTitle: "unregisterCron()"
|
||||
description: "`io.unregisterCron()` allows you to unregister a [DynamicSchedule](/sdk/dynamicschedule) that was previously registered with `io.registerCron()`."
|
||||
---
|
||||
|
||||
<Warning>
|
||||
This has been deprecated in favor of [DynamicSchedule.unregister](/sdk/dynamicschedule/unregister)
|
||||
</Warning>
|
||||
|
||||
## Parameters
|
||||
|
||||
<Snippet file="stable-key-param.mdx" />
|
||||
|
||||
@@ -4,6 +4,10 @@ sidebarTitle: "unregisterInterval()"
|
||||
description: "`io.unregisterInterval()` allows you to unregister a [DynamicSchedule](/sdk/dynamicschedule) that was previously registered with `io.registerInterval()`."
|
||||
---
|
||||
|
||||
<Warning>
|
||||
This has been deprecated in favor of [DynamicSchedule.unregister](/sdk/dynamicschedule/unregister)
|
||||
</Warning>
|
||||
|
||||
## Parameters
|
||||
|
||||
<Snippet file="stable-key-param.mdx" />
|
||||
|
||||
@@ -4,6 +4,10 @@ sidebarTitle: "unregisterTrigger()"
|
||||
description: "`io.unregisterTrigger()` allows you to unregister a [DynamicTrigger](/sdk/dynamictrigger) that was previously registered with `io.registerTrigger()`."
|
||||
---
|
||||
|
||||
<Warning>
|
||||
This has been deprecated in favor of [DynamicTrigger.unregister](/sdk/dynamictrigger/unregister)
|
||||
</Warning>
|
||||
|
||||
## Parameters
|
||||
|
||||
<Snippet file="stable-key-param.mdx" />
|
||||
|
||||
+1
-49
@@ -59,55 +59,7 @@ client.defineJob({
|
||||
An instance of [TriggerClient](/sdk/triggerclient) that is used to send events to the Trigger API.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="options" type="object" required>
|
||||
<Expandable title="properties" defaultOpen>
|
||||
<ParamField body="id" type="string" required>
|
||||
The `id` property is used to uniquely identify the Job. Only change this if you want to create a new Job.
|
||||
</ParamField>
|
||||
<ParamField body="name" type="string" required>
|
||||
The `name` of the Job that you want to appear in the dashboard and logs. You can change this without creating a new Job.
|
||||
</ParamField>
|
||||
<ParamField body="version" type="string" required>
|
||||
The `version` property is used to version your Job. A new version will be created if you change this property. We recommend using [semantic versioning](https://www.baeldung.com/cs/semantic-versioning), e.g. `1.0.3`.
|
||||
</ParamField>
|
||||
<ParamField body="trigger" type="object" required>
|
||||
The `trigger` property is used to define when the Job should run. There are currently the following Trigger types:
|
||||
- [cronTrigger](/sdk/crontrigger)
|
||||
- [intervalTrigger](/sdk/intervaltrigger)
|
||||
- [eventTrigger](/sdk/eventtrigger)
|
||||
- [DynamicTrigger](/sdk/dynamictrigger)
|
||||
- [DynamicSchedule](/sdk/dynamicschedule)
|
||||
- integration Triggers, like webhooks. See the [integrations](/integrations) page for more information.
|
||||
</ParamField>
|
||||
<ParamField body="run" type="function" required>
|
||||
This function gets called automatically when a Run is Triggered. It has three parameters:
|
||||
1. `payload` – The payload that was sent to the Trigger API.
|
||||
2. [io](/sdk/io) – An object that contains the integrations that you specified in the `integrations` property and other useful functions like delays and running Tasks.
|
||||
3. [context](/sdk/context) – An object that contains information about the Organization, Job, Run and more.
|
||||
|
||||
This is where you put the code you want to run for a Job. You can use normal code in here and you can also use Tasks.
|
||||
|
||||
You can return a value from this function and it will be sent back to the Trigger API.
|
||||
</ParamField>
|
||||
<ParamField body="integrations" type="object">
|
||||
Imports the specified integrations into the Job. The integrations will be available on the `io` object in the `run()` function with the same name as the key. For example:
|
||||
<Snippet file="how-to-pass-integrations.mdx" />
|
||||
</ParamField>
|
||||
<ParamField body="enabled" type="boolean">
|
||||
The `enabled` property is an optional property that specifies whether the Job is enabled or not. The Job will be enabled by default if you omit this property. When a job is disabled, no new runs will be triggered or resumed. In progress runs will continue to run until they are finished or delayed by using `io.wait`.
|
||||
</ParamField>
|
||||
<ParamField body="logLevel" type="log | error | warn | info | debug">
|
||||
The `logLevel` property is an optional property that specifies the level of
|
||||
logging for the Job. The level is inherited from the client if you omit this property.
|
||||
- `log` - logs only essential messages
|
||||
- `error` - logs error messages
|
||||
- `warn` - logs errors and warning messages
|
||||
- `info` - logs errors, warnings and info messages
|
||||
- `debug` - logs everything with full verbosity
|
||||
</ParamField>
|
||||
|
||||
</Expandable>
|
||||
</ParamField>
|
||||
<Snippet file="jobs/options.mdx" />
|
||||
|
||||
## Returns
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: "defineAuthResolver()"
|
||||
description: "Define a custom auth resolver for a specific integration"
|
||||
---
|
||||
|
||||
Auth Resolvers allow you to inject the authentication credentials of **your users**, using a third-party service like [Clerk](https://clerk.com/) or [Nango](https://www.nango.dev/) or your own custom solution.
|
||||
|
||||
See our [Bring-your-own Auth Guide](/documentation/guides/using-integrations-byo-auth) for more about how this works.
|
||||
|
||||
<RequestExample>
|
||||
|
||||
```ts example
|
||||
client.defineAuthResolver(slack, async (ctx) => {
|
||||
if (!ctx.account?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tokens = await clerk.users.getUserOauthAccessToken(ctx.account.id, "oauth_slack");
|
||||
|
||||
if (tokens.length === 0) {
|
||||
throw new Error(`Could not find Slack auth for account ${ctx.account.id}`);
|
||||
}
|
||||
|
||||
return {
|
||||
type: "oauth",
|
||||
token: tokens[0].token,
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
</RequestExample>
|
||||
|
||||
## Parameters
|
||||
|
||||
<ParamField body="integration" type="TriggerIntegration" required>
|
||||
The Integration client (e.g. `slack`) to define the auth resolver for.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="resolver" type="AuthResolver" required>
|
||||
The resolver function to use for this integration. Should return a [AuthResolverResult](#authresolverresult) object.
|
||||
|
||||
{" "}
|
||||
|
||||
<Expandable title="arguments" defaultOpen>
|
||||
<ParamField body="ctx" type="TriggerContext" required>
|
||||
The [TriggerContext](/sdk/context) object for the run that is requesting authentication.
|
||||
</ParamField>
|
||||
<ParamField body="integration" type="TriggerIntegration">
|
||||
The Integration client that is requesting authentication.
|
||||
</ParamField>
|
||||
</Expandable>
|
||||
|
||||
</ParamField>
|
||||
|
||||
## AuthResolverResult
|
||||
|
||||
<ParamField body="type" type="string" required>
|
||||
Should be either "apiKey" or "oauth"
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="token" type="string" required>
|
||||
The authentication token to use for this integration.
|
||||
</ParamField>
|
||||
|
||||
<ParamField body="additionalFields" type="Record<string, string>">
|
||||
Additional fields to pass to the integration.
|
||||
</ParamField>
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
title: "defineDynamicSchedule()"
|
||||
description: "Define a Dynamic Schedule"
|
||||
---
|
||||
|
||||
## Parameters
|
||||
|
||||
<ResponseField name="options" type="DynamicScheduleOptions" required>
|
||||
The options for the dynamic schedule.
|
||||
<Expandable title="options" defaultOpen>
|
||||
<ResponseField name="id" type="string" required>
|
||||
Used to uniquely identify a DynamicSchedule
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
## Returns
|
||||
|
||||
<ResponseField name="DynamicSchedule instance" type="DynamicSchedule" />
|
||||
|
||||
<RequestExample>
|
||||
|
||||
```ts example
|
||||
const dynamicSchedule = client.defineDynamicSchedule({
|
||||
id: "dynamicinterval",
|
||||
});
|
||||
```
|
||||
|
||||
</RequestExample>
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
title: "defineDynamicTrigger()"
|
||||
description: "Define a Dynamic Trigger"
|
||||
---
|
||||
|
||||
## Parameters
|
||||
|
||||
<ResponseField name="options" type="DynamicTriggerOptions" required>
|
||||
The options for the dynamic trigger.
|
||||
<Expandable title="options" defaultOpen>
|
||||
<ResponseField name="id" type="string" required>
|
||||
Used to uniquely identify a DynamicTrigger
|
||||
</ResponseField>
|
||||
<ResponseField name="event" type="event" required>
|
||||
An event from an [Integration](/integrations) package that you want to attach to the
|
||||
DynamicTrigger. The event types will come through to the payload in your Job's run.
|
||||
</ResponseField>
|
||||
<ResponseField name="source" type="source" required>
|
||||
An external source fron an [Integration](/integrations) package
|
||||
</ResponseField>
|
||||
</Expandable>
|
||||
</ResponseField>
|
||||
|
||||
## Returns
|
||||
|
||||
<ResponseField name="DynamicTrigger instance" type="DynamicTrigger" />
|
||||
|
||||
<RequestExample>
|
||||
|
||||
```ts example
|
||||
const dynamicOnIssueOpenedTrigger = client.defineDynamicTrigger({
|
||||
id: "github-issue-opened",
|
||||
event: events.onIssueOpened,
|
||||
source: github.sources.repo,
|
||||
});
|
||||
```
|
||||
|
||||
</RequestExample>
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: "defineJob()"
|
||||
description: "Defines a job"
|
||||
---
|
||||
|
||||
A [Job](/documentation/concepts/jobs) is used to define the [Trigger](/documentation/concepts/triggers), metadata, and what happens when it runs.
|
||||
|
||||
<RequestExample>
|
||||
|
||||
```ts example
|
||||
client.defineJob({
|
||||
id: "github-integration-on-issue",
|
||||
name: "GitHub Integration - On Issue",
|
||||
version: "0.1.0",
|
||||
trigger: github.triggers.repo({
|
||||
event: events.onIssue,
|
||||
owner: "triggerdotdev",
|
||||
repo: "empty",
|
||||
}),
|
||||
run: async (payload, io, ctx) => {
|
||||
await io.logger.info("This is a simple log info message");
|
||||
return { payload, ctx };
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
</RequestExample>
|
||||
|
||||
## Parameters
|
||||
|
||||
<Snippet file="jobs/options.mdx" />
|
||||
|
||||
## Returns
|
||||
|
||||
<ResponseField name="Job instance" type="Job" />
|
||||
@@ -4,7 +4,7 @@ sidebarTitle: "sendEvent()"
|
||||
description: "The `sendEvent()` instance method send an event that triggers any Jobs that are listening for that event (based on the name)."
|
||||
---
|
||||
|
||||
You can call this function from anywhere in your code to send an event. The other way to send an event is by using [io.sendEvent()](/sdk/io/sendevent) from inside a `run()` function.
|
||||
You can call this function from anywhere in your backend to send an event. The other way to send an event is by using [io.sendEvent()](/sdk/io/sendevent) from inside a `run()` function.
|
||||
|
||||
Use [eventTrigger()](/sdk/eventtrigger) on a Job to listen for events.
|
||||
|
||||
|
||||
@@ -43,3 +43,19 @@ The `getRuns()` method gets runs for a Job.
|
||||
#### [getRun()](/sdk/triggerclient/instancemethods/getrun)
|
||||
|
||||
The `getRun()` method gets the details for a given Run.
|
||||
|
||||
#### [defineJob()](/sdk/triggerclient/instancemethods/define-job)
|
||||
|
||||
The `defineJob()` method defines a new Job.
|
||||
|
||||
#### [defineDynamicTrigger()](/sdk/triggerclient/instancemethods/define-dynamic-trigger)
|
||||
|
||||
The `defineDynamicTrigger()` method defines a new Dynamic Trigger.
|
||||
|
||||
#### [defineDynamicSchedule()](/sdk/triggerclient/instancemethods/define-dynamic-schedule)
|
||||
|
||||
The `defineDynamicSchedule()` method defines a new Dynamic Schedule.
|
||||
|
||||
#### [defineAuthResolver()](/sdk/triggerclient/instancemethods/define-auth-resolver)
|
||||
|
||||
The `defineAuthResolver()` method defines a new Auth Resolver.
|
||||
|
||||
@@ -11,13 +11,10 @@ export type AirtableRecordsParams = TableParams<{}>;
|
||||
export type AirtableRecords = Records<FieldSet>;
|
||||
|
||||
export class Base {
|
||||
runTask: AirtableRunTask;
|
||||
baseId: string;
|
||||
|
||||
constructor(runTask: AirtableRunTask, baseId: string) {
|
||||
this.runTask = runTask;
|
||||
this.baseId = baseId;
|
||||
}
|
||||
constructor(
|
||||
private runTask: AirtableRunTask,
|
||||
public baseId: string
|
||||
) {}
|
||||
|
||||
table<TFields extends AirtableFieldSet>(tableName: string) {
|
||||
return new Table<TFields>(this.runTask, this.baseId, tableName);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Prettify } from "@trigger.dev/integration-kit";
|
||||
import {
|
||||
Json,
|
||||
retry,
|
||||
type ConnectionAuth,
|
||||
type IO,
|
||||
type IOTask,
|
||||
@@ -8,18 +9,10 @@ import {
|
||||
type RunTaskErrorCallback,
|
||||
type RunTaskOptions,
|
||||
type TriggerIntegration,
|
||||
retry,
|
||||
} from "@trigger.dev/sdk";
|
||||
import AirtableSDK from "airtable";
|
||||
import { Base } from "./base";
|
||||
import * as events from "./events";
|
||||
import {
|
||||
WebhookChangeType,
|
||||
WebhookDataType,
|
||||
Webhooks,
|
||||
createTrigger,
|
||||
createWebhookEventSource,
|
||||
} from "./webhooks";
|
||||
import { Webhooks, createWebhookEventSource } from "./webhooks";
|
||||
|
||||
export * from "./types";
|
||||
|
||||
@@ -33,9 +26,13 @@ export type AirtableIntegrationOptions = {
|
||||
export type AirtableRunTask = InstanceType<typeof Airtable>["runTask"];
|
||||
|
||||
export class Airtable implements TriggerIntegration {
|
||||
// @internal
|
||||
private _options: AirtableIntegrationOptions;
|
||||
// @internal
|
||||
private _client?: AirtableSDK;
|
||||
// @internal
|
||||
private _io?: IO;
|
||||
// @internal
|
||||
private _connectionKey?: string;
|
||||
|
||||
constructor(options: Prettify<AirtableIntegrationOptions>) {
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
},
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
"baseUrl": "."
|
||||
"baseUrl": ".",
|
||||
"stripInternal": true
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
@@ -5,15 +5,11 @@ import { Issues } from "./issues";
|
||||
import { ReactionContent, Reactions } from "./reactions";
|
||||
|
||||
export class Compound {
|
||||
runTask: GitHubRunTask;
|
||||
issues: Issues;
|
||||
reactions: Reactions;
|
||||
|
||||
constructor(runTask: GitHubRunTask, issues: Issues, reactions: Reactions) {
|
||||
this.runTask = runTask;
|
||||
this.issues = issues;
|
||||
this.reactions = reactions;
|
||||
}
|
||||
constructor(
|
||||
private runTask: GitHubRunTask,
|
||||
public issues: Issues,
|
||||
public reactions: Reactions
|
||||
) {}
|
||||
|
||||
createIssueCommentWithReaction(
|
||||
key: IntegrationTaskKey,
|
||||
|
||||
@@ -34,11 +34,7 @@ type TreeType = {
|
||||
};
|
||||
|
||||
export class Git {
|
||||
runTask: GitHubRunTask;
|
||||
|
||||
constructor(runTask: GitHubRunTask) {
|
||||
this.runTask = runTask;
|
||||
}
|
||||
constructor(private runTask: GitHubRunTask) {}
|
||||
|
||||
createBlob(
|
||||
key: IntegrationTaskKey,
|
||||
|
||||
@@ -68,9 +68,13 @@ export type GitHubReturnType<T extends (params: any) => Promise<{ data: K }>, K
|
||||
>;
|
||||
|
||||
export class Github implements TriggerIntegration {
|
||||
// @internal
|
||||
private _options: GithubIntegrationOptions;
|
||||
// @internal
|
||||
private _client?: Octokit;
|
||||
// @internal
|
||||
private _io?: IO;
|
||||
// @internal
|
||||
private _connectionKey?: string;
|
||||
|
||||
_repoSource: ReturnType<typeof createRepoEventSource>;
|
||||
|
||||
@@ -6,11 +6,7 @@ import { issueProperties, repoProperties } from "./propertyHelpers";
|
||||
|
||||
type AddIssueLabels = GitHubReturnType<Octokit["rest"]["issues"]["addLabels"]>;
|
||||
export class Issues {
|
||||
runTask: GitHubRunTask;
|
||||
|
||||
constructor(runTask: GitHubRunTask) {
|
||||
this.runTask = runTask;
|
||||
}
|
||||
constructor(private runTask: GitHubRunTask) {}
|
||||
|
||||
create(
|
||||
key: IntegrationTaskKey,
|
||||
|
||||
@@ -3,11 +3,7 @@ import { Octokit } from "octokit";
|
||||
import { GitHubReturnType, GitHubRunTask, onError } from "./index";
|
||||
|
||||
export class Orgs {
|
||||
runTask: GitHubRunTask;
|
||||
|
||||
constructor(runTask: GitHubRunTask) {
|
||||
this.runTask = runTask;
|
||||
}
|
||||
constructor(private runTask: GitHubRunTask) {}
|
||||
|
||||
updateWebhook(
|
||||
key: IntegrationTaskKey,
|
||||
|
||||
@@ -15,11 +15,7 @@ export type ReactionContent =
|
||||
| "eyes";
|
||||
|
||||
export class Reactions {
|
||||
runTask: GitHubRunTask;
|
||||
|
||||
constructor(runTask: GitHubRunTask) {
|
||||
this.runTask = runTask;
|
||||
}
|
||||
constructor(private runTask: GitHubRunTask) {}
|
||||
|
||||
createForIssueComment(
|
||||
key: IntegrationTaskKey,
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import { truncate } from "@trigger.dev/integration-kit";
|
||||
import { IntegrationTaskKey, Prettify, retry } from "@trigger.dev/sdk";
|
||||
import { GitHubReturnType, GitHubRunTask, onError } from "./index";
|
||||
import { IntegrationTaskKey } from "@trigger.dev/sdk";
|
||||
import { Octokit } from "octokit";
|
||||
import { issueProperties, repoProperties } from "./propertyHelpers";
|
||||
import { GitHubReturnType, GitHubRunTask, onError } from "./index";
|
||||
|
||||
export class Repos {
|
||||
runTask: GitHubRunTask;
|
||||
|
||||
constructor(runTask: GitHubRunTask) {
|
||||
this.runTask = runTask;
|
||||
}
|
||||
constructor(private runTask: GitHubRunTask) {}
|
||||
|
||||
get(
|
||||
key: IntegrationTaskKey,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Webhooks } from "@octokit/webhooks";
|
||||
import { ExternalSource, TriggerIntegration, HandlerEvent } from "@trigger.dev/sdk";
|
||||
import { omit, safeJsonParse } from "@trigger.dev/integration-kit";
|
||||
import type { Logger } from "@trigger.dev/sdk";
|
||||
import { safeJsonParse, omit } from "@trigger.dev/integration-kit";
|
||||
import { Octokit } from "octokit";
|
||||
import { ExternalSource, HandlerEvent } from "@trigger.dev/sdk";
|
||||
import { z } from "zod";
|
||||
import { Github } from "./index";
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
},
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
"baseUrl": "."
|
||||
"baseUrl": ".",
|
||||
"stripInternal": true
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
@@ -4,11 +4,7 @@ import { OpenAIRunTask } from "./index";
|
||||
import { createTaskUsageProperties } from "./taskUtils";
|
||||
|
||||
export class Chat {
|
||||
runTask: OpenAIRunTask;
|
||||
|
||||
constructor(runTask: OpenAIRunTask) {
|
||||
this.runTask = runTask;
|
||||
}
|
||||
constructor(private runTask: OpenAIRunTask) {}
|
||||
|
||||
completions = {
|
||||
create: (
|
||||
|
||||
@@ -23,9 +23,13 @@ import { FineTunes } from "./fineTunes";
|
||||
export type OpenAIRunTask = InstanceType<typeof OpenAI>["runTask"];
|
||||
|
||||
export class OpenAI implements TriggerIntegration {
|
||||
// @internal
|
||||
private _options: OpenAIIntegrationOptions;
|
||||
// @internal
|
||||
private _client?: OpenAIApi;
|
||||
// @internal
|
||||
private _io?: IO;
|
||||
// @internal
|
||||
private _connectionKey?: string;
|
||||
|
||||
/**
|
||||
@@ -46,10 +50,6 @@ export class OpenAI implements TriggerIntegration {
|
||||
public readonly native: OpenAIApi;
|
||||
|
||||
constructor(private options: OpenAIIntegrationOptions) {
|
||||
if (Object.keys(options).includes("apiKey") && !options.apiKey) {
|
||||
throw `Can't create OpenAI integration (${options.id}) as apiKey was undefined`;
|
||||
}
|
||||
|
||||
this._options = options;
|
||||
|
||||
this.native = new OpenAIApi({
|
||||
@@ -63,11 +63,19 @@ export class OpenAI implements TriggerIntegration {
|
||||
}
|
||||
|
||||
cloneForRun(io: IO, connectionKey: string, auth?: ConnectionAuth) {
|
||||
const apiKey = this._options.apiKey ?? auth?.accessToken;
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error(
|
||||
`Can't initialize OpenAI integration (${this._options.id}) as apiKey was undefined`
|
||||
);
|
||||
}
|
||||
|
||||
const openai = new OpenAI(this._options);
|
||||
openai._io = io;
|
||||
openai._connectionKey = connectionKey;
|
||||
openai._client = new OpenAIApi({
|
||||
apiKey: this._options.apiKey,
|
||||
apiKey,
|
||||
organization: this._options.organization,
|
||||
});
|
||||
return openai;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export type OpenAIIntegrationOptions = {
|
||||
id: string;
|
||||
apiKey: string;
|
||||
apiKey?: string;
|
||||
organization?: string;
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
},
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
"baseUrl": "."
|
||||
"baseUrl": ".",
|
||||
"stripInternal": true
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
@@ -23,21 +23,21 @@ import {
|
||||
|
||||
export type PlainIntegrationOptions = {
|
||||
id: string;
|
||||
apiKey: string;
|
||||
apiKey?: string;
|
||||
apiUrl?: string;
|
||||
};
|
||||
|
||||
export class Plain implements TriggerIntegration {
|
||||
// @internal
|
||||
private _options: PlainIntegrationOptions;
|
||||
// @internal
|
||||
private _client?: PlainClient;
|
||||
// @internal
|
||||
private _io?: IO;
|
||||
// @internal
|
||||
private _connectionKey?: string;
|
||||
|
||||
constructor(private options: PlainIntegrationOptions) {
|
||||
if (Object.keys(options).includes("apiKey") && !options.apiKey) {
|
||||
throw `Can't create Plain integration (${options.id}) as apiKey was undefined`;
|
||||
}
|
||||
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
@@ -46,11 +46,19 @@ export class Plain implements TriggerIntegration {
|
||||
}
|
||||
|
||||
cloneForRun(io: IO, connectionKey: string, auth?: ConnectionAuth) {
|
||||
const apiKey = this._options.apiKey ?? auth?.accessToken;
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error(
|
||||
`Can't initialize Plain integration (${this._options.id}) as apiKey was undefined`
|
||||
);
|
||||
}
|
||||
|
||||
const plain = new Plain(this._options);
|
||||
plain._io = io;
|
||||
plain._connectionKey = connectionKey;
|
||||
plain._client = new PlainClient({
|
||||
apiKey: this._options.apiKey,
|
||||
apiKey,
|
||||
apiUrl: this._options.apiUrl,
|
||||
});
|
||||
return plain;
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
},
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
"baseUrl": "."
|
||||
"baseUrl": ".",
|
||||
"stripInternal": true
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
@@ -39,20 +39,27 @@ function onError(error: unknown) {
|
||||
|
||||
export type ResendIntegrationOptions = {
|
||||
id: string;
|
||||
apiKey: string;
|
||||
apiKey?: string;
|
||||
};
|
||||
|
||||
export class Resend implements TriggerIntegration {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
private _options: ResendIntegrationOptions;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
private _client?: ResendClient;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
private _io?: IO;
|
||||
|
||||
// @internal
|
||||
private _connectionKey?: string;
|
||||
|
||||
constructor(private options: ResendIntegrationOptions) {
|
||||
if (Object.keys(options).includes("apiKey") && !options.apiKey) {
|
||||
throw `Can't create Resend integration (${options.id}) as apiKey was undefined`;
|
||||
}
|
||||
|
||||
constructor(options: ResendIntegrationOptions) {
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
@@ -61,15 +68,23 @@ export class Resend implements TriggerIntegration {
|
||||
}
|
||||
|
||||
cloneForRun(io: IO, connectionKey: string, auth?: ConnectionAuth) {
|
||||
const apiKey = this._options.apiKey ?? auth?.accessToken;
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error(
|
||||
`Can't create Resend integration (${this._options.id}) as apiKey was undefined`
|
||||
);
|
||||
}
|
||||
|
||||
const resend = new Resend(this._options);
|
||||
resend._io = io;
|
||||
resend._connectionKey = connectionKey;
|
||||
resend._client = new ResendClient(this._options.apiKey);
|
||||
resend._client = new ResendClient(apiKey);
|
||||
return resend;
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this.options.id;
|
||||
return this._options.id;
|
||||
}
|
||||
|
||||
get metadata() {
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
},
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
"baseUrl": "."
|
||||
"baseUrl": ".",
|
||||
"stripInternal": true
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
@@ -15,20 +15,20 @@ type SendEmailData = Parameters<InstanceType<typeof MailService>["send"]>[0];
|
||||
|
||||
export type SendGridIntegrationOptions = {
|
||||
id: string;
|
||||
apiKey: string;
|
||||
apiKey?: string;
|
||||
};
|
||||
|
||||
export class SendGrid implements TriggerIntegration {
|
||||
// @internal
|
||||
private _options: SendGridIntegrationOptions;
|
||||
// @internal
|
||||
private _client?: MailService;
|
||||
// @internal
|
||||
private _io?: IO;
|
||||
// @internal
|
||||
private _connectionKey?: string;
|
||||
|
||||
constructor(private options: SendGridIntegrationOptions) {
|
||||
if (!options.apiKey) {
|
||||
throw new Error(`Can't create SendGrid integration (${options.id}) as apiKey was undefined`);
|
||||
}
|
||||
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
@@ -37,11 +37,19 @@ export class SendGrid implements TriggerIntegration {
|
||||
}
|
||||
|
||||
cloneForRun(io: IO, connectionKey: string, auth?: ConnectionAuth) {
|
||||
const apiKey = this._options.apiKey ?? auth?.accessToken;
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error(
|
||||
`Can't initialize SendGrid integration (${this._options.id}) as apiKey was undefined`
|
||||
);
|
||||
}
|
||||
|
||||
const sendgrid = new SendGrid(this._options);
|
||||
sendgrid._io = io;
|
||||
sendgrid._connectionKey = connectionKey;
|
||||
sendgrid._client = new MailService();
|
||||
sendgrid._client.setApiKey(this._options.apiKey);
|
||||
sendgrid._client.setApiKey(apiKey);
|
||||
return sendgrid;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
},
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
"baseUrl": "."
|
||||
"baseUrl": ".",
|
||||
"stripInternal": true
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
@@ -47,9 +47,13 @@ export type ChatPostMessageArguments = {
|
||||
};
|
||||
|
||||
export class Slack implements TriggerIntegration {
|
||||
// @internal
|
||||
private _options: SlackIntegrationOptions;
|
||||
// @internal
|
||||
private _client?: WebClient;
|
||||
// @internal
|
||||
private _io?: IO;
|
||||
// @internal
|
||||
private _connectionKey?: string;
|
||||
|
||||
constructor(private options: SlackIntegrationOptions) {
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
},
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
"baseUrl": "."
|
||||
"baseUrl": ".",
|
||||
"stripInternal": true
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
@@ -2,11 +2,7 @@ import { IntegrationTaskKey } from "@trigger.dev/sdk";
|
||||
import { CreateChargeParams, CreateChargeResponse, StripeRunTask } from "./index";
|
||||
|
||||
export class Charges {
|
||||
runTask: StripeRunTask;
|
||||
|
||||
constructor(runTask: StripeRunTask) {
|
||||
this.runTask = runTask;
|
||||
}
|
||||
constructor(private runTask: StripeRunTask) {}
|
||||
|
||||
/**
|
||||
* Use the [Payment Intents API](https://stripe.com/docs/api/payment_intents) to initiate a new payment instead
|
||||
|
||||
@@ -2,11 +2,7 @@ import { IntegrationTaskKey } from "@trigger.dev/sdk";
|
||||
import { CreateCheckoutSessionParams, CreateCheckoutSessionResponse, StripeRunTask } from "./index";
|
||||
|
||||
export class Checkout {
|
||||
runTask: StripeRunTask;
|
||||
|
||||
constructor(runTask: StripeRunTask) {
|
||||
this.runTask = runTask;
|
||||
}
|
||||
constructor(private runTask: StripeRunTask) {}
|
||||
|
||||
sessions = {
|
||||
/**
|
||||
|
||||
@@ -9,11 +9,7 @@ import {
|
||||
import { omit } from "./utils";
|
||||
|
||||
export class Customers {
|
||||
runTask: StripeRunTask;
|
||||
|
||||
constructor(runTask: StripeRunTask) {
|
||||
this.runTask = runTask;
|
||||
}
|
||||
constructor(private runTask: StripeRunTask) {}
|
||||
|
||||
create(key: IntegrationTaskKey, params: CreateCustomerParams): Promise<CreateCustomerResponse> {
|
||||
return this.runTask(
|
||||
|
||||
@@ -51,9 +51,13 @@ export * from "./types";
|
||||
export type StripeRunTask = InstanceType<typeof Stripe>["runTask"];
|
||||
|
||||
export class Stripe implements TriggerIntegration {
|
||||
// @internal
|
||||
private _options: StripeIntegrationOptions;
|
||||
// @internal
|
||||
private _client?: StripeClient;
|
||||
// @internal
|
||||
private _io?: IO;
|
||||
// @internal
|
||||
private _connectionKey?: string;
|
||||
|
||||
/**
|
||||
@@ -71,23 +75,25 @@ export class Stripe implements TriggerIntegration {
|
||||
* const customer = await stripe.native.customers.create({}); // etc.
|
||||
* ```
|
||||
*/
|
||||
public readonly native: StripeClient;
|
||||
public readonly native?: StripeClient;
|
||||
|
||||
constructor(private options: StripeIntegrationOptions) {
|
||||
this._options = options;
|
||||
|
||||
this.native = new StripeClient(options.apiKey, {
|
||||
apiVersion: "2022-11-15",
|
||||
typescript: true,
|
||||
timeout: 10000,
|
||||
maxNetworkRetries: 0,
|
||||
stripeAccount: options.stripeAccount,
|
||||
appInfo: {
|
||||
name: "Trigger.dev Stripe Integration",
|
||||
version: "0.1.0",
|
||||
url: "https://trigger.dev",
|
||||
},
|
||||
});
|
||||
this.native = options.apiKey
|
||||
? new StripeClient(options.apiKey, {
|
||||
apiVersion: "2022-11-15",
|
||||
typescript: true,
|
||||
timeout: 10000,
|
||||
maxNetworkRetries: 0,
|
||||
stripeAccount: options.stripeAccount,
|
||||
appInfo: {
|
||||
name: "Trigger.dev Stripe Integration",
|
||||
version: "0.1.0",
|
||||
url: "https://trigger.dev",
|
||||
},
|
||||
})
|
||||
: undefined;
|
||||
}
|
||||
|
||||
get authSource() {
|
||||
@@ -95,10 +101,18 @@ export class Stripe implements TriggerIntegration {
|
||||
}
|
||||
|
||||
cloneForRun(io: IO, connectionKey: string, auth?: ConnectionAuth) {
|
||||
const apiKey = this._options.apiKey ?? auth?.accessToken;
|
||||
|
||||
if (!apiKey) {
|
||||
throw new Error(
|
||||
`Can't initialize Stripe integration (${this._options.id}) as apiKey was undefined`
|
||||
);
|
||||
}
|
||||
|
||||
const stripe = new Stripe(this._options);
|
||||
stripe._io = io;
|
||||
stripe._connectionKey = connectionKey;
|
||||
stripe._client = new StripeClient(this._options.apiKey, {
|
||||
stripe._client = new StripeClient(apiKey, {
|
||||
apiVersion: "2022-11-15",
|
||||
typescript: true,
|
||||
timeout: 10000,
|
||||
|
||||
@@ -3,11 +3,7 @@ import { RetrieveSubscriptionParams, RetrieveSubscriptionResponse, StripeRunTask
|
||||
import { omit } from "./utils";
|
||||
|
||||
export class Subscriptions {
|
||||
runTask: StripeRunTask;
|
||||
|
||||
constructor(runTask: StripeRunTask) {
|
||||
this.runTask = runTask;
|
||||
}
|
||||
constructor(private runTask: StripeRunTask) {}
|
||||
|
||||
/**
|
||||
* Retrieves the subscription with the given ID.
|
||||
|
||||
@@ -6,7 +6,7 @@ export type StripeSDK = Stripe;
|
||||
|
||||
export type StripeIntegrationOptions = {
|
||||
id: string;
|
||||
apiKey: string;
|
||||
apiKey?: string;
|
||||
|
||||
/**
|
||||
* An account id on whose behalf you wish to make every request.
|
||||
|
||||
@@ -11,11 +11,7 @@ import {
|
||||
import { omit } from "./utils";
|
||||
|
||||
export class WebhookEndpoints {
|
||||
runTask: StripeRunTask;
|
||||
|
||||
constructor(runTask: StripeRunTask) {
|
||||
this.runTask = runTask;
|
||||
}
|
||||
constructor(private runTask: StripeRunTask) {}
|
||||
|
||||
create(key: IntegrationTaskKey, params: CreateWebhookParams): Promise<CreateWebhookResponse> {
|
||||
return this.runTask(
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
"resolveJsonModule": true,
|
||||
"lib": ["es2019"],
|
||||
"module": "commonjs",
|
||||
"target": "es2021"
|
||||
"target": "es2021",
|
||||
"stripInternal": true
|
||||
},
|
||||
"include": ["./src/**/*.ts", "tsup.config.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
|
||||
@@ -55,9 +55,13 @@ export class Supabase<
|
||||
: any,
|
||||
> implements TriggerIntegration
|
||||
{
|
||||
// @internal
|
||||
private _options: SupabaseIntegrationOptions<SchemaName>;
|
||||
// @internal
|
||||
private _client?: SupabaseClient<Database, SchemaName, Schema>;
|
||||
// @internal
|
||||
private _io?: IO;
|
||||
// @internal
|
||||
private _connectionKey?: string;
|
||||
|
||||
/**
|
||||
|
||||
@@ -266,9 +266,13 @@ class SupabaseDatabase<Database = any> {
|
||||
}
|
||||
|
||||
export class SupabaseManagement implements TriggerIntegration {
|
||||
// @internal
|
||||
private _options: SupabaseManagementIntegrationOptions;
|
||||
// @internal
|
||||
private _client?: SupabaseManagementAPI;
|
||||
// @internal
|
||||
private _io?: IO;
|
||||
// @internal
|
||||
private _connectionKey?: string;
|
||||
|
||||
constructor(private options: SupabaseManagementIntegrationOptions) {
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
},
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
"baseUrl": "."
|
||||
"baseUrl": ".",
|
||||
"stripInternal": true
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
@@ -3,11 +3,7 @@ import { GetFormParams, GetFormResponse, ListFormsParams, TypeformRunTask } from
|
||||
import { Typeform } from "@typeform/api-client";
|
||||
|
||||
export class Forms {
|
||||
runTask: TypeformRunTask;
|
||||
|
||||
constructor(runTask: TypeformRunTask) {
|
||||
this.runTask = runTask;
|
||||
}
|
||||
constructor(private runTask: TypeformRunTask) {}
|
||||
|
||||
list(key: IntegrationTaskKey, params: ListFormsParams): Promise<Typeform.API.Forms.List> {
|
||||
return this.runTask(
|
||||
|
||||
@@ -37,16 +37,16 @@ type TypeformTrigger = ReturnType<typeof createWebhookEventTrigger>;
|
||||
export type TypeformRunTask = InstanceType<typeof Typeform>["runTask"];
|
||||
|
||||
export class Typeform implements TriggerIntegration {
|
||||
// @internal
|
||||
private _options: TypeformIntegrationOptions;
|
||||
// @internal
|
||||
private _client?: TypeformSDK;
|
||||
// @internal
|
||||
private _io?: IO;
|
||||
// @internal
|
||||
private _connectionKey?: string;
|
||||
|
||||
constructor(private options: TypeformIntegrationOptions) {
|
||||
if (Object.keys(options).includes("token") && !options.token) {
|
||||
throw `Can't create Typeform integration (${options.id}) as token was undefined`;
|
||||
}
|
||||
|
||||
this._options = options;
|
||||
}
|
||||
|
||||
@@ -63,10 +63,18 @@ export class Typeform implements TriggerIntegration {
|
||||
}
|
||||
|
||||
cloneForRun(io: IO, connectionKey: string, auth?: ConnectionAuth) {
|
||||
const token = this._options.token ?? auth?.accessToken;
|
||||
|
||||
if (!token) {
|
||||
throw new Error(
|
||||
`Can't initialize Typeform integration (${this._options.id}) as token was undefined`
|
||||
);
|
||||
}
|
||||
|
||||
const typeform = new Typeform(this._options);
|
||||
typeform._io = io;
|
||||
typeform._connectionKey = connectionKey;
|
||||
typeform._client = createClient({ token: this._options.token });
|
||||
typeform._client = createClient({ token });
|
||||
return typeform;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Typeform, createClient } from "@typeform/api-client";
|
||||
|
||||
export type TypeformIntegrationOptions = {
|
||||
id: string;
|
||||
token: string;
|
||||
token?: string;
|
||||
apiBaseUrl?: string;
|
||||
};
|
||||
|
||||
|
||||
@@ -12,11 +12,7 @@ import {
|
||||
} from ".";
|
||||
|
||||
export class Webhooks {
|
||||
runTask: TypeformRunTask;
|
||||
|
||||
constructor(runTask: TypeformRunTask) {
|
||||
this.runTask = runTask;
|
||||
}
|
||||
constructor(private runTask: TypeformRunTask) {}
|
||||
|
||||
create(key: IntegrationTaskKey, params: CreateWebhookParams): Promise<GetWebhookResponse> {
|
||||
return this.runTask(
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
"resolveJsonModule": true,
|
||||
"lib": ["es2019"],
|
||||
"module": "commonjs",
|
||||
"target": "es2021"
|
||||
"target": "es2021",
|
||||
"stripInternal": true
|
||||
},
|
||||
"include": ["./src/**/*.ts", "tsup.config.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
|
||||
@@ -437,6 +437,13 @@ export const RunJobErrorSchema = z.object({
|
||||
|
||||
export type RunJobError = z.infer<typeof RunJobErrorSchema>;
|
||||
|
||||
export const RunJobUnresolvedAuthErrorSchema = z.object({
|
||||
status: z.literal("UNRESOLVED_AUTH_ERROR"),
|
||||
issues: z.record(z.object({ id: z.string(), error: z.string() })),
|
||||
});
|
||||
|
||||
export type RunJobUnresolvedAuthError = z.infer<typeof RunJobUnresolvedAuthErrorSchema>;
|
||||
|
||||
export const RunJobResumeWithTaskSchema = z.object({
|
||||
status: z.literal("RESUME_WITH_TASK"),
|
||||
task: TaskSchema,
|
||||
@@ -469,6 +476,7 @@ export type RunJobSuccess = z.infer<typeof RunJobSuccessSchema>;
|
||||
|
||||
export const RunJobResponseSchema = z.discriminatedUnion("status", [
|
||||
RunJobErrorSchema,
|
||||
RunJobUnresolvedAuthErrorSchema,
|
||||
RunJobResumeWithTaskSchema,
|
||||
RunJobRetryWithTaskSchema,
|
||||
RunJobCanceledWithTaskSchema,
|
||||
@@ -675,6 +683,7 @@ export type RegisterTriggerBodyV1 = z.infer<typeof RegisterTriggerBodySchemaV1>;
|
||||
export const RegisterTriggerBodySchemaV2 = z.object({
|
||||
rule: EventRuleSchema,
|
||||
source: SourceMetadataV2Schema,
|
||||
accountId: z.string().optional(),
|
||||
});
|
||||
|
||||
export type RegisterTriggerBodyV2 = z.infer<typeof RegisterTriggerBodySchemaV2>;
|
||||
@@ -693,7 +702,7 @@ const RegisterCommonScheduleBodySchema = z.object({
|
||||
id: z.string(),
|
||||
/** Any additional metadata about the schedule. */
|
||||
metadata: z.any(),
|
||||
/** This will be used by the Trigger.dev Connect feature, which is coming soon. */
|
||||
/** An optional Account ID to associate with runs triggered by this schedule */
|
||||
accountId: z.string().optional(),
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const ConnectionAuthSchema = z.object({
|
||||
type: z.enum(["oauth2"]),
|
||||
type: z.enum(["oauth2", "apiKey"]),
|
||||
accessToken: z.string(),
|
||||
scopes: z.array(z.string()).optional(),
|
||||
additionalFields: z.record(z.string()).optional(),
|
||||
@@ -20,7 +20,7 @@ export type IntegrationMetadata = z.infer<typeof IntegrationMetadataSchema>;
|
||||
export const IntegrationConfigSchema = z.object({
|
||||
id: z.string(),
|
||||
metadata: IntegrationMetadataSchema,
|
||||
authSource: z.enum(["HOSTED", "LOCAL"]),
|
||||
authSource: z.enum(["HOSTED", "LOCAL", "RESOLVER"]),
|
||||
});
|
||||
|
||||
export type IntegrationConfig = z.infer<typeof IntegrationConfigSchema>;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ZodObject, z } from "zod";
|
||||
import { z } from "zod";
|
||||
import { TaskStatusSchema } from "./tasks";
|
||||
import { JobRunStatusRecordSchema } from "./statuses";
|
||||
|
||||
@@ -13,6 +13,7 @@ export const RunStatusSchema = z.union([
|
||||
z.literal("TIMED_OUT"),
|
||||
z.literal("ABORTED"),
|
||||
z.literal("CANCELED"),
|
||||
z.literal("UNRESOLVED_AUTH"),
|
||||
]);
|
||||
|
||||
export const RunTaskSchema = z.object({
|
||||
|
||||
@@ -30,6 +30,8 @@ export type CronOptions = z.infer<typeof CronOptionsSchema>;
|
||||
export const CronMetadataSchema = z.object({
|
||||
type: z.literal("cron"),
|
||||
options: CronOptionsSchema,
|
||||
/** An optional Account ID to associate with runs triggered by this interval */
|
||||
accountId: z.string().optional(),
|
||||
metadata: z.any(),
|
||||
});
|
||||
|
||||
@@ -40,6 +42,8 @@ export const IntervalMetadataSchema = z.object({
|
||||
type: z.literal("interval"),
|
||||
/** An object containing options about the interval. */
|
||||
options: IntervalOptionsSchema,
|
||||
/** An optional Account ID to associate with runs triggered by this interval */
|
||||
accountId: z.string().optional(),
|
||||
/** Any additional metadata about the schedule. */
|
||||
metadata: z.any(),
|
||||
});
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
-- AlterEnum
|
||||
ALTER TYPE "IntegrationAuthSource" ADD VALUE 'RESOLVER';
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user