From fcb44853fd8e104aca2cfc30f66556d1e6d15e82 Mon Sep 17 00:00:00 2001 From: Jesse Shawl Date: Tue, 19 Mar 2024 06:30:52 -0500 Subject: [PATCH 1/5] add mail --- .editorconfig | 12 ------------ src/index.js | 2 ++ src/mail.js | 3 +++ 3 files changed, 5 insertions(+), 12 deletions(-) delete mode 100644 .editorconfig create mode 100644 src/mail.js diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index a727df3..0000000 --- a/.editorconfig +++ /dev/null @@ -1,12 +0,0 @@ -# http://editorconfig.org -root = true - -[*] -indent_style = tab -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.yml] -indent_style = space diff --git a/src/index.js b/src/index.js index b4dc21d..46d34e1 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,5 @@ +import { mail } from './mail'; + const validate = async (request) => { const required = ['subject', 'to']; let data; diff --git a/src/mail.js b/src/mail.js new file mode 100644 index 0000000..8b280af --- /dev/null +++ b/src/mail.js @@ -0,0 +1,3 @@ +export const mail = ({ subject, to }) => { + // sendgrid +}; From 637fb7e242286d3e3c77bcbbb3e7e5c0b418f431 Mon Sep 17 00:00:00 2001 From: Jesse Shawl Date: Tue, 19 Mar 2024 18:07:21 -0500 Subject: [PATCH 2/5] send email --- README.md | 15 +++++++++++++++ src/index.js | 14 ++++++++------ {test => src}/index.spec.js | 3 ++- src/mail.js | 35 +++++++++++++++++++++++++++++++++-- src/mail.test.js | 27 +++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 9 deletions(-) create mode 100644 README.md rename {test => src}/index.spec.js (93%) create mode 100644 src/mail.test.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..b2fbdfb --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# CINotify API + +## Demo + +``` +curl --request POST 'http://localhost:8787/api/notify' \ + -d "to=api-demo@jesse.sh&subject=🎉" +``` + +## Local Development + +``` +npm run dev +npm test +``` diff --git a/src/index.js b/src/index.js index 46d34e1..ccc502f 100644 --- a/src/index.js +++ b/src/index.js @@ -2,7 +2,7 @@ import { mail } from './mail'; const validate = async (request) => { const required = ['subject', 'to']; - let data; + let data = {}; try { if (request.headers.get('content-type') === 'application/json') { data = (await request.json()) ?? {}; @@ -15,7 +15,7 @@ const validate = async (request) => { data = {}; } - data.errors = required.reduce((acc, el) => (data[el] ? acc : [...acc, `missing required parameter '${el}'`]), []); + data.errors = required.reduce((acc, el) => (data?.[el] ? acc : [...acc, `missing required parameter '${el}'`]), []); return data; }; @@ -23,12 +23,14 @@ const validate = async (request) => { export default { async fetch(request, env, ctx) { const { pathname } = new URL(request.url); - if (pathname === '/api/notify') { - const { subject, to, errors } = await validate(request); - const body = { + if (pathname === '/api/notify' && request.method === 'POST') { + const { subject, to, errors = [] } = await validate(request); + console.log('env is', env); + const body = await mail({ + env, subject, to, - }; + }); const response = { ...body, ...(errors.length && { errors }), diff --git a/test/index.spec.js b/src/index.spec.js similarity index 93% rename from test/index.spec.js rename to src/index.spec.js index ecdc7c3..22615b2 100644 --- a/test/index.spec.js +++ b/src/index.spec.js @@ -23,7 +23,8 @@ describe('API', () => { }); expect(response.status).toEqual(400); }); - it('responds to application/json and application/x-www-form-urlencoded', async () => { + // TODO: mock worker outbound fetch + it.skip('responds to application/json and application/x-www-form-urlencoded', async () => { const jsonResponse = await send( new Request('http://example.com/api/notify', { method: 'POST', diff --git a/src/mail.js b/src/mail.js index 8b280af..61542f9 100644 --- a/src/mail.js +++ b/src/mail.js @@ -1,3 +1,34 @@ -export const mail = ({ subject, to }) => { - // sendgrid +export const mail = async ({ subject, to, env }) => { + const response = await fetch('https://api.sendgrid.com/v3/mail/send', { + headers: { + Authorization: `Bearer ${env.SENDGRID_API_KEY}`, + 'Content-Type': 'application/json', + }, + method: 'POST', + body: JSON.stringify({ + // https://docs.sendgrid.com/api-reference/mail-send/mail-send#body + content: [ + { + type: 'text/plain', + value: ' ', + }, + ], + from: { + email: 'app@cinotify.cc', + }, + personalizations: [ + { + subject, + to: [{ email: to }], + }, + ], + }), + }); + let data; + try { + data = await response.json(); + } catch (e) { + data = {}; + } + return data; }; diff --git a/src/mail.test.js b/src/mail.test.js new file mode 100644 index 0000000..39fddc1 --- /dev/null +++ b/src/mail.test.js @@ -0,0 +1,27 @@ +import { describe, it, expect, vi, beforeAll, afterAll } from 'vitest'; +import { mail } from './mail'; + +const originalFetch = global.fetch; + +describe('mail', () => { + beforeAll(() => { + global.fetch = vi.fn(); + }); + afterAll(() => { + global.fetch = originalFetch; + vi.clearAllMocks(); + }); + it('makes a request to the sendgrid api', async () => { + global.fetch.mockResolvedValueOnce({ json: () => '{}' }); + const to = 'ex@mple.com'; + const subject = 'hello'; + await mail({ to, subject }); + expect(global.fetch).toHaveBeenCalledWith('https://api.sendgrid.com/v3/mail/send', { + headers: { + Authorization: expect.any(String), + }, + method: 'POST', + body: JSON.stringify({ to, subject }), + }); + }); +}); From 26dc5403f75281c0c7a26464e401cc63fde7cbb4 Mon Sep 17 00:00:00 2001 From: Jesse Shawl Date: Tue, 19 Mar 2024 18:09:35 -0500 Subject: [PATCH 3/5] add default env --- src/mail.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mail.js b/src/mail.js index 61542f9..7c49dd1 100644 --- a/src/mail.js +++ b/src/mail.js @@ -1,4 +1,4 @@ -export const mail = async ({ subject, to, env }) => { +export const mail = async ({ subject, to, env = {} }) => { const response = await fetch('https://api.sendgrid.com/v3/mail/send', { headers: { Authorization: `Bearer ${env.SENDGRID_API_KEY}`, From 9ee0a32805a88498edba9c0b99cca44a5dc7ff08 Mon Sep 17 00:00:00 2001 From: Jesse Shawl Date: Tue, 19 Mar 2024 18:18:29 -0500 Subject: [PATCH 4/5] fix tests --- src/mail.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mail.test.js b/src/mail.test.js index 39fddc1..148b6b3 100644 --- a/src/mail.test.js +++ b/src/mail.test.js @@ -19,9 +19,10 @@ describe('mail', () => { expect(global.fetch).toHaveBeenCalledWith('https://api.sendgrid.com/v3/mail/send', { headers: { Authorization: expect.any(String), + 'Content-Type': 'application/json', }, method: 'POST', - body: JSON.stringify({ to, subject }), + body: expect.stringMatching(/hello(.*)ex@mple.com/), }); }); }); From 2d396aa43527cb079c12dd21497512f7a991aa45 Mon Sep 17 00:00:00 2001 From: Jesse Shawl Date: Tue, 19 Mar 2024 18:20:15 -0500 Subject: [PATCH 5/5] cleanup --- src/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/index.js b/src/index.js index ccc502f..feedf59 100644 --- a/src/index.js +++ b/src/index.js @@ -25,7 +25,6 @@ export default { const { pathname } = new URL(request.url); if (pathname === '/api/notify' && request.method === 'POST') { const { subject, to, errors = [] } = await validate(request); - console.log('env is', env); const body = await mail({ env, subject,