Ghost/ghost/email-suppression-list/lib/email-suppression-list.js
Fabien "egg" O'Carroll 9736d942e1 Unsubscribed Members from newsletters when their email is suppressed
refs https://github.com/TryGhost/Team/issues/2367

This ensures that a Member is not considered subscribed to any emails, so that
counts for newsletter recipients are correct. Eventually we will filter members
on their email suppression status but this is not implemented yet.
2022-12-08 13:02:36 +07:00

125 lines
2.7 KiB
JavaScript

const assert = require('assert');
/**
* @typedef {object} EmailSuppressionInfo
* @prop {'spam' | 'failed'} reason
* @prop {Date} timestamp
*/
/**
* @typedef {object} EmailSuppressedData
* @prop {true} suppressed
* @prop {EmailSuppressionInfo} info
*/
/**
* @typedef {object} EmailNotSuppressedData
* @prop {false} suppressed
* @prop {null} info
*/
/**
* @typedef {EmailSuppressedData | EmailNotSuppressedData} IEmailSuppressionData
*/
/**
* @typedef {object} IEmailSuppressionList
* @prop {(email: string) => Promise<EmailSuppressionData>} getSuppressionData
* @prop {(emails: string[]) => Promise<EmailSuppressionData[]>} getBulkSuppressionData
* @prop {(email: string) => Promise<boolean>} removeEmail
*/
/**
* @implements {IEmailSuppressionData}
*/
class EmailSuppressionData {
/** @type {boolean} */
suppressed;
/** @type {EmailSuppressionInfo | null} */
info;
constructor(suppressed, info) {
if (!suppressed) {
this.suppressed = false;
this.info = null;
} else {
this.suppressed = true;
assert(info.reason === 'spam' || info.reason === 'fail');
assert(info.timestamp instanceof Date);
this.info = {
reason: info.reason,
timestamp: info.timestamp
};
}
}
}
/**
* @abstract
* @implements {IEmailSuppressionList}
*/
class AbstractEmailSuppressionList {
/**
* @param {string} email
* @returns {Promise<boolean>}
*/
async removeEmail(email) { // eslint-disable-line
return Promise.reject();
}
/**
* @param {string} email
* @returns {Promise<EmailSuppressionData>}
*/
async getSuppressionData(email) { // eslint-disable-line
return Promise.reject();
}
/**
* @param {string[]} emails
* @returns {Promise<EmailSuppressionData[]>}
*/
async getBulkSuppressionData(emails) {
return Promise.all(emails.map(email => this.getSuppressionData(email)));
}
}
class EmailSuppressedEvent {
/**
* @readonly
* @type {{emailId: string, emailAddress: string, reason: string}}
*/
data;
/**
* @readonly
* @type {Date}
*/
timestamp;
/**
* @private
*/
constructor({emailAddress, emailId, reason, timestamp}) {
this.data = {
emailAddress,
emailId,
reason
};
this.timestamp = timestamp;
}
static create(data, timestamp) {
return new EmailSuppressedEvent({
...data,
timestamp: timestamp || new Date
});
}
}
module.exports = {
AbstractEmailSuppressionList,
EmailSuppressionData,
EmailSuppressedEvent
};