Ghost/core/server/services/mega/mega.js

560 lines
20 KiB
JavaScript
Raw Normal View History

const _ = require('lodash');
const Promise = require('bluebird');
const debug = require('@tryghost/debug')('mega');
const tpl = require('@tryghost/tpl');
const url = require('url');
const moment = require('moment');
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
const ObjectID = require('bson-objectid');
const errors = require('@tryghost/errors');
const logging = require('@tryghost/logging');
const settingsCache = require('../../../shared/settings-cache');
const membersService = require('../members');
const limitService = require('../limits');
const bulkEmailService = require('../bulk-email');
const jobsService = require('../jobs');
Store email batch and recipient records when sending newsletters (#12195) requires https://github.com/TryGhost/Ghost/pull/12192 - added initial `EmailBatch` and `EmailRecipient` model definitions with defaults and relationships - added missing `post` relationship function to email model - fetch member list without bookshelf - bookshelf can add around 3x overhead when fetching the members list for an email - we don't need full members at this point, only having the data is fine - if we need full models later on we can push the model hydration into background jobs where recipient batches are fetched ready for an email to be sent - bookshelf model instantiation of many models blocks the event loop, using knex directly keeps concurrent requests fast - adds `getFilteredCollectionQuery` method to base model to facilitate getting a knex query based on our normal model filters along with transaction/forUpdate applied - store recipient list before sending email - chunk already-fetched members list into batches and insert records into the `email_recipients` table via knex - chunked into batches of 1000 to match the number of emails that Mailgun accepts in a single API request but this may not be the absolute fastest batch size for recipient insertion: | Batch size | Batch time | Total time | | ---------- | ---------- | ---------- | | 500 | 20ms | 4142ms | | 1000 | 50ms | 4651ms | | 5000 | 170ms | 3540ms | | 10000 | 370ms | 3684ms | - create an email_batch record before inserting recipient rows so we can effeciently fetch recipients by batch and store the overall batch status
2020-09-14 17:40:00 +03:00
const db = require('../../data/db');
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
const models = require('../../models');
const postEmailSerializer = require('./post-email-serializer');
const {getSegmentsFromHtml} = require('./segment-parser');
// Used to listen to email.added and email.edited model events originally, I think to offload this - ideally would just use jobs now if possible
const events = require('../../lib/common/events');
const messages = {
invalidSegment: 'Invalid segment value. Use one of the valid:"status:free" or "status:-free" values.',
unexpectedFilterError: 'Unexpected {property} value "{value}", expected an NQL equivalent',
noneFilterError: 'Cannot send email to "none" {property}',
emailSendingDisabled: `Email sending is temporarily disabled because your account is currently in review. You should have an email about this from us already, but you can also reach us any time at support@ghost.org`,
sendEmailRequestFailed: 'The email service was unable to send an email batch.'
};
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
const getFromAddress = () => {
let fromAddress = membersService.config.getEmailFromAddress();
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
if (/@localhost$/.test(fromAddress) || /@ghost.local$/.test(fromAddress)) {
const localAddress = 'localhost@example.com';
logging.warn(`Rewriting bulk email from address ${fromAddress} to ${localAddress}`);
fromAddress = localAddress;
}
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
const siteTitle = settingsCache.get('title') ? settingsCache.get('title').replace(/"/g, '\\"') : '';
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
return siteTitle ? `"${siteTitle}"<${fromAddress}>` : fromAddress;
};
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
const getReplyToAddress = () => {
const fromAddress = membersService.config.getEmailFromAddress();
const supportAddress = membersService.config.getEmailSupportAddress();
const replyAddressOption = settingsCache.get('members_reply_address');
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
return (replyAddressOption === 'support') ? supportAddress : fromAddress;
};
/**
*
* @param {Object} postModel - post model instance
* @param {Object} options
* @param {ValidAPIVersion} options.apiVersion - api version to be used when serializing email data
*/
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
const getEmailData = async (postModel, options) => {
const {subject, html, plaintext} = await postEmailSerializer.serialize(postModel, options);
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
return {
subject,
html,
plaintext,
from: getFromAddress(),
replyTo: getReplyToAddress()
};
};
/**
*
* @param {Object} postModel - post model instance
* @param {[string]} toEmails - member email addresses to send email to
* @param {ValidAPIVersion} apiVersion - api version to be used when serializing email data
* @param {ValidMemberSegment} [memberSegment]
*/
const sendTestEmail = async (postModel, toEmails, apiVersion, memberSegment) => {
let emailData = await getEmailData(postModel, {apiVersion});
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
emailData.subject = `[Test] ${emailData.subject}`;
if (memberSegment) {
emailData = postEmailSerializer.renderEmailForSegment(emailData, memberSegment);
}
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
// fetch any matching members so that replacements use expected values
const recipients = await Promise.all(toEmails.map(async (email) => {
const member = await membersService.api.members.get({email});
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
if (member) {
return {
member_uuid: member.get('uuid'),
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
member_email: member.get('email'),
member_name: member.get('name')
};
}
return {
member_email: email
};
}));
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
// enable tracking for previews to match real-world behaviour
emailData.track_opens = !!settingsCache.get('email_track_opens');
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
const response = await bulkEmailService.send(emailData, recipients);
if (response instanceof bulkEmailService.FailedBatch) {
return Promise.reject(response.error);
}
if (response && response[0] && response[0].error) {
return Promise.reject(new errors.EmailError({
statusCode: response[0].error.statusCode,
message: response[0].error.message,
context: response[0].error.originalMessage
}));
}
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
return response;
2019-11-05 12:09:07 +03:00
};
/**
* transformRecipientFilter
*
* Accepts a filter string, errors on unexpected legacy filter syntax and enforces subscribed:true
*
* @param {string} emailRecipientFilter NQL filter for members
* @param {object} options
*/
const transformEmailRecipientFilter = (emailRecipientFilter, {errorProperty = 'email_recipient_filter'} = {}) => {
switch (emailRecipientFilter) {
// `paid` and `free` were swapped out for NQL filters in 4.5.0, we shouldn't see them here now
case 'paid':
case 'free':
throw new errors.GhostError({
message: tpl(messages.unexpectedFilterError, {
property: errorProperty,
value: emailRecipientFilter
})
});
case 'all':
return 'subscribed:true';
case 'none':
throw new errors.GhostError({
message: tpl(messages.noneFilterError, {
property: errorProperty
})
});
default:
return `subscribed:true+(${emailRecipientFilter})`;
}
};
2019-11-07 07:10:36 +03:00
/**
* addEmail
*
* Accepts a post model and creates an email record based on it. Only creates one
2019-11-07 07:10:36 +03:00
* record per post
*
* @param {object} postModel Post Model Object
* @param {object} options
* @param {ValidAPIVersion} options.apiVersion - api version to be used when serializing email data
2019-11-07 07:10:36 +03:00
*/
const addEmail = async (postModel, options) => {
if (limitService.isLimited('emails')) {
await limitService.errorIfWouldGoOverLimit('emails');
}
if (settingsCache.get('email_verification_required') === true) {
throw new errors.HostLimitError({
message: tpl(messages.emailSendingDisabled)
});
}
const knexOptions = _.pick(options, ['transacting', 'forUpdate']);
const filterOptions = Object.assign({}, knexOptions, {limit: 1});
Updated newsletter functionality to use `email_recipient_filter` (#12343) no-issue * Used email_recipient_filter in MEGA This officially decouples the newsletter recipients from the post visibility allowing us to send emails to free members only * Supported enum for send_email_when_published in model This allows us to migrate from the previously used boolean to an enum when we eventually rename the email_recipient_filter column to send_email_when_published * Updated the posts API to handle email_recipient_filter We now no longer rely on the send_email_when_published property to send newsletters, meaning we can remove the column and start cleaning up the new columns name * Handled draft status changes when emails not sent We want to reset any concept of sending an email when a post is transition to the draft status, if and only if, and email has not already been sent. If an email has been sent, we should leave the email related fields as they were. * Removed send_email_when_published from add method This is not supported at the model layer * Removed email_recipient_filter from v2&Content API This should not be exposed on previous api versions, or publicly * Removed reference to send_email_when_published This allows us to move completely to the email_recipient_filter property, keeping the code clean and allowing us to delete the send_email_when_published column in the database. We plan to then migrate _back_ to the send_email_when_published name at both the database and api level.
2020-11-06 20:32:23 +03:00
const emailRecipientFilter = postModel.get('email_recipient_filter');
filterOptions.filter = transformEmailRecipientFilter(emailRecipientFilter, {errorProperty: 'email_recipient_filter'});
const startRetrieve = Date.now();
debug('addEmail: retrieving members count');
const {meta: {pagination: {total: membersCount}}} = await membersService.api.members.list(Object.assign({}, knexOptions, filterOptions));
debug(`addEmail: retrieved members count - ${membersCount} members (${Date.now() - startRetrieve}ms)`);
// NOTE: don't create email object when there's nobody to send the email to
if (membersCount === 0) {
return null;
}
if (limitService.isLimited('emails')) {
await limitService.errorIfWouldGoOverLimit('emails', {addedCount: membersCount});
}
const postId = postModel.get('id');
const existing = await models.Email.findOne({post_id: postId}, knexOptions);
if (!existing) {
// get email contents and perform replacements using no member data so
// we have a decent snapshot of email content for later display
const emailData = await getEmailData(postModel, options);
return models.Email.add({
post_id: postId,
status: 'pending',
email_count: membersCount,
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
subject: emailData.subject,
from: emailData.from,
reply_to: emailData.replyTo,
html: emailData.html,
plaintext: emailData.plaintext,
submitted_at: moment().toDate(),
track_opens: !!settingsCache.get('email_track_opens'),
Updated newsletter functionality to use `email_recipient_filter` (#12343) no-issue * Used email_recipient_filter in MEGA This officially decouples the newsletter recipients from the post visibility allowing us to send emails to free members only * Supported enum for send_email_when_published in model This allows us to migrate from the previously used boolean to an enum when we eventually rename the email_recipient_filter column to send_email_when_published * Updated the posts API to handle email_recipient_filter We now no longer rely on the send_email_when_published property to send newsletters, meaning we can remove the column and start cleaning up the new columns name * Handled draft status changes when emails not sent We want to reset any concept of sending an email when a post is transition to the draft status, if and only if, and email has not already been sent. If an email has been sent, we should leave the email related fields as they were. * Removed send_email_when_published from add method This is not supported at the model layer * Removed email_recipient_filter from v2&Content API This should not be exposed on previous api versions, or publicly * Removed reference to send_email_when_published This allows us to move completely to the email_recipient_filter property, keeping the code clean and allowing us to delete the send_email_when_published column in the database. We plan to then migrate _back_ to the send_email_when_published name at both the database and api level.
2020-11-06 20:32:23 +03:00
recipient_filter: emailRecipientFilter
}, knexOptions);
} else {
return existing;
}
};
/**
* retryFailedEmail
*
* Accepts an Email model and resets it's fields to trigger retry listeners
*
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
* @param {Email} emailModel Email model
*/
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
const retryFailedEmail = async (emailModel) => {
return await models.Email.edit({
status: 'pending'
}, {
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
id: emailModel.get('id')
});
};
/**
* handleUnsubscribeRequest
*
* Takes a request/response pair and reads the `unsubscribe` query parameter,
* using the content to update the members service to set the `subscribed` flag
* to false on the member
*
* If any operation fails, or the request is invalid the function will error - so using
* as middleware should consider wrapping with `try/catch`
*
* @param {Request} req
* @returns {Promise<void>}
*/
async function handleUnsubscribeRequest(req) {
if (!req.url) {
throw new errors.BadRequestError({
2019-11-26 13:02:53 +03:00
message: 'Unsubscribe failed! Could not find member'
});
}
const {query} = url.parse(req.url, true);
if (!query || !query.uuid) {
throw new errors.BadRequestError({
2019-11-26 13:02:53 +03:00
message: (query.preview ? 'Unsubscribe preview' : 'Unsubscribe failed! Could not find member')
});
}
const member = await membersService.api.members.get({
uuid: query.uuid
});
if (!member) {
throw new errors.BadRequestError({
2019-11-26 13:02:53 +03:00
message: 'Unsubscribe failed! Could not find member'
});
}
try {
const memberModel = await membersService.api.members.update({subscribed: false}, {id: member.id});
return memberModel.toJSON();
} catch (err) {
throw new errors.InternalServerError({
err,
message: 'Failed to unsubscribe member'
});
}
}
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
async function pendingEmailHandler(emailModel, options) {
// CASE: do not send email if we import a database
// TODO: refactor post.published events to never fire on importing
if (options && options.importing) {
return;
}
if (emailModel.get('status') !== 'pending') {
return;
}
// make sure recurring background analytics jobs are running once we have emails
const emailAnalyticsJobs = require('../email-analytics/jobs');
emailAnalyticsJobs.scheduleRecurringJobs();
return jobsService.addJob({
job: sendEmailJob,
data: {emailModel},
offloaded: false
});
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
}
async function sendEmailJob({emailModel, options}) {
let startEmailSend = null;
try {
// Check host limit for allowed member count and throw error if over limit
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
// - do this even if it's a retry so that there's no way around the limit
if (limitService.isLimited('members')) {
await limitService.errorIfIsOverLimit('members');
}
// Check host limit for disabled emails or going over emails limit
if (limitService.isLimited('emails')) {
await limitService.errorIfWouldGoOverLimit('emails');
}
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
// Create email batch and recipient rows unless this is a retry and they already exist
const existingBatchCount = await emailModel.related('emailBatches').count('id');
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
if (existingBatchCount === 0) {
let newBatchCount;
await models.Base.transaction(async (transacting) => {
newBatchCount = await createSegmentedEmailBatches({emailModel, options: {transacting}});
});
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
if (newBatchCount === 0) {
return;
}
}
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
debug('sendEmailJob: sending email');
startEmailSend = Date.now();
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
await bulkEmailService.processEmail({emailId: emailModel.get('id'), options});
debug(`sendEmailJob: sent email (${Date.now() - startEmailSend}ms)`);
} catch (error) {
if (startEmailSend) {
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
debug(`sendEmailJob: send email failed (${Date.now() - startEmailSend}ms)`);
}
let errorMessage = error.message;
if (errorMessage.length > 2000) {
errorMessage = errorMessage.substring(0, 2000);
}
await emailModel.save({
status: 'failed',
error: errorMessage
}, {patch: true});
throw new errors.GhostError({
err: error,
context: tpl(messages.sendEmailRequestFailed)
});
}
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
}
/**
* Fetch rows of members that should receive an email.
* Uses knex directly rather than bookshelf to avoid thousands of bookshelf model
* instantiations and associated processing and event loop blocking
*
* @param {Object} options
* @param {Object} options.emailModel - instance of Email model
* @param {string} [options.memberSegment] - NQL filter to apply in addition to the one defined in emailModel
* @param {Object} options.options - knex options
*
* @returns {Promise<Object[]>} instances of filtered knex member rows
*/
async function getEmailMemberRows({emailModel, memberSegment, options}) {
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
const knexOptions = _.pick(options, ['transacting', 'forUpdate']);
const filterOptions = Object.assign({}, knexOptions);
const recipientFilter = transformEmailRecipientFilter(emailModel.get('recipient_filter'), {errorProperty: 'recipient_filter'});
filterOptions.filter = recipientFilter;
if (memberSegment) {
filterOptions.filter = `${filterOptions.filter}+${memberSegment}`;
}
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
const startRetrieve = Date.now();
debug('getEmailMemberRows: retrieving members list');
// select('members.*') is necessary here to avoid duplicate `email` columns in the result set
// without it we do `select *` which pulls in the Stripe customer email too which overrides the member email
const memberRows = await models.Member.getFilteredCollectionQuery(filterOptions).select('members.*').distinct();
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
debug(`getEmailMemberRows: retrieved members list - ${memberRows.length} members (${Date.now() - startRetrieve}ms)`);
return memberRows;
}
/**
* Partitions array of member records according to the segment they belong to
*
* @param {Object[]} memberRows raw member rows to partition
* @param {string[]} segments segment filters to partition batches by
*
* @returns {Object} partitioned memberRows with keys that correspond segment names
*/
function partitionMembersBySegment(memberRows, segments) {
const partitions = {};
for (const memberSegment of segments) {
let segmentedMemberRows;
// NOTE: because we only support two types of segments at the moment the logic was kept dead simple
// in the future this segmentation should probably be substituted with NQL:
// memberRows.filter(member => nql(memberSegment).queryJSON(member));
if (memberSegment === 'status:free') {
segmentedMemberRows = memberRows.filter(member => member.status === 'free');
memberRows = memberRows.filter(member => member.status !== 'free');
} else if (memberSegment === 'status:-free') {
segmentedMemberRows = memberRows.filter(member => member.status !== 'free');
memberRows = memberRows.filter(member => member.status === 'free');
} else {
throw new errors.ValidationError(tpl(messages.invalidSegment));
}
partitions[memberSegment] = segmentedMemberRows;
}
if (memberRows.length) {
partitions.unsegmented = memberRows;
}
return partitions;
}
/**
* Detects segment filters in emailModel's html and creates separate batches per segment
*
* @param {Object} options
* @param {Object} options.emailModel - instance of Email model
* @param {Object} options.options - knex options
*/
async function createSegmentedEmailBatches({emailModel, options}) {
let memberRows = await getEmailMemberRows({emailModel, options});
if (!memberRows.length) {
return [];
}
const segments = getSegmentsFromHtml(emailModel.get('html'));
const batchIds = [];
if (segments.length) {
const partitionedMembers = partitionMembersBySegment(memberRows, segments);
for (const partition in partitionedMembers) {
const emailBatchIds = await createEmailBatches({
emailModel,
memberRows: partitionedMembers[partition],
memberSegment: partition === 'unsegmented' ? null : partition,
options
});
batchIds.push(emailBatchIds);
}
} else {
const emailBatchIds = await createEmailBatches({emailModel, memberRows, options});
batchIds.push(emailBatchIds);
}
return batchIds;
}
/**
* Store email_batch and email_recipient records for an email.
* Uses knex directly rather than bookshelf to avoid thousands of bookshelf model
* instantiations and associated processing and event loop blocking.
*
* @param {Object} options
* @param {Object} options.emailModel - instance of Email model
* @param {string} [options.memberSegment] - NQL filter to apply in addition to the one defined in emailModel
* @param {Object[]} [options.memberRows] - member rows to be batched
* @param {Object} options.options - knex options
* @returns {Promise<string[]>} - created batch ids
*/
async function createEmailBatches({emailModel, memberRows, memberSegment, options}) {
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
const storeRecipientBatch = async function (recipients) {
const knexOptions = _.pick(options, ['transacting', 'forUpdate']);
const batchModel = await models.EmailBatch.add({
email_id: emailModel.id,
member_segment: memberSegment
}, knexOptions);
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
const recipientData = [];
recipients.forEach((memberRow) => {
if (!memberRow.id || !memberRow.uuid || !memberRow.email) {
logging.warn(`Member row not included as email recipient due to missing data - id: ${memberRow.id}, uuid: ${memberRow.uuid}, email: ${memberRow.email}`);
return;
}
recipientData.push({
id: ObjectID().toHexString(),
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
email_id: emailModel.id,
member_id: memberRow.id,
batch_id: batchModel.id,
member_uuid: memberRow.uuid,
member_email: memberRow.email,
member_name: memberRow.name
});
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
});
const insertQuery = db.knex('email_recipients').insert(recipientData);
if (knexOptions.transacting) {
insertQuery.transacting(knexOptions.transacting);
}
await insertQuery;
Refactor mega service to use stored email content and batch/recipient records no issue - store raw content in email record - keep any replacement strings in the html/plaintext content so that it can be used when sending email rather than needing to re-serialize the post content which may have changed - split post email serializer into separate serialization and replacement parsing functions - serialization now returns any email content that is derived from the post content (subject/html/plaintext) rather than post content plus replacements - `parseReplacements` has been split out so that it can be run against email content rather than a post, this allows mega and the email preview service to work with the stored email content - move mailgun-specific functionality into the mailgun provider - previously mailgun-specific behaviour was spread across the post email serializer, mega, and bulk-email service - the per-batch `send` functionality was moved from the `bulk-email` service to the mailgun provider and updated to take email content, recipient info, and replacement info so that all mailgun-specific headers and replacement formatting can be handled in one place - exposes the `BATCH_SIZE` constant because batch sizes are limited to what the provider allows - `bulk-email` service split into three methods - `send` responsible for taking email content and recipients, parsing replacement info from the email content and using that to collate a recipient data object, and finally coordinating the send via the mailgun provider. Usable directly for use-cases such as test emails - `processEmail` takes an email ID, loads it and coordinates sending related batches concurrently - `processEmailBatch` takes an email_batch ID, loads it along with associated email_recipient records and passes the data through to the `send` function, updating the batch status as it's processed - `processEmail` and `processEmailBatch` take IDs rather than objects ready for future use by job-queues, it's best to keep job parameters as minimal as possible - refactored `mega` service - modified `getEmailData` to collate email content (from/reply-to/subject/html/plaintext) rather than being responsible for dealing with replacements and mailgun-specific replacement formats - used for generating email content before storing in the email table, and when sending test emails - from/reply-to calculation moved out of the post-email-serializer into mega and extracted into separate functions used by `getEmailData` - `sendTestEmail` updated to generate `EmailRecipient`-like objects for each email address so that appropriate data can be supplied to the updated `bulk-email.send` method - `sendEmailJob` updated to create `email_batches` and associated `email_recipients` records then hand over processing to the `bulk-email` service - member row fetching extracted into a separate function and used by `createEmailBatches` - moved updating of email status from `mega` to the `bulk-email` service, keeps concept of Successful/FailedBatch internal to the `bulk-email` service
2020-09-24 11:35:29 +03:00
return batchModel.id;
};
debug('createEmailBatches: storing recipient list');
const startOfRecipientStorage = Date.now();
const batches = _.chunk(memberRows, bulkEmailService.BATCH_SIZE);
const batchIds = await Promise.mapSeries(batches, storeRecipientBatch);
debug(`createEmailBatches: stored recipient list (${Date.now() - startOfRecipientStorage}ms)`);
return batchIds;
}
const statusChangedHandler = (emailModel, options) => {
const emailRetried = emailModel.wasChanged()
&& emailModel.get('status') === 'pending'
&& emailModel.previous('status') === 'failed';
if (emailRetried) {
pendingEmailHandler(emailModel, options);
}
};
function listen() {
events.on('email.added', pendingEmailHandler);
events.on('email.edited', statusChangedHandler);
}
// Public API
module.exports = {
2019-11-05 12:09:07 +03:00
listen,
addEmail,
retryFailedEmail,
sendTestEmail,
handleUnsubscribeRequest,
// NOTE: below are only exposed for testing purposes
_transformEmailRecipientFilter: transformEmailRecipientFilter,
_partitionMembersBySegment: partitionMembersBySegment,
_getEmailMemberRows: getEmailMemberRows
};
/**
* @typedef {'v2' | 'v3' | 'v4' | 'canary' } ValidAPIVersion
* @typedef {'status:free' | 'status:-free'} ValidMemberSegment
*/