Skip to content

Commit

Permalink
Wip: BCC & CC & space separated emails
Browse files Browse the repository at this point in the history
  • Loading branch information
Gum-Joe committed Aug 4, 2024
1 parent 7a2eaf0 commit fdbf127
Show file tree
Hide file tree
Showing 32 changed files with 121 additions and 625 deletions.
45 changes: 0 additions & 45 deletions email/libmailmerge/src/cli-testing.ts

This file was deleted.

15 changes: 11 additions & 4 deletions email/libmailmerge/src/engines/nunjucks-md/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ import { promises as fs } from "fs";
import nunjucks from "nunjucks";

import { renderMarkdownToHtml } from "../../markdown/toHtml";
import { CSVRecord } from "../../util/types";
import { MappedCSVRecord } from "../../util/types";
import { TemplateEngineOptions, TemplatePreviews } from "../types";
import { TemplateEngine } from "../types";
import getTemplateFields from "./getFields";
import { assertIsNunjucksTemplateOptions, NunjucksMarkdownTemplateOptions, NunjucksSidecarMetadata } from "./types";
import {
assertIsNunjucksTemplateOptions,
NunjucksMarkdownTemplateOptions,
NunjucksSidecarMetadata,
} from "./types";

export { default as getNunjucksTemplateFields } from "./getFields";
export * from "./types";
Expand Down Expand Up @@ -41,7 +45,10 @@ export default class NunjucksMarkdownEngine extends TemplateEngine {
private async renderMarkdownToHtmlInsideWrapper(markdown: string) {
// Render the MD to HTML
const htmlWrapper = await fs.readFile(this.templateOptions.rootHtmlTemplate, "utf-8");
const htmlWrapperCompiled = nunjucks.compile(htmlWrapper, nunjucks.configure({ autoescape: false }));
const htmlWrapperCompiled = nunjucks.compile(
htmlWrapper,
nunjucks.configure({ autoescape: false }),
);
const html = renderMarkdownToHtml(markdown);

// Wrap the rendered markdown html in the wrapper
Expand All @@ -62,7 +69,7 @@ export default class NunjucksMarkdownEngine extends TemplateEngine {
return getTemplateFields(this.loadedTemplate);
}

override async renderPreview(record: CSVRecord) {
override async renderPreview(record: MappedCSVRecord) {
if (!this.loadedTemplate) {
throw new Error("Template not loaded");
}
Expand Down
14 changes: 10 additions & 4 deletions email/libmailmerge/src/engines/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Types shared by all template engines.
* @packageDocumentation
*/
import { CSVRecord } from "../util/types";
import { MappedCSVRecord } from "../util/types";

/**
* Generic "any" type for template engine options: essentially a dictionary.
Expand Down Expand Up @@ -53,7 +53,7 @@ export abstract class TemplateEngine {
* @param record - The record to render the template with, where the keys correspond to the fields extracted in {@link TemplateEngine.extractFields}
* @returns A promise that resolves to an array of {@link TemplatePreviews} objects - check the type for more information
*/
abstract renderPreview(record: CSVRecord): Promise<TemplatePreviews>;
abstract renderPreview(record: MappedCSVRecord): Promise<TemplatePreviews>;

/**
* Given previews generated by {@link TemplateEngine.renderPreview} or this method, re-render a preview.
Expand All @@ -68,14 +68,20 @@ export abstract class TemplateEngine {
*
* **IMPORTANT NOTE**: You _must_ rerender _all_ previews, even if only one was edited. This is a replace-all operations, not a patch.
*/
abstract rerenderPreviews(loadedPreviews: TemplatePreviews, associatedRecord: CSVRecord): Promise<TemplatePreviews>;
abstract rerenderPreviews(
loadedPreviews: TemplatePreviews,
associatedRecord: MappedCSVRecord,
): Promise<TemplatePreviews>;

/**
* Given the (re-)rendered previews, generate the HTML to send.
*
* Most likely this means returning one of the previews, but you may want to combine them or do other operations.
*/
abstract getHTMLToSend(loadedPreviews: TemplatePreviews, associatedRecord: CSVRecord): Promise<string>;
abstract getHTMLToSend(
loadedPreviews: TemplatePreviews,
associatedRecord: MappedCSVRecord,
): Promise<string>;
}

/**
Expand Down
2 changes: 2 additions & 0 deletions email/libmailmerge/src/mailer/defaultMailer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const defaultMailer = (
html: string,
mailer: Mailer,
attachments: Mail.Options["attachments"] = [],
additionalInfo: { cc: EmailString[]; bcc: EmailString[] } = { cc: [], bcc: [] },
): Promise<void> =>
mailer.sendMail(
Mailer.makeFromLineFromEmail(
Expand All @@ -39,4 +40,5 @@ export const defaultMailer = (
subject,
html,
attachments,
additionalInfo,
);
3 changes: 3 additions & 0 deletions email/libmailmerge/src/mailer/mailer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export default class Mailer {
subject: string,
html: string,
attachments: Mail.Options["attachments"] = [],
additionalInfo: { cc: EmailString[]; bcc: EmailString[] } = { cc: [], bcc: [] },
text: string = convert(html),
): Promise<void> {
const info = await this.transporter.sendMail({
Expand All @@ -49,6 +50,8 @@ export default class Mailer {
text: text, // plain text body
html: html, // html body
attachments,
cc: additionalInfo.cc,
bcc: additionalInfo.bcc,
});

logger.debug(
Expand Down
31 changes: 18 additions & 13 deletions email/libmailmerge/src/previews/sidecarData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { join } from "path";
import { TEMPLATE_ENGINES } from "../engines";
import { TemplateEngineOptions, TemplatePreview, TemplatePreviews } from "../engines/types";
import Mailer from "../mailer/mailer";
import { CSVRecord, EmailString } from "../util/types";
import { MappedCSVRecord, EmailString } from "../util/types";
import { SidecarData } from "./types";

const PARTS_SEPARATOR = "__";
Expand All @@ -26,8 +26,8 @@ const logger = createLogger("docsoc.sidecar");
* @returns
*/
export const getRecordPreviewPrefix = (
record: CSVRecord,
fileNamer: (record: CSVRecord) => string,
record: MappedCSVRecord,
fileNamer: (record: MappedCSVRecord) => string,
) => `${fileNamer(record)}`;

/**
Expand All @@ -40,8 +40,8 @@ export const getRecordPreviewPrefix = (
* // => "file_1__nunjucks__preview1.txt"
*/
export const getRecordPreviewPrefixForIndividual = (
record: CSVRecord,
fileNamer: (record: CSVRecord) => string,
record: MappedCSVRecord,
fileNamer: (record: MappedCSVRecord) => string,
templateEngine: string,
preview: TemplatePreview,
) =>
Expand All @@ -58,16 +58,16 @@ export const getRecordPreviewPrefixForIndividual = (
* // => "file_1-metadata.json"
*/
export const getRecordPreviewPrefixForMetadata = (
record: CSVRecord,
fileNamer: (record: CSVRecord) => string,
record: MappedCSVRecord,
fileNamer: (record: MappedCSVRecord) => string,
) => `${getRecordPreviewPrefix(record, fileNamer)}${METADATA_FILE_SUFFIX}`;

type ValidRecordReturn = { valid: false; reason: string } | { valid: true };
/**
* Check a record is valid for use in mailmerge - specifically, that it has a valid email address and a subject.
* @param record __Mapped__ CSV Record to validate
*/
export const validateRecord = (record: CSVRecord): ValidRecordReturn => {
export const validateRecord = (record: MappedCSVRecord): ValidRecordReturn => {
if (!Mailer.validateEmail(record["email"] as string)) {
return {
valid: false,
Expand Down Expand Up @@ -96,9 +96,9 @@ export const validateRecord = (record: CSVRecord): ValidRecordReturn => {
* @returns
*/
export async function writeMetadata(
record: CSVRecord,
record: MappedCSVRecord,
sidecarData: SidecarData,
fileNamer: (record: CSVRecord) => string,
fileNamer: (record: MappedCSVRecord) => string,
previewsRoot: string,
): Promise<void> {
const recordState = validateRecord(record);
Expand All @@ -116,6 +116,9 @@ export async function writeMetadata(
return Promise.resolve();
}

const parseEmailList = (emailList: string | undefined): EmailString[] =>
emailList ? (emailList.split(/\s/) as EmailString[]) : [];

/**
* Generate sidecar metadata for a record & the previews generated from it.
* It is recommended you then store this info alongside the preview, e.g. in a JSON file
Expand All @@ -130,8 +133,8 @@ export async function writeMetadata(
* @returns Sidecar metadata
*/
export function getSidecarMetadata(
fileNamer: (record: CSVRecord) => string,
record: CSVRecord,
fileNamer: (record: MappedCSVRecord) => string,
record: MappedCSVRecord,
templateEngine: TEMPLATE_ENGINES,
templateOptions: TemplateEngineOptions,
attachments: string[],
Expand All @@ -155,7 +158,9 @@ export function getSidecarMetadata(
},
})),
email: {
to: record["email"] as EmailString,
to: parseEmailList(record["email"] as string),
cc: parseEmailList(record["cc"] as string),
bcc: parseEmailList(record["bcc"] as string),
subject: record["subject"] as string,
},
attachments,
Expand Down
8 changes: 5 additions & 3 deletions email/libmailmerge/src/previews/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ENGINES_MAP } from "../engines";
import { TemplateEngineOptions, TemplatePreview } from "../engines/types";
import { CSVRecord, EmailString } from "../util/types";
import { MappedCSVRecord, EmailString } from "../util/types";

/**
* Outputted to JSON files next to rendered template previews, containing metadata about the preview.
Expand All @@ -9,7 +9,7 @@ export interface SidecarData {
/** Name of the template rendered (used for logging) */
name: string;
/** Record associated with the template rendered */
record: CSVRecord;
record: MappedCSVRecord;
/** Engine used */
engine: keyof typeof ENGINES_MAP;
/** Options given to the engine */
Expand All @@ -24,7 +24,9 @@ export interface SidecarData {
}>;
/** Metadata for emailing */
email: {
to: EmailString;
to: EmailString[];
cc: EmailString[];
bcc: EmailString[];
subject: string;
};
/** Array of paths to attachments to include in the final email, relative to the mailmerge workspace root (top level folder) */
Expand Down
98 changes: 0 additions & 98 deletions email/libmailmerge/src/send.ts

This file was deleted.

8 changes: 8 additions & 0 deletions email/libmailmerge/src/util/constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
export const DOCSOC_DEFAULT_FROM_LINE = `"DoCSoc" <[email protected]>`;

/** Expected names of requried email fields in CSVRecords */
export const CSV_DEFAULT_FIELD_NAMES = {
to: "email",
subject: "subject",
cc: "cc",
bcc: "bcc",
};
3 changes: 2 additions & 1 deletion email/libmailmerge/src/util/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export type EmailString = `${string}@${string}`;
export type FromEmail = `"${string}" <${EmailString}>`;

export type CSVRecord = Record<string, unknown>;
export type RawCSVRecord = Record<string, unknown>;
export type MappedCSVRecord = Record<string, unknown>;
Loading

0 comments on commit fdbf127

Please sign in to comment.