import { Injectable, OnModuleInit } from '@nestjs/common' import { MailDataRequired, MailService as SendgridLibMailService } from '@sendgrid/mail' import { debug, Debugger } from 'debug' import { get, isEmpty, isNil } from 'lodash' import { nanoid } from 'nanoid' import { SendgridConfig } from '../../typings/global' import { MailService, MailerSendOptions } from './mail.service' export const SENDGRID_DISABLED_MESSAGE = 'Message has not be sent: SendGrid is disabled!' type SendOptions = MailerSendOptions & { templateId: string from: string sender: string } @Injectable() export class SendgridMailService extends MailService implements OnModuleInit { private debug: Debugger = debug(`http:${this.constructor.name}`) private _settings: SendgridConfig private _sendgridLibMailService: SendgridLibMailService onModuleInit(): void { this._settings = globalThis.secret.sendgrid_settings as SendgridConfig this._sendgridLibMailService = new SendgridLibMailService() this._sendgridLibMailService.setApiKey(this._settings.key) } get enabled(): boolean { return this._settings.enabled } async send(options: MailerSendOptions): Promise { try { const args = this.extract(options) return await this.sendMessage(args) } catch (error) { const bodyErrors = get(error, 'response.body.errors', null)?.reduce( (previous: string, current: { message: string }) => `${previous}${!isEmpty(previous) ? ` | ${current.message}` : current.message}`, '', ) throw new Error(`Error sending mail to '${JSON.stringify(options)}': ${bodyErrors || error.message}`) } } private async sendMessage({ to, substitutions, dynamicTemplateData, templateId, from, sendAt, attachments, }: SendOptions): Promise { if (!this.enabled) { return this.disabledMessage(SENDGRID_DISABLED_MESSAGE) } const args: MailDataRequired = { dynamicTemplateData, substitutions, templateId, from, to, } if (attachments) args.attachments = attachments.map(attachment => ({ content: Buffer.isBuffer(attachment.content) ? attachment.content.toString('base64') : attachment.content, filename: attachment.filename, type: attachment.type, disposition: 'attachment', })) if (!isNil(sendAt)) args.sendAt = Math.round(+sendAt / 1000) const id = nanoid() this.debug.extend('request')(JSON.stringify({ id, type: 'publish_sns', body: { ...args } })) const response = this._sendgridLibMailService.send(args) this.debug.extend('response')(JSON.stringify({ id, response })) return response } private extract = (options: MailerSendOptions): SendOptions => { const { to, substitutions, dynamicTemplateData, templateId } = options const { sender } = this._settings return { to, substitutions, sender, from: sender, templateId, dynamicTemplateData, sendAt: options.sendAt, attachments: options.attachments, } } private disabledMessage = (result: string): { result: string } => { return { result, } } }