Files
triggerdotdev--trigger.dev/docs/integrations/create-tasks.mdx
T
nicktrn 6a992a1995 Replicate integration and remote callbacks (#507)
* Support tasks with remote callbacks

* Add common integration tsconfig

* Add Replicate integration

* Basic job catalog example

* Integration catalog entry

* Check for callbackUrl during executeTask

* Fix getAll

* Improve JSDoc

* Bump version

* Remove named queue

* Simplify runTask types

* Trust the types

* Fail tasks on timeout

* Callback timeout as param

* Mess with types

* performRunExecutionV1

* Update runTask docs

* Shorten callback task methods

* Fix run method return type

* Image processing jobs

* Replicate docs

* Text output example

* Changeset

* Version bump

* Roll back ugly types

* Remove missing types

* Quicker return when waiting on remote callback

* Remote callback example

* Bump version

* Remove schema parsing

* Only schedule positive callback timeout

* Decrease callback secret length

* Explicit default timeouts

* Import deployments tasks

* JSDoc

* Deployments docs

* Fix runTask examples, mention wrappers

---------

Co-authored-by: Eric Allam <eric@trigger.dev>
2023-10-04 10:46:17 +01:00

219 lines
6.5 KiB
Plaintext

---
title: Tasks
description: "Tasks are the main way that developers will interact with your Integration. They are the actions that developers will be able to perform in their jobs."
---
## runTask
You should add a `runTask()` function to your integration, before adding individual Tasks. It will be used by all Tasks, and allows users to use any function in the official SDK as a Task.
<CodeGroup>
```ts integrations/github/index.ts
export class Github implements TriggerIntegration {
...
runTask<T, TResult extends Json<T> | void>(
key: IntegrationTaskKey,
//change Octokit to the official SDK for your integration
callback: (client: Octokit, task: IOTask, io: IO) => Promise<TResult>,
options?: RunTaskOptions,
errorCallback?: RunTaskErrorCallback
): Promise<TResult> {
if (!this._io) throw new Error("No IO");
if (!this._connectionKey) throw new Error("No connection key");
return this._io.runTask(
key,
(task, io) => {
if (!this._client) throw new Error("No client");
return callback(this._client, task, io);
},
{
//change this to the slug for your integration (lowercase, no spaces)
icon: "github",
retry: retry.standardBackoff,
...(options ?? {}),
connectionKey: this._connectionKey,
},
errorCallback
);
}
...
}
```
```ts Usage
//Users can then use it in their Jobs like this:
client.defineJob({
...
integrations: {
github,
},
run: async (payload, io, ctx) => {
//io.github.runTask allows you to use the underlying SDK client
const { data } = await io.github.runTask(
"create-card",
async (client) => {
//this is octokit, authenticated for the user
return client.rest.projects.createCard({
column_id: 123,
note: "test",
});
},
//this is optional, displays in the dashboard when viewing a Run
{ name: "Create card" }
);
},
});
```
</CodeGroup>
## Matching the original SDK structure
The structure of the integrtion should closely map to the official Node SDK for that API.
| API | Official SDK | Trigger.Dev Task |
| ---------------- | --------------------------- | ------------------------------ |
| GitHub (octokit) | `client.rest.issues.create` | `io.github.issues.create` |
| Typeform | `client.forms.get` | `io.typeform.forms.get` |
| OpenAI | `client.completions.create` | `io.openai.completions.create` |
Closely resembling the official SDK makes it far easier for developers to use integrations. Aim for 1:1 mapping wherever possible.
## Adding Tasks
Any tasks you add will use the `runTask` function you added above.
Remember we want to structure our integration to match the original SDK. If an SDK has a `client.forms.get` function, we want to add a `myintegration.forms.get` task.
This requires adding a bit of structure to the SDK, to add the Forms object, the get function and use `runTask` inside them.
### Create the structure
In this example, we'll add `io.github.issues.create`. This will allow users to create a GitHub issue in their jobs.
Comments inline are for instructional purposes only, and should not be included in your final code.
```ts integration/github/issues.ts
import { IntegrationTaskKey } from "@trigger.dev/sdk";
import { GitHubReturnType, GitHubRunTask, onError } from "./index";
import { Octokit } from "octokit";
export class Issues {
//the runTask you created in the other file, we'll use this for all Tasks
runTask: GitHubRunTask;
constructor(runTask: GitHubRunTask) {
this.runTask = runTask;
}
create(
//this must be the first parameter, it's used to identify the task
key: IntegrationTaskKey,
//if possible use the official SDK type. Here we had to define our own.
params: { title: string; owner: string; repo: string }
//you must define the return type (it will be a promise, in this case grabbed from the official SDK)
): GitHubReturnType<Octokit["rest"]["issues"]["create"]> {
//use runTask
return this.runTask(
key,
async (client, task) => {
//the official SDK is used here
const result = await client.rest.issues.create({
owner: params.owner,
repo: params.repo,
title: params.title,
});
return result.data;
},
//all of these properties are displayed in the Trigger.dev dashboard
{
name: "Create Issue",
params,
//properties provide a great experience for developers debugging a Run
properties: [
{
label: "Owner",
text: params.owner,
},
{
label: "Repo",
text: params.repo,
},
{
label: "Title",
text: params.title,
},
],
},
//you can define a custom error function, or omit this.
onError
);
}
}
```
<Note>
Notice how the params are the _second_ argument, that's because the first argument is always the
task key. See our [Keys and Resumability docs](/documentation/concepts/resumability) for more on
why this is important.
</Note>
```ts integrations/github/index.ts
//This type is used in the Issues class above
export type GitHubRunTask = InstanceType<typeof Github>["runTask"];
export class Github implements TriggerIntegration {
...
//this allows us to do `io.github.issues.create()`
get issues() {
//the bind is needed to preserve the `this` context
return new Issues(this.runTask.bind(this));
}
...
}
```
### `onError` function
You can optionally have logic in the `onError` param. See [the reference for runTask](/sdk/io/runtask) for more info.
The GitHub integration uses this to retry rate-limited requests when the rate limit resets:
```ts integrations/github/index.ts
...
function isRequestError(error: unknown): error is RequestError {
return typeof error === "object" && error !== null && "status" in error;
}
export function onError(error: unknown) {
if (!isRequestError(error)) {
return;
}
// Check if this is a rate limit error
if (error.status === 403 && error.response) {
const rateLimitRemaining = error.response.headers["x-ratelimit-remaining"];
const rateLimitReset = error.response.headers["x-ratelimit-reset"];
if (rateLimitRemaining === "0" && rateLimitReset) {
const resetDate = new Date(Number(rateLimitReset) * 1000);
return {
retryAt: resetDate,
error,
};
}
}
}
```