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:
Kritik Jiyaviya
2024-01-17 19:36:45 +05:30
committed by GitHub
parent adf66d23b8
commit 4dd6cf18dc
8 changed files with 593 additions and 22 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/resend": patch
---
Add support for the new Resend audience API endpoints
+16 -7
View File
@@ -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
+1 -1
View File
@@ -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"
+134
View File
@@ -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
);
}
}
+188
View File
@@ -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
);
}
}
+34
View File
@@ -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));
}
}
+5 -14
View File
@@ -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
+210
View File
@@ -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);