{payload.error ? (
{payload.error}
diff --git a/apps/webapp/app/routes/api.v1.accounts.$accountId.connections.$clientSlug.ts b/apps/webapp/app/routes/api.v1.accounts.$accountId.connections.$clientSlug.ts
index 5763ce8a37..e9f8d39b6f 100644
--- a/apps/webapp/app/routes/api.v1.accounts.$accountId.connections.$clientSlug.ts
+++ b/apps/webapp/app/routes/api.v1.accounts.$accountId.connections.$clientSlug.ts
@@ -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({
diff --git a/apps/webapp/app/services/events/ingestSendEvent.server.ts b/apps/webapp/app/services/events/ingestSendEvent.server.ts
index a4130bfdbc..f2c5e79335 100644
--- a/apps/webapp/app/services/events/ingestSendEvent.server.ts
+++ b/apps/webapp/app/services/events/ingestSendEvent.server.ts
@@ -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;
diff --git a/apps/webapp/app/services/jobs/registerJob.server.ts b/apps/webapp/app/services/jobs/registerJob.server.ts
index c66a6e1572..401c69c8aa 100644
--- a/apps/webapp/app/services/jobs/registerJob.server.ts
+++ b/apps/webapp/app/services/jobs/registerJob.server.ts
@@ -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 {
+ 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;
@@ -572,3 +643,7 @@ export class RegisterJobService {
});
}
}
+
+function assertExhaustive(x: never): never {
+ throw new Error("Unexpected object: " + x);
+}
diff --git a/apps/webapp/app/services/jobs/testJob.server.ts b/apps/webapp/app/services/jobs/testJob.server.ts
index 2b154cc0e6..ab95d10856 100644
--- a/apps/webapp/app/services/jobs/testJob.server.ts
+++ b/apps/webapp/app/services/jobs/testJob.server.ts
@@ -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(),
diff --git a/apps/webapp/app/services/runs/continueRun.server.ts b/apps/webapp/app/services/runs/continueRun.server.ts
index 00dad4ad22..644cc2e0e9 100644
--- a/apps/webapp/app/services/runs/continueRun.server.ts
+++ b/apps/webapp/app/services/runs/continueRun.server.ts
@@ -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;
diff --git a/apps/webapp/app/services/runs/performRunExecutionV1.server.ts b/apps/webapp/app/services/runs/performRunExecutionV1.server.ts
index c886427db9..016d71477f 100644
--- a/apps/webapp/app/services/runs/performRunExecutionV1.server.ts
+++ b/apps/webapp/app/services/runs/performRunExecutionV1.server.ts
@@ -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,
- status: "FAILURE" | "ABORTED" = "FAILURE"
+ status: "FAILURE" | "ABORTED" | "UNRESOLVED_AUTH" = "FAILURE"
): Promise {
const { run } = execution;
diff --git a/apps/webapp/app/services/runs/performRunExecutionV2.server.ts b/apps/webapp/app/services/runs/performRunExecutionV2.server.ts
index 763318153a..7d5f979c2c 100644
--- a/apps/webapp/app/services/runs/performRunExecutionV2.server.ts
+++ b/apps/webapp/app/services/runs/performRunExecutionV2.server.ts
@@ -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,
- status: "FAILURE" | "ABORTED" | "TIMED_OUT" = "FAILURE",
+ status: "FAILURE" | "ABORTED" | "TIMED_OUT" | "UNRESOLVED_AUTH" = "FAILURE",
durationInMs: number = 0
): Promise {
await $transaction(prisma, async (tx) => {
diff --git a/apps/webapp/app/services/runs/reRun.server.ts b/apps/webapp/app/services/runs/reRun.server.ts
index 8c1b8449ed..9a604c16b1 100644
--- a/apps/webapp/app/services/runs/reRun.server.ts
+++ b/apps/webapp/app/services/runs/reRun.server.ts
@@ -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(),
diff --git a/apps/webapp/app/services/runs/startRun.server.ts b/apps/webapp/app/services/runs/startRun.server.ts
index 818e310c54..0773eda6cb 100644
--- a/apps/webapp/app/services/runs/startRun.server.ts
+++ b/apps/webapp/app/services/runs/startRun.server.ts
@@ -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({
diff --git a/apps/webapp/app/services/schedules/registerSchedule.server.ts b/apps/webapp/app/services/schedules/registerSchedule.server.ts
index bda3daf894..950942f05b 100644
--- a/apps/webapp/app/services/schedules/registerSchedule.server.ts
+++ b/apps/webapp/app/services/schedules/registerSchedule.server.ts
@@ -59,6 +59,7 @@ export class RegisterScheduleService {
schedule: payload,
accountId: payload.accountId,
dynamicTrigger,
+ organizationId: environment.organizationId,
});
return registration;
diff --git a/apps/webapp/app/services/schedules/registerScheduleSource.server.ts b/apps/webapp/app/services/schedules/registerScheduleSource.server.ts
index f27267b855..1aa58fd82d 100644
--- a/apps/webapp/app/services/schedules/registerScheduleSource.server.ts
+++ b/apps/webapp/app/services/schedules/registerScheduleSource.server.ts
@@ -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;
diff --git a/apps/webapp/app/services/sources/registerSourceV1.server.ts b/apps/webapp/app/services/sources/registerSourceV1.server.ts
index 2be96c6d62..828d8a0c24 100644
--- a/apps/webapp/app/services/sources/registerSourceV1.server.ts
+++ b/apps/webapp/app/services/sources/registerSourceV1.server.ts
@@ -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;
diff --git a/apps/webapp/app/services/sources/registerSourceV2.server.ts b/apps/webapp/app/services/sources/registerSourceV2.server.ts
index 837793080d..4b6bea2527 100644
--- a/apps/webapp/app/services/sources/registerSourceV2.server.ts
+++ b/apps/webapp/app/services/sources/registerSourceV2.server.ts
@@ -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;
diff --git a/apps/webapp/app/services/triggers/registerTriggerSourceV2.server.ts b/apps/webapp/app/services/triggers/registerTriggerSourceV2.server.ts
index 861d4fb9ad..3118e58a1c 100644
--- a/apps/webapp/app/services/triggers/registerTriggerSourceV2.server.ts
+++ b/apps/webapp/app/services/triggers/registerTriggerSourceV2.server.ts
@@ -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 {
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 }
);
diff --git a/docs/_snippets/installs/slack.mdx b/docs/_snippets/installs/slack.mdx
new file mode 100644
index 0000000000..1c6a647af5
--- /dev/null
+++ b/docs/_snippets/installs/slack.mdx
@@ -0,0 +1,15 @@
+
+
+```bash npm
+npm install @trigger.dev/slack@latest
+```
+
+```bash pnpm
+pnpm install @trigger.dev/slack@latest
+```
+
+```bash yarn
+yarn add @trigger.dev/slack@latest
+```
+
+
diff --git a/docs/_snippets/jobs/options.mdx b/docs/_snippets/jobs/options.mdx
new file mode 100644
index 0000000000..d9fb0cdcb4
--- /dev/null
+++ b/docs/_snippets/jobs/options.mdx
@@ -0,0 +1,49 @@
+
+
+
+ The `id` property is used to uniquely identify the Job. Only change this if you want to create a new Job.
+
+
+ The `name` of the Job that you want to appear in the dashboard and logs. You can change this without creating a new Job.
+
+
+ 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`.
+
+
+ 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.
+
+
+ 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.
+
+
+ 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:
+
+
+
+ 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`.
+
+
+ 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
+
+
+
+
diff --git a/docs/documentation/concepts/triggers/dynamic.mdx b/docs/documentation/concepts/triggers/dynamic.mdx
index dda10dfede..5a12b2b5bc 100644
--- a/docs/documentation/concepts/triggers/dynamic.mdx
+++ b/docs/documentation/concepts/triggers/dynamic.mdx
@@ -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,
+ });
},
});
```
diff --git a/docs/documentation/guides/using-integrations-apikeys.mdx b/docs/documentation/guides/using-integrations-apikeys.mdx
index 330aee59b3..512dcc9e83 100644
--- a/docs/documentation/guides/using-integrations-apikeys.mdx
+++ b/docs/documentation/guides/using-integrations-apikeys.mdx
@@ -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
diff --git a/docs/documentation/guides/using-integrations-byo-auth.mdx b/docs/documentation/guides/using-integrations-byo-auth.mdx
new file mode 100644
index 0000000000..a46d94680b
--- /dev/null
+++ b/docs/documentation/guides/using-integrations-byo-auth.mdx
@@ -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)
+
+
+ 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.
+
+
+## 1. Install the Slack integration package
+
+
+
+## 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
+
+
+
+```bash npm
+npm install @clerk/backend@latest
+```
+
+```bash pnpm
+pnpm install @clerk/backend@latest
+```
+
+```bash yarn
+yarn add @clerk/backend@latest
+```
+
+
+
+## 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).
+
+
+ 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.
+
+
+## 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:
+
+
+
+
+ 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,
+});
+```
+
+
+
+## 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:
+
+
+
+
+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,
+});
+```
+
+
+
+
+
+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! 🥇",
+ });
+ },
+});
+```
+
+
+
+
+
+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,
+ };
+});
+```
+
+
+ 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`.
+
+
+
+
+
+
+ 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,
+ }
+ );
+}
+```
+
+
+
+
+# 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;
+
+type AuthResolverResult = {
+ type: "apiKey" | "oauth";
+ token: string;
+ additionalFields?: Record;
+};
+```
+
+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",
+ },
+ };
+});
+```
diff --git a/docs/documentation/guides/using-integrations.mdx b/docs/documentation/guides/using-integrations.mdx
index b8af49a61f..b2a3dc8bf5 100644
--- a/docs/documentation/guides/using-integrations.mdx
+++ b/docs/documentation/guides/using-integrations.mdx
@@ -1,12 +1,12 @@
---
-title: "Using Integrations"
-description: "How to use Integrations"
+title: "Integrations Overview"
+description: "How to use Trigger.dev Integrations"
+sidebarTitle: "Overview"
---
- 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.
[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
+
+ Use our integrations with your user’s auth credentials, using Clerk.com, Nango.dev, or rolling
+ your own with our custom auth resolvers
+
## 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.
- 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.
diff --git a/docs/images/byo-auth/dynamic-schedule-task.png b/docs/images/byo-auth/dynamic-schedule-task.png
new file mode 100644
index 0000000000..6fbbffdce2
Binary files /dev/null and b/docs/images/byo-auth/dynamic-schedule-task.png differ
diff --git a/docs/images/byo-auth/run-dashboard-account-id.png b/docs/images/byo-auth/run-dashboard-account-id.png
new file mode 100644
index 0000000000..831fa882b2
Binary files /dev/null and b/docs/images/byo-auth/run-dashboard-account-id.png differ
diff --git a/docs/images/byo-auth/run-test-account-id.png b/docs/images/byo-auth/run-test-account-id.png
new file mode 100644
index 0000000000..8120e90746
Binary files /dev/null and b/docs/images/byo-auth/run-test-account-id.png differ
diff --git a/docs/mint.json b/docs/mint.json
index 81c5dfb171..6c041b32bc 100644
--- a/docs/mint.json
+++ b/docs/mint.json
@@ -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"
}
}
-}
+}
\ No newline at end of file
diff --git a/docs/sdk/dynamicschedule/overview.mdx b/docs/sdk/dynamicschedule/overview.mdx
index 4525aac74c..f6faaa0a80 100644
--- a/docs/sdk/dynamicschedule/overview.mdx
+++ b/docs/sdk/dynamicschedule/overview.mdx
@@ -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);
},
});
```
diff --git a/docs/sdk/dynamicschedule/register.mdx b/docs/sdk/dynamicschedule/register.mdx
index 71b9cd76c5..8b15bcc164 100644
--- a/docs/sdk/dynamicschedule/register.mdx
+++ b/docs/sdk/dynamicschedule/register.mdx
@@ -7,8 +7,8 @@ description: "Use this method to register a new schedule with the DynamicSchedul
## Parameters
- 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.
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
- 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.
+
+
+ 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)
@@ -40,8 +45,13 @@ description: "Use this method to register a new schedule with the DynamicSchedul
- 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.
+
+
+ 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)
diff --git a/docs/sdk/dynamictrigger/constructor.mdx b/docs/sdk/dynamictrigger/constructor.mdx
index 477d264e10..aea8078d00 100644
--- a/docs/sdk/dynamictrigger/constructor.mdx
+++ b/docs/sdk/dynamictrigger/constructor.mdx
@@ -16,9 +16,8 @@ description: "The `DynamicTrigger()` constructor creates a new [DynamicTrigger](
Used to uniquely identify a DynamicTrigger
- 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.
An external source fron an [Integration](/integrations) package
diff --git a/docs/sdk/dynamictrigger/overview.mdx b/docs/sdk/dynamictrigger/overview.mdx
index c6626d90ac..b78330cf3b 100644
--- a/docs/sdk/dynamictrigger/overview.mdx
+++ b/docs/sdk/dynamictrigger/overview.mdx
@@ -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,
+ });
},
});
```
diff --git a/docs/sdk/dynamictrigger/register.mdx b/docs/sdk/dynamictrigger/register.mdx
index 18136e7a71..95e2049c8c 100644
--- a/docs/sdk/dynamictrigger/register.mdx
+++ b/docs/sdk/dynamictrigger/register.mdx
@@ -7,12 +7,25 @@ description: "Use this method to register a new configuration with the DynamicTr
## Parameters
- 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.
- 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`.
+
+
+
+
+ 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)
+
+
+ An optional filter to apply to the event. See our [EventFilter
+ guide](/documentation/guides/event-filter) for more
+
+
## Returns
diff --git a/docs/sdk/io/registercron.mdx b/docs/sdk/io/registercron.mdx
index ab9bc9708a..769acc0913 100644
--- a/docs/sdk/io/registercron.mdx
+++ b/docs/sdk/io/registercron.mdx
@@ -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."
---
+
+ This has been deprecated in favor of [DynamicSchedule.register](/sdk/dynamicschedule/register)
+
+
## Parameters
diff --git a/docs/sdk/io/registerinterval.mdx b/docs/sdk/io/registerinterval.mdx
index 48ef44bbf3..7704267ec7 100644
--- a/docs/sdk/io/registerinterval.mdx
+++ b/docs/sdk/io/registerinterval.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."
---
+
+ This has been deprecated in favor of [DynamicSchedule.register](/sdk/dynamicschedule/register)
+
+
## Parameters
diff --git a/docs/sdk/io/registertrigger.mdx b/docs/sdk/io/registertrigger.mdx
index e3bf1fb029..6562ef8cef 100644
--- a/docs/sdk/io/registertrigger.mdx
+++ b/docs/sdk/io/registertrigger.mdx
@@ -4,6 +4,10 @@ sidebarTitle: "registerTrigger()"
description: "`io.registerTrigger()` allows you to register a [DynamicTrigger](/sdk/dynamictrigger) with the specified trigger data."
---
+
+ This has been deprecated in favor of [DynamicTrigger.register](/sdk/dynamictrigger/register)
+
+
## Parameters
@@ -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,
diff --git a/docs/sdk/io/unregistercron.mdx b/docs/sdk/io/unregistercron.mdx
index 7f750efef2..9b43e0e166 100644
--- a/docs/sdk/io/unregistercron.mdx
+++ b/docs/sdk/io/unregistercron.mdx
@@ -4,6 +4,10 @@ sidebarTitle: "unregisterCron()"
description: "`io.unregisterCron()` allows you to unregister a [DynamicSchedule](/sdk/dynamicschedule) that was previously registered with `io.registerCron()`."
---
+
+ This has been deprecated in favor of [DynamicSchedule.unregister](/sdk/dynamicschedule/unregister)
+
+
## Parameters
diff --git a/docs/sdk/io/unregisterinterval.mdx b/docs/sdk/io/unregisterinterval.mdx
index 86d0ff9bd7..7b5fcf3d25 100644
--- a/docs/sdk/io/unregisterinterval.mdx
+++ b/docs/sdk/io/unregisterinterval.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()`."
---
+
+ This has been deprecated in favor of [DynamicSchedule.unregister](/sdk/dynamicschedule/unregister)
+
+
## Parameters
diff --git a/docs/sdk/io/unregistertrigger.mdx b/docs/sdk/io/unregistertrigger.mdx
index ca47e9541d..3fa9fa2c2f 100644
--- a/docs/sdk/io/unregistertrigger.mdx
+++ b/docs/sdk/io/unregistertrigger.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()`."
---
+
+ This has been deprecated in favor of [DynamicTrigger.unregister](/sdk/dynamictrigger/unregister)
+
+
## Parameters
diff --git a/docs/sdk/job.mdx b/docs/sdk/job.mdx
index a9bc27f818..229bbf13c5 100644
--- a/docs/sdk/job.mdx
+++ b/docs/sdk/job.mdx
@@ -59,55 +59,7 @@ client.defineJob({
An instance of [TriggerClient](/sdk/triggerclient) that is used to send events to the Trigger API.
-
-
-
- The `id` property is used to uniquely identify the Job. Only change this if you want to create a new Job.
-
-
- The `name` of the Job that you want to appear in the dashboard and logs. You can change this without creating a new Job.
-
-
- 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`.
-
-
- 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.
-
-
- 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.
-
-
- 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:
-
-
-
- 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`.
-
-
- 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
-
-
-
-
+
## Returns
diff --git a/docs/sdk/triggerclient/instancemethods/define-auth-resolver.mdx b/docs/sdk/triggerclient/instancemethods/define-auth-resolver.mdx
new file mode 100644
index 0000000000..b6f34a577b
--- /dev/null
+++ b/docs/sdk/triggerclient/instancemethods/define-auth-resolver.mdx
@@ -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.
+
+
+
+```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,
+ };
+});
+```
+
+
+
+## Parameters
+
+
+ The Integration client (e.g. `slack`) to define the auth resolver for.
+
+
+
+ The resolver function to use for this integration. Should return a [AuthResolverResult](#authresolverresult) object.
+
+{" "}
+
+
+
+ The [TriggerContext](/sdk/context) object for the run that is requesting authentication.
+
+
+ The Integration client that is requesting authentication.
+
+
+
+
+
+## AuthResolverResult
+
+
+ Should be either "apiKey" or "oauth"
+
+
+
+ The authentication token to use for this integration.
+
+
+
+ Additional fields to pass to the integration.
+
diff --git a/docs/sdk/triggerclient/instancemethods/define-dynamic-schedule.mdx b/docs/sdk/triggerclient/instancemethods/define-dynamic-schedule.mdx
new file mode 100644
index 0000000000..3509ce38e3
--- /dev/null
+++ b/docs/sdk/triggerclient/instancemethods/define-dynamic-schedule.mdx
@@ -0,0 +1,29 @@
+---
+title: "defineDynamicSchedule()"
+description: "Define a Dynamic Schedule"
+---
+
+## Parameters
+
+
+ The options for the dynamic schedule.
+
+
+ Used to uniquely identify a DynamicSchedule
+
+
+
+
+## Returns
+
+
+
+
+
+```ts example
+const dynamicSchedule = client.defineDynamicSchedule({
+ id: "dynamicinterval",
+});
+```
+
+
diff --git a/docs/sdk/triggerclient/instancemethods/define-dynamic-trigger.mdx b/docs/sdk/triggerclient/instancemethods/define-dynamic-trigger.mdx
new file mode 100644
index 0000000000..42dd6b850c
--- /dev/null
+++ b/docs/sdk/triggerclient/instancemethods/define-dynamic-trigger.mdx
@@ -0,0 +1,38 @@
+---
+title: "defineDynamicTrigger()"
+description: "Define a Dynamic Trigger"
+---
+
+## Parameters
+
+
+ The options for the dynamic trigger.
+
+
+ Used to uniquely identify a DynamicTrigger
+
+
+ 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 external source fron an [Integration](/integrations) package
+
+
+
+
+## Returns
+
+
+
+
+
+```ts example
+const dynamicOnIssueOpenedTrigger = client.defineDynamicTrigger({
+ id: "github-issue-opened",
+ event: events.onIssueOpened,
+ source: github.sources.repo,
+});
+```
+
+
diff --git a/docs/sdk/triggerclient/instancemethods/define-job.mdx b/docs/sdk/triggerclient/instancemethods/define-job.mdx
new file mode 100644
index 0000000000..2b9732e1b0
--- /dev/null
+++ b/docs/sdk/triggerclient/instancemethods/define-job.mdx
@@ -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.
+
+
+
+```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 };
+ },
+});
+```
+
+
+
+## Parameters
+
+
+
+## Returns
+
+
diff --git a/docs/sdk/triggerclient/instancemethods/sendevent.mdx b/docs/sdk/triggerclient/instancemethods/sendevent.mdx
index ba2c393b70..05c0b4bf88 100644
--- a/docs/sdk/triggerclient/instancemethods/sendevent.mdx
+++ b/docs/sdk/triggerclient/instancemethods/sendevent.mdx
@@ -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.
diff --git a/docs/sdk/triggerclient/overview.mdx b/docs/sdk/triggerclient/overview.mdx
index c9484812aa..f7583caeb0 100644
--- a/docs/sdk/triggerclient/overview.mdx
+++ b/docs/sdk/triggerclient/overview.mdx
@@ -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.
diff --git a/integrations/airtable/src/base.ts b/integrations/airtable/src/base.ts
index 821a56fced..b354bc136c 100644
--- a/integrations/airtable/src/base.ts
+++ b/integrations/airtable/src/base.ts
@@ -11,13 +11,10 @@ export type AirtableRecordsParams = TableParams<{}>;
export type AirtableRecords = Records