diff --git a/CHANGELOG.md b/CHANGELOG.md index 20d3bad..d23e325 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,13 @@ ## 1.3.0 (2024-08-12) - ### 🩹 Fixes -- ignore collection dirs ([d26eb66](https://github.com/icdocsoc/docsoc-tools/commit/d26eb66)) +- ignore collection dirs ([d26eb66](https://github.com/icdocsoc/docsoc-tools/commit/d26eb66)) +- Add --only & moving of drafts [396b645](https://github.com/icdocsoc/docsoc-tools/commit/396b645) -### ❤️ Thank You +### ❤️ Thank You -- Kishan Sambhi @Gum-Joe +- Kishan Sambhi @Gum-Joe ## 1.2.1 (2024-08-06) @@ -15,26 +15,24 @@ This was a version bump only, there were no code changes. ## 1.2.0 (2024-08-06) - ### 🩹 Fixes -- build cli in place so oclif won't complain ([596a78c](https://github.com/icdocsoc/docsoc-tools/commit/596a78c)) -- tests breaking because ESM ([49823c9](https://github.com/icdocsoc/docsoc-tools/commit/49823c9)) +- build cli in place so oclif won't complain ([596a78c](https://github.com/icdocsoc/docsoc-tools/commit/596a78c)) +- tests breaking because ESM ([49823c9](https://github.com/icdocsoc/docsoc-tools/commit/49823c9)) -### ❤️ Thank You +### ❤️ Thank You -- Kishan Sambhi @Gum-Joe +- Kishan Sambhi @Gum-Joe ## 1.1.2 (2024-08-06) - ### 🩹 Fixes -- add bin to mailmerge cli bundle ([8fec651](https://github.com/icdocsoc/docsoc-tools/commit/8fec651)) +- add bin to mailmerge cli bundle ([8fec651](https://github.com/icdocsoc/docsoc-tools/commit/8fec651)) -### ❤️ Thank You +### ❤️ Thank You -- Kishan Sambhi @Gum-Joe +- Kishan Sambhi @Gum-Joe ## 1.1.1 (2024-08-06) @@ -46,20 +44,19 @@ This was a version bump only, there were no code changes. # 1.0.0 (2024-08-06) - ### 🚀 Features -- scaffold up full preview renderer ([a41320e](https://github.com/icdocsoc/docsoc-tools/commit/a41320e)) -- rerendering of templates ([ea21d57](https://github.com/icdocsoc/docsoc-tools/commit/ea21d57)) -- init command fix: issue where nunjucks didn't pass it all ([5eb8b75](https://github.com/icdocsoc/docsoc-tools/commit/5eb8b75)) -- **nx:** Added Nx Cloud token to your nx.json ([646eb34](https://github.com/icdocsoc/docsoc-tools/commit/646eb34)) -- **nx:** Generated CI workflow ([abf6ea5](https://github.com/icdocsoc/docsoc-tools/commit/abf6ea5)) +- scaffold up full preview renderer ([a41320e](https://github.com/icdocsoc/docsoc-tools/commit/a41320e)) +- rerendering of templates ([ea21d57](https://github.com/icdocsoc/docsoc-tools/commit/ea21d57)) +- init command fix: issue where nunjucks didn't pass it all ([5eb8b75](https://github.com/icdocsoc/docsoc-tools/commit/5eb8b75)) +- **nx:** Added Nx Cloud token to your nx.json ([646eb34](https://github.com/icdocsoc/docsoc-tools/commit/646eb34)) +- **nx:** Generated CI workflow ([abf6ea5](https://github.com/icdocsoc/docsoc-tools/commit/abf6ea5)) ### 🩹 Fixes -- throw error on undefined value ([15c45ab](https://github.com/icdocsoc/docsoc-tools/commit/15c45ab)) -- stop if uploaded ([ea0b979](https://github.com/icdocsoc/docsoc-tools/commit/ea0b979)) +- throw error on undefined value ([15c45ab](https://github.com/icdocsoc/docsoc-tools/commit/15c45ab)) +- stop if uploaded ([ea0b979](https://github.com/icdocsoc/docsoc-tools/commit/ea0b979)) -### ❤️ Thank You +### ❤️ Thank You -- Kishan Sambhi @Gum-Joe \ No newline at end of file +- Kishan Sambhi @Gum-Joe diff --git a/email/mailmerge-cli/src/commands/send.ts b/email/mailmerge-cli/src/commands/send.ts index 679e253..a76a4ed 100644 --- a/email/mailmerge-cli/src/commands/send.ts +++ b/email/mailmerge-cli/src/commands/send.ts @@ -4,6 +4,8 @@ import { getDefaultMailer, getDefaultDoCSocFromLine, ENGINES_MAP, + EmailString, + Mailer, } from "@docsoc/mailmerge"; import { Args, Command, Flags } from "@oclif/core"; @@ -33,6 +35,10 @@ export default class Send extends Command { char: "n", description: "Only send this many emails (i.e. the first X emails)", }), + testSendTo: Flags.string({ + char: "t", + description: "Send the top X emails to this email as a test. Requires --only to be set", + }), }; public async run(): Promise { @@ -44,6 +50,21 @@ export default class Send extends Command { /// @ts-expect-error: Required for fileNamer namer: (record) => record[DEFAULT_FIELD_NAMES.to], }); + + if (flags.testSendTo && !flags.only) { + this.error("You must set --only to use --testSendTo"); + } + + let testSendTo: EmailString | undefined; + + if (flags.testSendTo) { + if (Mailer.validateEmail(flags.testSendTo)) { + testSendTo = flags.testSendTo; + } else { + throw new Error("Invalid email address provided for --testSendTo"); + } + } + // Rerender previews await sendEmails( storageBackend, @@ -54,6 +75,7 @@ export default class Send extends Command { { sleepBetween: flags.sleepBetween, onlySend: flags.only, + testSendTo, }, ); } diff --git a/email/mailmerge/src/pipelines/send.spec.ts b/email/mailmerge/src/pipelines/send.spec.ts index a52ff8f..8d23e32 100644 --- a/email/mailmerge/src/pipelines/send.spec.ts +++ b/email/mailmerge/src/pipelines/send.spec.ts @@ -123,6 +123,84 @@ describe("sendEmails", () => { expect(mockMailer.sendMail).toHaveBeenCalledTimes(1); }); + it("should send to test email if given and not cc or bcc anyone in", async () => { + const mergeResults: MergeResultWithMetadata[] = [ + { + record: { field1: "value1" }, + /// @ts-expect-error: Mocking previews + previews: ["preview"], + engineInfo: { name: "testEngine", options: {} }, + email: { + to: ["test@example.com"], + subject: "Test Subject", + cc: ["cc@cc.com"], + bcc: ["bcc@bcc.com"], + }, + attachmentPaths: [], + }, + ]; + (mockStorageBackend.loadMergeResults as jest.Mock).mockReturnValue(mergeResults); + + const enginesMap = { + testEngine: mockEngineConstructor, + }; + + await sendEmails( + mockStorageBackend, + mockMailer, + '"From" ', + enginesMap, + true, + { + onlySend: 1, + testSendTo: "test@example.com", + }, + ); + + expect(mockMailer.sendMail).toHaveBeenCalledWith( + '"From" ', + ["test@example.com"], + "(TEST) Test Subject", + expect.any(String), + [], + { + cc: [], + bcc: [], + }, + ); + }); + + it("should refuse test send if onlySend is not set", async () => { + const mergeResults: MergeResultWithMetadata[] = [ + { + record: { field1: "value1" }, + /// @ts-expect-error: Mocking previews + previews: ["preview"], + engineInfo: { name: "testEngine", options: {} }, + email: { to: ["test@example.com"], subject: "Test Subject", cc: [], bcc: [] }, + attachmentPaths: [], + }, + ]; + (mockStorageBackend.loadMergeResults as jest.Mock).mockReturnValue(mergeResults); + + const enginesMap = { + testEngine: mockEngineConstructor, + }; + + await expect( + sendEmails( + mockStorageBackend, + mockMailer, + '"From" ', + enginesMap, + true, + { + testSendTo: "test@example.com", + }, + ), + ).rejects.toThrow("You must set onlySend to a number to use test mode."); + }); + it("should send no emails if only send 0", async () => { const mergeResults: MergeResultWithMetadata[] = [ { diff --git a/email/mailmerge/src/pipelines/send.ts b/email/mailmerge/src/pipelines/send.ts index 0e30eea..62af176 100644 --- a/email/mailmerge/src/pipelines/send.ts +++ b/email/mailmerge/src/pipelines/send.ts @@ -15,6 +15,8 @@ interface SendEmailsOptions { sleepBetween?: number; /** Only send this many emails (i.e. the first X emails) */ onlySend?: number; + /** Send the top {@link onlySend} emails to this email as a test */ + testSendTo?: EmailString; } const DEFAULT_SLEEP_BETWEEN = 0; @@ -53,6 +55,20 @@ export async function sendEmails( logger.info("Loading merge results..."); const results = storageBackend.loadMergeResults(); + if (options.testSendTo) { + logger.warn(""); + logger.warn("======================="); + logger.warn("TEST MODE ACTIVATED."); + logger.warn("======================="); + if (!options.onlySend) { + logger.error("You must set onlySend to a number to use test mode."); + throw new Error("You must set onlySend to a number to use test mode."); + } + logger.warn(""); + logger.warn(`Will send ${options.onlySend} emails to ${options.testSendTo} as a test.`); + logger.warn(""); + } + // For each sidecar, send the previews const pendingEmails: { to: EmailString[]; @@ -83,21 +99,26 @@ export async function sendEmails( // Add to pending emails pendingEmails.push({ - to: email.to, - subject: email.subject, + to: options.testSendTo ? [options.testSendTo] : email.to, + subject: options.testSendTo ? "(TEST) " + email.subject : email.subject, html, attachments: attachmentPaths, - cc: email.cc, - bcc: email.bcc, + cc: options.testSendTo ? [] : email.cc, + bcc: options.testSendTo ? [] : email.bcc, originalResult: result, }); } // Print the warning + const emailsNumberDisplay = Math.min( + pendingEmails.length, + options.onlySend ?? Number.MAX_SAFE_INTEGER, + ); + console.log( chalk.yellow(`⚠️ --- WARNING --- ⚠️ -You are about to send ${pendingEmails.length} emails. +You are about to send ${emailsNumberDisplay} emails. This action is IRREVERSIBLE. If the system crashes, restarting will NOT necessarily send already-sent emails again. @@ -108,8 +129,8 @@ Check that: 3. You have tested the system beforehand 4. All indications this is a test have been removed -You are about to send ${pendingEmails.length} emails. The esitmated time for this is ${ - ((3 + (options.sleepBetween ?? DEFAULT_SLEEP_BETWEEN)) * pendingEmails.length) / 60 / 60 +You are about to send ${emailsNumberDisplay} emails. The esitmated time for this is ${ + ((3 + (options.sleepBetween ?? DEFAULT_SLEEP_BETWEEN)) * emailsNumberDisplay) / 60 / 60 } hours. If you are happy to proceed, please type "Yes, send emails" below.`), @@ -123,10 +144,15 @@ You are about to send ${pendingEmails.length} emails. The esitmated time for thi // Send the emails logger.info("Sending emails..."); - const total = pendingEmails.length; + const total = emailsNumberDisplay; let sent = 0; for (const { to, subject, html, attachments, cc, bcc, originalResult } of pendingEmails) { logger.info(`(${++sent} / ${total}) Sending email to ${to} with subject ${subject}...`); + if (options.testSendTo && to[0] !== options.testSendTo) { + throw new Error( + "Test mode is on, but the email is not the test email! This is a bug in the code, crashing to prevent sending emails to the wrong person.", + ); + } await mailer.sendMail( fromAddress, to, diff --git a/email/workspace/data/names.csv b/email/workspace/data/names.csv index f7d15b1..7e382a1 100644 --- a/email/workspace/data/names.csv +++ b/email/workspace/data/names.csv @@ -1,3 +1,3 @@ name,to,subject,attachment1,attachment2,bcc,cc -Kishan,kss22@ic.ac.uk kishansambhi@hotmail.co.uk,DoCSoc x Kishan,./attachments/DoCSoc Sponsorship Proposal 24-25.pdf,./attachments/munch.jpg,kishansambhi@hotmail.co.uk,jaskishansaran@gmail.com kishansambhi@outlook.com +Kishan,kishansambhi@hotmail.co.uk,DoCSoc x Kishan,./attachments/DoCSoc Sponsorship Proposal 24-25.pdf,./attachments/munch.jpg,kishansambhi@hotmail.co.uk,jaskishansaran@gmail.com kishansambhi@outlook.com NotKishan,kishansambhi@hotmail.co.uk," DoCSoc x NotKishan",./attachments/DoCSoc Sponsorship Proposal 24-25.pdf,./attachments/munch.jpg,, \ No newline at end of file diff --git a/email/workspace/quick-run.sh b/email/workspace/quick-run.sh index fbeeaa7..3d5e072 100755 --- a/email/workspace/quick-run.sh +++ b/email/workspace/quick-run.sh @@ -1,4 +1,4 @@ docsoc-mailmerge generate nunjucks ./data/names.csv -o ./output -n test -b -c docsoc-mailmerge regenerate ./output/test -docsoc-mailmerge upload-drafts ./output/test -y -s 2 -n 1 -docsoc-mailmerge send ./output/test -s 5 -n 1 \ No newline at end of file +# docsoc-mailmerge upload-drafts ./output/test -y -s 2 -n 1 +docsoc-mailmerge send ./output/test -s 5 -n 2 -t "kss22@ic.ac.uk" \ No newline at end of file