feature: initial work for sendgrid integration (#264)
* feature: initial work for sendgrid integration * fix: fixed the ts-error and added sendgrid integration to the job-catalogue * SendGrid added to dependencies * Made the from email an env var so it can be tested by non-Trigger.dev team members * Refactor: Modify sendEmail function to return void and remove SendEmailResponse type Co-authored-by: Matt Aitken <matt@mattaitken.com> * Create funny-kangaroos-glow.md --------- Co-authored-by: Matt Aitken <matt@mattaitken.com> Co-authored-by: Eric Allam <eallam@icloud.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@trigger.dev/sendgrid": patch
|
||||
---
|
||||
|
||||
feature: initial work for sendgrid integration
|
||||
@@ -4,6 +4,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"stripe": "nodemon --watch src/stripe.ts -r tsconfig-paths/register -r dotenv/config src/stripe.ts",
|
||||
"sendgrid": "nodemon --watch src/sendgrid.ts -r tsconfig-paths/register -r dotenv/config src/sendgrid.ts",
|
||||
"supabase": "nodemon --watch src/supabase.ts -r tsconfig-paths/register -r dotenv/config src/supabase.ts",
|
||||
"supabase:types": "npx supabase gen types typescript --project-id $SUPABASE_PROJECT_ID --schema public --schema auth --schema storage > src/supabase-types.ts",
|
||||
"dev:trigger": "trigger-cli dev --port 8080"
|
||||
@@ -14,6 +15,7 @@
|
||||
"@trigger.dev/openai": "workspace:*",
|
||||
"@trigger.dev/plain": "workspace:*",
|
||||
"@trigger.dev/resend": "workspace:*",
|
||||
"@trigger.dev/sendgrid": "workspace:*",
|
||||
"@trigger.dev/sdk": "workspace:*",
|
||||
"@trigger.dev/slack": "workspace:*",
|
||||
"@trigger.dev/stripe": "workspace:*",
|
||||
@@ -35,4 +37,4 @@
|
||||
"ts-node": "^10.9.1",
|
||||
"tsconfig-paths": "^3.14.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { TriggerClient, eventTrigger } from "@trigger.dev/sdk";
|
||||
import { createExpressServer } from "@trigger.dev/express";
|
||||
import { z } from "zod";
|
||||
import { SendGrid } from "@trigger.dev/sendgrid";
|
||||
|
||||
export const client = new TriggerClient({
|
||||
id: "job-catalog",
|
||||
apiKey: process.env["TRIGGER_API_KEY"],
|
||||
verbose: false,
|
||||
ioLogLocalEnabled: true,
|
||||
});
|
||||
|
||||
const sendgrid = new SendGrid({
|
||||
id: "sendgrid-client",
|
||||
apiKey: process.env.SENDGRID_API_KEY!,
|
||||
});
|
||||
|
||||
client.defineJob({
|
||||
id: "send-Sendgrid-email",
|
||||
name: "Send Sendgrid Email",
|
||||
version: "0.1.0",
|
||||
trigger: eventTrigger({
|
||||
name: "send.email",
|
||||
schema: z.object({
|
||||
to: z.union([z.string(), z.array(z.string())]),
|
||||
subject: z.string(),
|
||||
text: z.string(),
|
||||
}),
|
||||
}),
|
||||
integrations: {
|
||||
sendgrid,
|
||||
},
|
||||
run: async (payload, io, ctx) => {
|
||||
await io.sendgrid.sendEmail("📧", {
|
||||
to: payload.to,
|
||||
subject: payload.subject,
|
||||
text: payload.text,
|
||||
//this email must be verified in SendGrid, otherwise you'll get a forbidden error
|
||||
from: process.env.SENDGRID_FROM_EMAIL!,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
createExpressServer(client);
|
||||
@@ -29,7 +29,9 @@
|
||||
"@trigger.dev/supabase": ["../../integrations/supabase/src/index"],
|
||||
"@trigger.dev/supabase/*": ["../../integrations/supabase/src/*"],
|
||||
"@trigger.dev/stripe": ["../../integrations/stripe/src/index"],
|
||||
"@trigger.dev/stripe/*": ["../../integrations/stripe/src/*"]
|
||||
"@trigger.dev/stripe/*": ["../../integrations/stripe/src/*"],
|
||||
"@trigger.dev/sendgrid": ["../../integrations/sendgrid/src/index"],
|
||||
"@trigger.dev/sendgrid/*": ["../../integrations/sendgrid/src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
"typescript": "5.0.4",
|
||||
"zod": "3.21.4",
|
||||
"@trigger.dev/supabase": "workspace:*",
|
||||
"@trigger.dev/stripe": "workspace:*"
|
||||
"@trigger.dev/stripe": "workspace:*",
|
||||
"@trigger.dev/sendgrid": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@trigger.dev/cli": "workspace:*",
|
||||
@@ -43,4 +44,4 @@
|
||||
"trigger.dev": {
|
||||
"endpointId": "nextjs-example"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2015",
|
||||
"lib": ["dom", "dom.iterable", "ES2015"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"ES2015"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
@@ -15,31 +19,87 @@
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"@trigger.dev/sdk": ["../../packages/trigger-sdk/src/index"],
|
||||
"@trigger.dev/sdk/*": ["../../packages/trigger-sdk/src/*"],
|
||||
"@trigger.dev/nextjs": ["../../packages/nextjs/src/index"],
|
||||
"@trigger.dev/nextjs/*": ["../../packages/nextjs/src/*"],
|
||||
"@trigger.dev/core": ["../../packages/core/src/index"],
|
||||
"@trigger.dev/core/*": ["../../packages/core/src/*"],
|
||||
"@trigger.dev/integration-kit": ["../../packages/integration-kit/src/index"],
|
||||
"@trigger.dev/integration-kit/*": ["../../packages/integration-kit/src/*"],
|
||||
"@trigger.dev/github": ["../../integrations/github/src/index"],
|
||||
"@trigger.dev/github/*": ["../../integrations/github/src/*"],
|
||||
"@trigger.dev/slack": ["../../integrations/slack/src/index"],
|
||||
"@trigger.dev/slack/*": ["../../integrations/slack/src/*"],
|
||||
"@trigger.dev/openai": ["../../integrations/openai/src/index"],
|
||||
"@trigger.dev/openai/*": ["../../integrations/openai/src/*"],
|
||||
"@trigger.dev/resend": ["../../integrations/resend/src/index"],
|
||||
"@trigger.dev/resend/*": ["../../integrations/resend/src/*"],
|
||||
"@trigger.dev/typeform": ["../../integrations/typeform/src/index"],
|
||||
"@trigger.dev/typeform/*": ["../../integrations/typeform/src/*"],
|
||||
"@trigger.dev/plain": ["../../integrations/plain/src/index"],
|
||||
"@trigger.dev/plain/*": ["../../integrations/plain/src/*"],
|
||||
"@trigger.dev/supabase": ["../../integrations/supabase/src/index"],
|
||||
"@trigger.dev/supabase/*": ["../../integrations/supabase/src/*"],
|
||||
"@trigger.dev/stripe": ["../../integrations/stripe/src/index"],
|
||||
"@trigger.dev/stripe/*": ["../../integrations/stripe/src/*"]
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
],
|
||||
"@trigger.dev/sdk": [
|
||||
"../../packages/trigger-sdk/src/index"
|
||||
],
|
||||
"@trigger.dev/sdk/*": [
|
||||
"../../packages/trigger-sdk/src/*"
|
||||
],
|
||||
"@trigger.dev/nextjs": [
|
||||
"../../packages/nextjs/src/index"
|
||||
],
|
||||
"@trigger.dev/nextjs/*": [
|
||||
"../../packages/nextjs/src/*"
|
||||
],
|
||||
"@trigger.dev/core": [
|
||||
"../../packages/core/src/index"
|
||||
],
|
||||
"@trigger.dev/core/*": [
|
||||
"../../packages/core/src/*"
|
||||
],
|
||||
"@trigger.dev/integration-kit": [
|
||||
"../../packages/integration-kit/src/index"
|
||||
],
|
||||
"@trigger.dev/integration-kit/*": [
|
||||
"../../packages/integration-kit/src/*"
|
||||
],
|
||||
"@trigger.dev/github": [
|
||||
"../../integrations/github/src/index"
|
||||
],
|
||||
"@trigger.dev/github/*": [
|
||||
"../../integrations/github/src/*"
|
||||
],
|
||||
"@trigger.dev/slack": [
|
||||
"../../integrations/slack/src/index"
|
||||
],
|
||||
"@trigger.dev/slack/*": [
|
||||
"../../integrations/slack/src/*"
|
||||
],
|
||||
"@trigger.dev/openai": [
|
||||
"../../integrations/openai/src/index"
|
||||
],
|
||||
"@trigger.dev/openai/*": [
|
||||
"../../integrations/openai/src/*"
|
||||
],
|
||||
"@trigger.dev/resend": [
|
||||
"../../integrations/resend/src/index"
|
||||
],
|
||||
"@trigger.dev/resend/*": [
|
||||
"../../integrations/resend/src/*"
|
||||
],
|
||||
"@trigger.dev/typeform": [
|
||||
"../../integrations/typeform/src/index"
|
||||
],
|
||||
"@trigger.dev/typeform/*": [
|
||||
"../../integrations/typeform/src/*"
|
||||
],
|
||||
"@trigger.dev/plain": [
|
||||
"../../integrations/plain/src/index"
|
||||
],
|
||||
"@trigger.dev/plain/*": [
|
||||
"../../integrations/plain/src/*"
|
||||
],
|
||||
"@trigger.dev/supabase": [
|
||||
"../../integrations/supabase/src/index"
|
||||
],
|
||||
"@trigger.dev/supabase/*": [
|
||||
"../../integrations/supabase/src/*"
|
||||
],
|
||||
"@trigger.dev/stripe": [
|
||||
"../../integrations/stripe/src/index"
|
||||
],
|
||||
"@trigger.dev/stripe/*": [
|
||||
"../../integrations/stripe/src/*"
|
||||
],
|
||||
"@trigger.dev/sendgrid": [
|
||||
"../../integrations/sendgrid/src/index"
|
||||
],
|
||||
"@trigger.dev/sendgrid/*": [
|
||||
"../../integrations/sendgrid/src/*"
|
||||
]
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
@@ -47,6 +107,13 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
# @trigger.dev/sendgrid
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "@trigger.dev/sendgrid",
|
||||
"version": "0.0.1",
|
||||
"description": "Trigger.dev integration for @sendgrid/mail",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"files": [
|
||||
"dist/index.js",
|
||||
"dist/index.d.ts",
|
||||
"dist/index.js.map"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/node": "16.x",
|
||||
"rimraf": "^3.0.2",
|
||||
"tsup": "7.1.x",
|
||||
"typescript": "4.9.4"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"build": "npm run clean && npm run build:tsup",
|
||||
"build:tsup": "tsup",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sendgrid/mail": "^7.7.0",
|
||||
"@trigger.dev/sdk": "workspace:^2.0.3",
|
||||
"@trigger.dev/integration-kit": "workspace:^2.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.8.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
import { IntegrationClient, TriggerIntegration } from "@trigger.dev/sdk";
|
||||
import sgMail from "@sendgrid/mail";
|
||||
|
||||
import type { AuthenticatedTask } from "@trigger.dev/sdk";
|
||||
|
||||
type SendEmailData = Parameters<(typeof sgMail)["send"]>[0];
|
||||
export const sendEmail: AuthenticatedTask<typeof sgMail, SendEmailData, void> = {
|
||||
run: async (params, client) => {
|
||||
await client.send(params);
|
||||
},
|
||||
init: (params) => {
|
||||
const subjectProperty = Array.isArray(params)
|
||||
? []
|
||||
: params.subject
|
||||
? [{ label: "Subject", text: params.subject }]
|
||||
: [];
|
||||
|
||||
return {
|
||||
name: "Send Email",
|
||||
params,
|
||||
icon: "sendgrid",
|
||||
properties: [
|
||||
{
|
||||
label: "From",
|
||||
text: Array.isArray(params)
|
||||
? getEmailFromEmailData(params[0].from)
|
||||
: getEmailFromEmailData(params.from),
|
||||
},
|
||||
{
|
||||
label: "To",
|
||||
text: Array.isArray(params)
|
||||
? getEmailFromEmailData(params[0]?.to)
|
||||
: getEmailFromEmailData(params?.to),
|
||||
},
|
||||
...subjectProperty,
|
||||
],
|
||||
retry: {
|
||||
limit: 8,
|
||||
factor: 1.8,
|
||||
minTimeoutInMs: 500,
|
||||
maxTimeoutInMs: 30000,
|
||||
randomize: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const tasks = {
|
||||
sendEmail,
|
||||
};
|
||||
|
||||
export type SendGridIntegrationOptions = {
|
||||
id: string;
|
||||
apiKey: string;
|
||||
};
|
||||
|
||||
export class SendGrid
|
||||
implements TriggerIntegration<IntegrationClient<typeof sgMail, typeof tasks>>
|
||||
{
|
||||
client: IntegrationClient<typeof sgMail, typeof tasks>;
|
||||
|
||||
constructor(private options: SendGridIntegrationOptions) {
|
||||
if (!options.apiKey) {
|
||||
throw new Error(`Can't create SendGrid integration (${options.id}) as apiKey was undefined`);
|
||||
}
|
||||
|
||||
sgMail.setApiKey(options.apiKey);
|
||||
|
||||
this.client = {
|
||||
tasks,
|
||||
usesLocalAuth: true,
|
||||
client: sgMail,
|
||||
auth: {
|
||||
apiKey: options.apiKey,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this.options.id;
|
||||
}
|
||||
|
||||
get metadata() {
|
||||
return { id: "sendgrid", name: "SendGrid" };
|
||||
}
|
||||
}
|
||||
|
||||
type EmailData = string | { name?: string; email: string };
|
||||
type EmailDataArray = EmailData | EmailData[];
|
||||
|
||||
function getEmailFromEmailData(emailData: EmailDataArray | undefined): string {
|
||||
if (emailData === undefined) {
|
||||
return "";
|
||||
}
|
||||
if (Array.isArray(emailData)) {
|
||||
return emailData.map(getEmailFromEmailData).join(", ");
|
||||
}
|
||||
|
||||
if (typeof emailData === "string") {
|
||||
return emailData;
|
||||
}
|
||||
return emailData.email;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": false,
|
||||
"paths": {
|
||||
"@trigger.dev/sdk/*": ["../../packages/trigger-sdk/src/*"],
|
||||
"@trigger.dev/sdk": ["../../packages/trigger-sdk/src/index"],
|
||||
"@trigger.dev/integration-kit/*": ["../../packages/integration-kit/src/*"],
|
||||
"@trigger.dev/integration-kit": ["../../packages/integration-kit/src/index"]
|
||||
},
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"inlineSources": false,
|
||||
"isolatedModules": true,
|
||||
"moduleResolution": "node16",
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"preserveWatchOutput": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"lib": [
|
||||
"es2019"
|
||||
],
|
||||
"module": "Node16",
|
||||
"target": "es2021"
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*.ts",
|
||||
"tsup.config.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
import { defineConfig } from "tsup";
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
name: "main",
|
||||
entry: ["./src/index.ts"],
|
||||
outDir: "./dist",
|
||||
platform: "node",
|
||||
format: ["cjs"],
|
||||
legacyOutput: true,
|
||||
sourcemap: true,
|
||||
clean: true,
|
||||
bundle: true,
|
||||
splitting: false,
|
||||
dts: true,
|
||||
treeshake: {
|
||||
preset: "smallest",
|
||||
},
|
||||
esbuildPlugins: [],
|
||||
external: ["http", "https", "util", "events", "tty", "os", "timers"],
|
||||
},
|
||||
]);
|
||||
|
||||
Generated
+168
-1136
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user