Hooked mega service to listen to email.added event

- This was needed because we switched to synchronous request handling (to allow including email data with post.publish event)
This commit is contained in:
Nazar Gargol 2019-11-06 18:32:43 +07:00 committed by Naz Gargol
parent 4e1caa8b08
commit eca129c18d
3 changed files with 63 additions and 41 deletions

View File

@ -1,6 +1,7 @@
const models = require('../../models'); const models = require('../../models');
const common = require('../../lib/common'); const common = require('../../lib/common');
const urlUtils = require('../../lib/url-utils'); const urlUtils = require('../../lib/url-utils');
const {mega} = require('../../services/mega');
const allowedIncludes = ['tags', 'authors', 'authors.roles', 'email']; const allowedIncludes = ['tags', 'authors', 'authors.roles', 'email'];
const unsafeAttrs = ['status', 'authors', 'visibility']; const unsafeAttrs = ['status', 'authors', 'visibility'];
@ -141,6 +142,24 @@ module.exports = {
}, },
query(frame) { query(frame) {
return models.Post.edit(frame.data.posts[0], frame.options) return models.Post.edit(frame.data.posts[0], frame.options)
.then(async (model) => {
if (!model.get('send_email_when_published')) {
return model;
}
if (!model.get('email') && (model.get('status') === 'published') && model.wasChanged()) {
const email = await mega.addEmail(model.toJSON());
if (frame.options.include && frame.options.includes('email')) {
model.set('email', email);
}
return model;
} else {
return model;
}
})
.then((model) => { .then((model) => {
if ( if (
model.get('status') === 'published' && model.wasChanged() || model.get('status') === 'published' && model.wasChanged() ||

View File

@ -386,7 +386,7 @@ module.exports = {
maxlength: 50, maxlength: 50,
nullable: false, nullable: false,
defaultTo: 'sending', defaultTo: 'sending',
validations: {isIn: [['sending', 'sent', 'failed']]} validations: {isIn: [['pending', 'sending', 'sent', 'failed']]}
}, },
error: {type: 'string', maxlength: 2000, nullable: true}, error: {type: 'string', maxlength: 2000, nullable: true},
stats: {type: 'text', maxlength: 65535, nullable: true}, stats: {type: 'text', maxlength: 65535, nullable: true},

View File

@ -8,7 +8,9 @@ const models = require('../../models');
const postEmailSerializer = require('./post-email-serializer'); const postEmailSerializer = require('./post-email-serializer');
const urlUtils = require('../../lib/url-utils'); const urlUtils = require('../../lib/url-utils');
const sendEmail = async (post, members) => { const internalContext = {context: {internal: true}};
const getEmailData = (post, members) => {
const emailTmpl = postEmailSerializer.serialize(post); const emailTmpl = postEmailSerializer.serialize(post);
const membersToSendTo = members.filter((member) => { const membersToSendTo = members.filter((member) => {
@ -23,8 +25,13 @@ const sendEmail = async (post, members) => {
}, emailData); }, emailData);
}, {}); }, {});
return bulkEmailService.send(emailTmpl, emails, emailData) return {emailTmpl, emails, emailData};
.then(() => ({emailTmpl, emails})); };
const sendEmail = async (post, members) => {
const {emailTmpl, emails, emailData} = getEmailData(post, members);
return bulkEmailService.send(emailTmpl, emails, emailData);
}; };
const sendTestEmail = async (post, emails) => { const sendTestEmail = async (post, emails) => {
@ -33,6 +40,27 @@ const sendTestEmail = async (post, emails) => {
return bulkEmailService.send(emailTmpl, emails); return bulkEmailService.send(emailTmpl, emails);
}; };
const addEmail = async (post) => {
const {members} = await membersService.api.members.list(Object.assign({filter: 'subscribed:true'}, {limit: 'all'}));
const {emailTmpl, emails} = getEmailData(post, members);
const existing = await models.Email.findOne({post_id: post.id}, internalContext);
if (!existing) {
return models.Email.add({
post_id: post.id,
status: 'pending',
email_count: emails.length,
subject: emailTmpl.subject,
html: emailTmpl.html,
plaintext: emailTmpl.plaintext,
submitted_at: moment().toDate()
}, internalContext);
} else {
return existing;
}
};
// NOTE: serialization is needed to make sure we are using current API and do post transformations // NOTE: serialization is needed to make sure we are using current API and do post transformations
// such as image URL transformation from relative to absolute // such as image URL transformation from relative to absolute
const serialize = async (model) => { const serialize = async (model) => {
@ -111,24 +139,18 @@ async function handleUnsubscribeRequest(req) {
} }
} }
async function listener(model, options) { async function listener(emailModel, options) {
// CASE: do not send email if we import a database // CASE: do not send email if we import a database
// TODO: refactor post.published events to never fire on importing // TODO: refactor post.published events to never fire on importing
if (options && options.importing) { if (options && options.importing) {
return; return;
} }
if (!model.get('send_email_when_published')) { const postModel = await models.Post.findOne({id: emailModel.get('post_id')}, internalContext);
return;
}
const post = await serialize(model); const post = await serialize(postModel);
const deliveredEvents = await models.Action.findAll({ if (emailModel.get('status') !== 'pending') {
filter: `event:delivered+resource_id:${model.id}`
});
if (deliveredEvents && deliveredEvents.toJSON().length > 0) {
return; return;
} }
@ -139,43 +161,24 @@ async function listener(model, options) {
} }
sendEmail(post, members) sendEmail(post, members)
.then(async ({emailTmpl, emails}) => {
return models.Email.add({
post_id: post.id,
status: 'sent',
email_count: emails.length,
subject: emailTmpl.subject,
html: emailTmpl.html,
plaintext: emailTmpl.plaintext,
submitted_at: moment().toDate()
}, {context: {internal: true}});
})
.then(async () => { .then(async () => {
let actor = {id: null, type: null}; return models.Email.edit({
if (options.context && options.context.user) { status: 'sent'
actor = { }, {
id: options.context.user, id: emailModel.id,
type: 'user' context: {internal: true}
}; });
}
const action = {
event: 'delivered',
resource_id: model.id,
resource_type: 'post',
actor_id: actor.id,
actor_type: actor.type
};
return models.Action.add(action, {context: {internal: true}});
}); });
} }
function listen() { function listen() {
common.events.on('post.published', listener); common.events.on('email.added', listener);
} }
// Public API // Public API
module.exports = { module.exports = {
listen, listen,
addEmail,
sendTestEmail, sendTestEmail,
handleUnsubscribeRequest, handleUnsubscribeRequest,
createUnsubscribeUrl createUnsubscribeUrl