feat: Add support for the new Resend audience API endpoints (#850)
* feat: Add support for the new Resend audience API endpoints * update pnpm-lock.yaml * update pnpm-lock.yaml * Merge branch 'main' into feat/support-for-resend-audience-api * latest pnpm-lock.yaml * Create lovely-items-end.md * Update pnpm-lock.yaml --------- Co-authored-by: Matt Aitken <matt@mattaitken.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@trigger.dev/resend": patch
|
||||
---
|
||||
|
||||
Add support for the new Resend audience API endpoints
|
||||
@@ -26,13 +26,22 @@ Send an email to a recipient with a text payload. [Official Resend Docs](https:/
|
||||
|
||||
## Tasks
|
||||
|
||||
| Function Name | Description |
|
||||
| --------------- | -------------------------------- |
|
||||
| `emails.send` | Send an email |
|
||||
| `emails.create` | Create an email |
|
||||
| `emails.get` | Get an email |
|
||||
| `batch.send` | Send a batch of emails at once |
|
||||
| `batch.create` | Create a batch of emails at once |
|
||||
| Function Name | Description |
|
||||
| ------------------ | -------------------------------- |
|
||||
| `emails.send` | Send an email |
|
||||
| `emails.create` | Create an email |
|
||||
| `emails.get` | Get an email |
|
||||
| `batch.send` | Send a batch of emails at once |
|
||||
| `batch.create` | Create a batch of emails at once |
|
||||
| `audiences.create` | Create an audience |
|
||||
| `audiences.get` | Get an audience |
|
||||
| `audiences.remove` | Remove an audience |
|
||||
| `audiences.list` | List audiences |
|
||||
| `contacts.create` | Create a contact |
|
||||
| `contacts.get` | Get a contact |
|
||||
| `contacts.update` | Update a contact |
|
||||
| `contacts.remove` | Remove a contact |
|
||||
| `contacts.list` | List contacts |
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"dependencies": {
|
||||
"@trigger.dev/integration-kit": "workspace:^2.3.13",
|
||||
"@trigger.dev/sdk": "workspace:^2.3.13",
|
||||
"resend": "^2.0.0"
|
||||
"resend": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
import { IntegrationTaskKey, retry } from "@trigger.dev/sdk";
|
||||
import type { ResendRunTask } from "./index";
|
||||
import { Resend } from "resend";
|
||||
import { handleResendError } from "./utils";
|
||||
|
||||
type CreateAudienceResult = NonNullable<Awaited<ReturnType<Resend["audiences"]["create"]>>["data"]>;
|
||||
type GetAudienceResult = NonNullable<Awaited<ReturnType<Resend["audiences"]["get"]>>["data"]>;
|
||||
type DeleteAudienceResult = NonNullable<Awaited<ReturnType<Resend["audiences"]["remove"]>>["data"]>;
|
||||
type ListAudiencesResult = NonNullable<Awaited<ReturnType<Resend["audiences"]["list"]>>["data"]>;
|
||||
|
||||
export class Audiences {
|
||||
constructor(private runTask: ResendRunTask) {}
|
||||
|
||||
create(
|
||||
key: IntegrationTaskKey,
|
||||
payload: Parameters<Resend["audiences"]["create"]>[0],
|
||||
options?: Parameters<Resend["audiences"]["create"]>[1]
|
||||
): Promise<CreateAudienceResult> {
|
||||
return this.runTask(
|
||||
key,
|
||||
async (client, task) => {
|
||||
const { error, data } = await client.audiences.create(payload, options);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
throw new Error("No data returned from Resend");
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
{
|
||||
name: "Create Audience",
|
||||
params: payload,
|
||||
properties: [
|
||||
{
|
||||
label: "Name",
|
||||
text: payload.name,
|
||||
},
|
||||
],
|
||||
retry: retry.standardBackoff,
|
||||
},
|
||||
handleResendError
|
||||
);
|
||||
}
|
||||
|
||||
get(key: IntegrationTaskKey, payload: string): Promise<GetAudienceResult> {
|
||||
return this.runTask(
|
||||
key,
|
||||
async (client, task) => {
|
||||
const { error, data } = await client.audiences.get(payload);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
throw new Error("No data returned from Resend");
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
{
|
||||
name: "Get Audience",
|
||||
params: payload,
|
||||
properties: [
|
||||
{
|
||||
label: "ID",
|
||||
text: payload,
|
||||
},
|
||||
],
|
||||
retry: retry.standardBackoff,
|
||||
},
|
||||
handleResendError
|
||||
);
|
||||
}
|
||||
|
||||
remove(key: IntegrationTaskKey, payload: string): Promise<DeleteAudienceResult> {
|
||||
return this.runTask(
|
||||
key,
|
||||
async (client, task) => {
|
||||
const { error, data } = await client.audiences.remove(payload);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
throw new Error("No data returned from Resend");
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
{
|
||||
name: "Remove Audience",
|
||||
params: payload,
|
||||
properties: [
|
||||
{
|
||||
label: "ID",
|
||||
text: payload,
|
||||
},
|
||||
],
|
||||
retry: retry.standardBackoff,
|
||||
},
|
||||
handleResendError
|
||||
);
|
||||
}
|
||||
|
||||
list(key: IntegrationTaskKey): Promise<ListAudiencesResult> {
|
||||
return this.runTask(
|
||||
key,
|
||||
async (client, task) => {
|
||||
const { error, data } = await client.audiences.list();
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
throw new Error("No data returned from Resend");
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
{
|
||||
name: "List Audiences",
|
||||
retry: retry.standardBackoff,
|
||||
},
|
||||
handleResendError
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
import { IntegrationTaskKey, retry } from "@trigger.dev/sdk";
|
||||
import type { ResendRunTask } from "./index";
|
||||
import { Resend } from "resend";
|
||||
import { handleResendError } from "./utils";
|
||||
|
||||
type CreateContactResult = NonNullable<Awaited<ReturnType<Resend["contacts"]["create"]>>["data"]>;
|
||||
type GetContactResult = NonNullable<Awaited<ReturnType<Resend["contacts"]["get"]>>["data"]>;
|
||||
type UpdateContactResult = NonNullable<Awaited<ReturnType<Resend["contacts"]["update"]>>["data"]>;
|
||||
type DeleteContactResult = NonNullable<Awaited<ReturnType<Resend["contacts"]["remove"]>>["data"]>;
|
||||
type ListContactsResult = NonNullable<Awaited<ReturnType<Resend["contacts"]["list"]>>["data"]>;
|
||||
|
||||
export class Contacts {
|
||||
constructor(private runTask: ResendRunTask) {}
|
||||
|
||||
create(
|
||||
key: IntegrationTaskKey,
|
||||
payload: Parameters<Resend["contacts"]["create"]>[0],
|
||||
options?: Parameters<Resend["contacts"]["create"]>[1]
|
||||
): Promise<CreateContactResult> {
|
||||
return this.runTask(
|
||||
key,
|
||||
async (client, task) => {
|
||||
const { error, data } = await client.contacts.create(payload, options);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
throw new Error("No data returned from Resend");
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
{
|
||||
name: "Create Contact",
|
||||
params: payload,
|
||||
properties: [
|
||||
{
|
||||
label: "Email",
|
||||
text: payload.email,
|
||||
},
|
||||
...(payload.first_name && payload.last_name
|
||||
? [{ label: "Name", text: payload.first_name + " " + payload.last_name }]
|
||||
: []),
|
||||
],
|
||||
retry: retry.standardBackoff,
|
||||
},
|
||||
handleResendError
|
||||
);
|
||||
}
|
||||
|
||||
get(
|
||||
key: IntegrationTaskKey,
|
||||
payload: Parameters<Resend["contacts"]["get"]>[0]
|
||||
): Promise<GetContactResult> {
|
||||
return this.runTask(
|
||||
key,
|
||||
async (client, task) => {
|
||||
const { error, data } = await client.contacts.get(payload);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
throw new Error("No data returned from Resend");
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
{
|
||||
name: "Get Contact",
|
||||
params: payload,
|
||||
properties: [
|
||||
{
|
||||
label: "Id",
|
||||
text: payload.id,
|
||||
},
|
||||
],
|
||||
retry: retry.standardBackoff,
|
||||
},
|
||||
handleResendError
|
||||
);
|
||||
}
|
||||
|
||||
update(
|
||||
key: IntegrationTaskKey,
|
||||
payload: Parameters<Resend["contacts"]["update"]>[0]
|
||||
): Promise<UpdateContactResult> {
|
||||
return this.runTask(
|
||||
key,
|
||||
async (client, task) => {
|
||||
const { error, data } = await client.contacts.update(payload);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
throw new Error("No data returned from Resend");
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
{
|
||||
name: "Update Contact",
|
||||
params: payload,
|
||||
properties: [
|
||||
{
|
||||
label: "Id",
|
||||
text: payload.id,
|
||||
},
|
||||
],
|
||||
retry: retry.standardBackoff,
|
||||
},
|
||||
handleResendError
|
||||
);
|
||||
}
|
||||
|
||||
remove(
|
||||
key: IntegrationTaskKey,
|
||||
payload: Parameters<Resend["contacts"]["remove"]>[0]
|
||||
): Promise<DeleteContactResult> {
|
||||
return this.runTask(
|
||||
key,
|
||||
async (client, task) => {
|
||||
const { error, data } = await client.contacts.remove(payload);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
throw new Error("No data returned from Resend");
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
{
|
||||
name: "Remove Contact",
|
||||
params: payload,
|
||||
properties: [
|
||||
{
|
||||
label: "Id",
|
||||
text: payload.id,
|
||||
},
|
||||
],
|
||||
retry: retry.standardBackoff,
|
||||
},
|
||||
handleResendError
|
||||
);
|
||||
}
|
||||
|
||||
list(
|
||||
key: IntegrationTaskKey,
|
||||
payload: Parameters<Resend["contacts"]["list"]>[0]
|
||||
): Promise<ListContactsResult> {
|
||||
return this.runTask(
|
||||
key,
|
||||
async (client, task) => {
|
||||
const { error, data } = await client.contacts.list(payload);
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
throw new Error("No data returned from Resend");
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
{
|
||||
name: "List Contacts",
|
||||
params: payload,
|
||||
properties: [
|
||||
{
|
||||
label: "Audience Id",
|
||||
text: payload.audience_id,
|
||||
},
|
||||
],
|
||||
retry: retry.standardBackoff,
|
||||
},
|
||||
handleResendError
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ import {
|
||||
import { Resend as ResendClient } from "resend";
|
||||
import { Emails } from "./emails";
|
||||
import { Batch } from "./batch";
|
||||
import { Contacts } from "./contacts";
|
||||
import { Audiences } from "./audiences";
|
||||
|
||||
type ErrorResponse = {
|
||||
statusCode: number;
|
||||
@@ -157,4 +159,36 @@ export class Resend implements TriggerIntegration {
|
||||
async sendEmail(...args: Parameters<typeof this.emails.send>) {
|
||||
return this.emails.send(...args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the Resend Audiences API
|
||||
* @example
|
||||
* ```ts
|
||||
* const response = await io.resend.audiences.create("📧", {
|
||||
* name: payload.name,
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
|
||||
get audiences() {
|
||||
return new Audiences(this.runTask.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the Resend Contacts API
|
||||
* @example
|
||||
* ```ts
|
||||
* const response = await io.resend.contacts.create("📧", {
|
||||
* email: payload.email,
|
||||
* first_name: payload.first_name,
|
||||
* last_name: payload.last_name,
|
||||
* unsubscribed: false
|
||||
* audienceId: payload.audienceId
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
|
||||
get contacts() {
|
||||
return new Contacts(this.runTask.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+5
-14
@@ -625,14 +625,14 @@ importers:
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@types/node': '18'
|
||||
resend: ^2.0.0
|
||||
resend: ^2.1.0
|
||||
rimraf: ^3.0.2
|
||||
tsup: 8.0.1
|
||||
typescript: ^5.3.0
|
||||
dependencies:
|
||||
'@trigger.dev/integration-kit': link:../../packages/integration-kit
|
||||
'@trigger.dev/sdk': link:../../packages/trigger-sdk
|
||||
resend: 2.0.0
|
||||
resend: 2.1.0
|
||||
devDependencies:
|
||||
'@trigger.dev/tsconfig': link:../../config-packages/tsconfig
|
||||
'@trigger.dev/tsup': link:../../config-packages/tsup
|
||||
@@ -23364,7 +23364,7 @@ packages:
|
||||
'@selderee/plugin-htmlparser2': 0.10.0
|
||||
deepmerge: 4.3.1
|
||||
dom-serializer: 2.0.0
|
||||
htmlparser2: 8.0.1
|
||||
htmlparser2: 8.0.2
|
||||
selderee: 0.10.0
|
||||
dev: false
|
||||
|
||||
@@ -23405,15 +23405,6 @@ packages:
|
||||
entities: 2.2.0
|
||||
dev: true
|
||||
|
||||
/htmlparser2/8.0.1:
|
||||
resolution: {integrity: sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==}
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
domhandler: 5.0.3
|
||||
domutils: 3.0.1
|
||||
entities: 4.4.0
|
||||
dev: false
|
||||
|
||||
/htmlparser2/8.0.2:
|
||||
resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
|
||||
dependencies:
|
||||
@@ -30388,8 +30379,8 @@ packages:
|
||||
- debug
|
||||
dev: false
|
||||
|
||||
/resend/2.0.0:
|
||||
resolution: {integrity: sha512-jAh0DN84ZjjmzGM2vMjJ1hphPBg1mG98dzopF7kJzmin62v8ESg4og2iCKWdkAboGOT2SeO5exbr/8Xh8gLddw==}
|
||||
/resend/2.1.0:
|
||||
resolution: {integrity: sha512-s6LlaEReTUvlbo6w3Eg1M1TMuwK9OKJ1GVgyptIV8smLPHhFZVqnwBTFPZHID9rcsih72t3iuyrtkQ3IIGwnow==}
|
||||
engines: {node: '>=18'}
|
||||
dependencies:
|
||||
'@react-email/render': 0.0.9
|
||||
|
||||
@@ -132,4 +132,214 @@ client.defineJob({
|
||||
},
|
||||
});
|
||||
|
||||
client.defineJob({
|
||||
id: "create-resend-audience",
|
||||
name: "Create Resend Audience",
|
||||
version: "0.1.0",
|
||||
trigger: eventTrigger({
|
||||
name: "create.audience",
|
||||
schema: z.object({
|
||||
name: z.string(),
|
||||
}),
|
||||
}),
|
||||
integrations: {
|
||||
resend,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
const response = await io.resend.audiences.create("📧", {
|
||||
name: payload.name,
|
||||
});
|
||||
|
||||
await io.logger.info("Created audience", { response });
|
||||
},
|
||||
});
|
||||
|
||||
client.defineJob({
|
||||
id: "get-resend-audience",
|
||||
name: "Get Resend Audience",
|
||||
version: "0.1.0",
|
||||
trigger: eventTrigger({
|
||||
name: "get.audience",
|
||||
schema: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
}),
|
||||
integrations: {
|
||||
resend,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
const response = await io.resend.audiences.get("📧", payload.id);
|
||||
|
||||
await io.logger.info("Got audience", { response });
|
||||
},
|
||||
});
|
||||
|
||||
client.defineJob({
|
||||
id: "remove-resend-audience",
|
||||
name: "Remove Resend Audience",
|
||||
version: "0.1.0",
|
||||
trigger: eventTrigger({
|
||||
name: "remove.audience",
|
||||
schema: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
}),
|
||||
integrations: {
|
||||
resend,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
const response = await io.resend.audiences.remove("📧", payload.id);
|
||||
|
||||
await io.logger.info("Removed audience", { response });
|
||||
},
|
||||
});
|
||||
|
||||
client.defineJob({
|
||||
id: "list-resend-audiences",
|
||||
name: "List Resend Audiences",
|
||||
version: "0.1.0",
|
||||
trigger: eventTrigger({
|
||||
name: "list.audiences",
|
||||
}),
|
||||
integrations: {
|
||||
resend,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
const response = await io.resend.audiences.list("📧");
|
||||
|
||||
await io.logger.info("Listed audiences", { response });
|
||||
},
|
||||
});
|
||||
|
||||
client.defineJob({
|
||||
id: "create resend contact",
|
||||
name: "Create Resend Contact",
|
||||
version: "0.1.0",
|
||||
trigger: eventTrigger({
|
||||
name: "create.contact",
|
||||
schema: z.object({
|
||||
audience_id: z.string(),
|
||||
email: z.string(),
|
||||
unsubscribed: z.boolean().optional(),
|
||||
first_name: z.string().optional(),
|
||||
last_name: z.string().optional(),
|
||||
}),
|
||||
}),
|
||||
integrations: {
|
||||
resend,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
const response = await io.resend.contacts.create("📧", {
|
||||
audience_id: payload.audience_id,
|
||||
email: payload.email,
|
||||
unsubscribed: payload.unsubscribed,
|
||||
first_name: payload.first_name,
|
||||
last_name: payload.last_name,
|
||||
});
|
||||
|
||||
await io.logger.info("Created contact", { response });
|
||||
},
|
||||
});
|
||||
|
||||
client.defineJob({
|
||||
id: "get resend contact",
|
||||
name: "Get Resend Contact",
|
||||
version: "0.1.0",
|
||||
trigger: eventTrigger({
|
||||
name: "get.contact",
|
||||
schema: z.object({
|
||||
audience_id: z.string(),
|
||||
id: z.string(),
|
||||
}),
|
||||
}),
|
||||
integrations: {
|
||||
resend,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
const response = await io.resend.contacts.get("📧", {
|
||||
audience_id: payload.audience_id,
|
||||
id: payload.id,
|
||||
});
|
||||
|
||||
await io.logger.info("Got contact", { response });
|
||||
},
|
||||
});
|
||||
|
||||
client.defineJob({
|
||||
id: "update resend contact",
|
||||
name: "Update Resend Contact",
|
||||
version: "0.1.0",
|
||||
trigger: eventTrigger({
|
||||
name: "update.contact",
|
||||
schema: z.object({
|
||||
audience_id: z.string(),
|
||||
id: z.string(),
|
||||
email: z.string().optional(),
|
||||
unsubscribed: z.boolean().optional(),
|
||||
first_name: z.string().optional(),
|
||||
last_name: z.string().optional(),
|
||||
}),
|
||||
}),
|
||||
integrations: {
|
||||
resend,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
const response = await io.resend.contacts.update("📧", {
|
||||
audience_id: payload.audience_id,
|
||||
id: payload.id,
|
||||
unsubscribed: payload.unsubscribed,
|
||||
first_name: payload.first_name,
|
||||
last_name: payload.last_name,
|
||||
});
|
||||
|
||||
await io.logger.info("Updated contact", { response });
|
||||
},
|
||||
});
|
||||
|
||||
client.defineJob({
|
||||
id: "remove resend contact",
|
||||
name: "Remove Resend Contact",
|
||||
version: "0.1.0",
|
||||
trigger: eventTrigger({
|
||||
name: "remove.contact",
|
||||
schema: z.object({
|
||||
audience_id: z.string(),
|
||||
id: z.string(),
|
||||
}),
|
||||
}),
|
||||
integrations: {
|
||||
resend,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
const response = await io.resend.contacts.remove("📧", {
|
||||
audience_id: payload.audience_id,
|
||||
id: payload.id,
|
||||
});
|
||||
|
||||
await io.logger.info("Removed contact", { response });
|
||||
},
|
||||
});
|
||||
|
||||
client.defineJob({
|
||||
id: "list resend contacts",
|
||||
name: "List Resend Contacts",
|
||||
version: "0.1.0",
|
||||
trigger: eventTrigger({
|
||||
name: "list.contacts",
|
||||
schema: z.object({
|
||||
audience_id: z.string(),
|
||||
}),
|
||||
}),
|
||||
integrations: {
|
||||
resend,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
const response = await io.resend.contacts.list("📧", {
|
||||
audience_id: payload.audience_id,
|
||||
});
|
||||
|
||||
await io.logger.info("Listed contacts", { response });
|
||||
},
|
||||
});
|
||||
|
||||
createExpressServer(client);
|
||||
|
||||
Reference in New Issue
Block a user