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:
Chigala
2023-08-11 10:12:19 +01:00
committed by GitHub
parent a5b5912270
commit 2bedc779b9
12 changed files with 526 additions and 1169 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@trigger.dev/sendgrid": patch
---
feature: initial work for sendgrid integration
+3 -1
View File
@@ -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"
}
}
}
+44
View File
@@ -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);
+3 -1
View File
@@ -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/*"]
}
}
}
+3 -2
View File
@@ -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"
}
}
}
+96 -29
View File
@@ -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"
]
}
+3
View File
@@ -0,0 +1,3 @@
# @trigger.dev/sendgrid
+35
View File
@@ -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"
}
}
+103
View File
@@ -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;
}
+39
View File
@@ -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"
]
}
+24
View File
@@ -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"],
},
]);
+168 -1136
View File
File diff suppressed because it is too large Load Diff