Wired up storage of EmailSpamComplaintEvents

refs https://github.com/TryGhost/Team/issues/2337

This also removes the code which unsubscribes members from newsletters because
that is not the spec of the feature.
This commit is contained in:
Fabien "egg" O'Carroll 2022-11-30 18:50:30 +07:00
parent 9c691f3ea9
commit f9fdee1b28
3 changed files with 23 additions and 12 deletions

View File

@ -14,7 +14,7 @@ class EmailServiceWrapper {
} }
const {EmailService, EmailController, EmailRenderer, SendingService, BatchSendingService, EmailSegmenter, EmailEventStorage, MailgunEmailProvider} = require('@tryghost/email-service'); const {EmailService, EmailController, EmailRenderer, SendingService, BatchSendingService, EmailSegmenter, EmailEventStorage, MailgunEmailProvider} = require('@tryghost/email-service');
const {Post, Newsletter, Email, EmailBatch, EmailRecipient, Member, EmailRecipientFailure} = require('../../models'); const {Post, Newsletter, Email, EmailBatch, EmailRecipient, Member, EmailRecipientFailure, EmailSpamComplaintEvent} = require('../../models');
const MailgunClient = require('@tryghost/mailgun-client'); const MailgunClient = require('@tryghost/mailgun-client');
const configService = require('../../../shared/config'); const configService = require('../../../shared/config');
const settingsCache = require('../../../shared/settings-cache'); const settingsCache = require('../../../shared/settings-cache');
@ -114,7 +114,8 @@ class EmailServiceWrapper {
db, db,
membersRepository, membersRepository,
models: { models: {
EmailRecipientFailure EmailRecipientFailure,
EmailSpamComplaintEvent
} }
}); });
this.eventStorage.listen(domainEvents); this.eventStorage.listen(domainEvents);

View File

@ -767,10 +767,13 @@ describe('EmailEventStorage', function () {
const memberId = emailRecipient.member_id; const memberId = emailRecipient.member_id;
const providerId = emailBatch.provider_id; const providerId = emailBatch.provider_id;
const timestamp = new Date(2000, 0, 1); const timestamp = new Date(2000, 0, 1);
const eventsURI = '/members/events/?' + encodeURIComponent(
`filter=type:-[comment_event,aggregated_click_event]+data.member_id:${memberId}`
);
// Check not unsubscribed // Check not unsubscribed
const memberInitial = await membersService.api.members.get({id: memberId}, {withRelated: ['newsletters']}); const {body: {events: [notSpamEvent]}} = await agent.get(eventsURI);
assert.notEqual(memberInitial.related('newsletters').length, 0, 'This test requires a member that is subscribed to at least one newsletter'); assert.notEqual(notSpamEvent.type, 'email_complaint_event', 'This test requires a member that does not have a spam event');
events = [{ events = [{
event: 'complained', event: 'complained',
@ -799,9 +802,9 @@ describe('EmailEventStorage', function () {
// Now wait for events processed // Now wait for events processed
await sleep(200); await sleep(200);
// Check if unsubscribed // Check if event exists
const member = await membersService.api.members.get({id: memberId}, {withRelated: ['newsletters']}); const {body: {events: [spamComplaintEvent]}} = await agent.get(eventsURI);
assert.equal(member.related('newsletters').length, 0); assert.equal(spamComplaintEvent.type, 'email_complaint_event');
}); });
it('Can handle unsubscribe events', async function () { it('Can handle unsubscribe events', async function () {

View File

@ -13,6 +13,9 @@ class EmailEventStorage {
this.#membersRepository = membersRepository; this.#membersRepository = membersRepository;
} }
/**
* @param {import('@tryghost/domain-events')} domainEvents
*/
listen(domainEvents) { listen(domainEvents) {
domainEvents.subscribe(EmailDeliveredEvent, async (event) => { domainEvents.subscribe(EmailDeliveredEvent, async (event) => {
try { try {
@ -100,9 +103,9 @@ class EmailEventStorage {
/** /**
* @private * @private
* @param {'temporary'|'permanent'} severity * @param {'temporary'|'permanent'} severity
* @param {import('@tryghost/email-events').EmailTemporaryBouncedEvent|import('@tryghost/email-events').EmailBouncedEvent} event * @param {import('@tryghost/email-events').EmailTemporaryBouncedEvent|import('@tryghost/email-events').EmailBouncedEvent} event
* @param {{transacting?: any}} options * @param {{transacting?: any}} options
* @returns * @returns
*/ */
async saveFailure(severity, event, options = {}) { async saveFailure(severity, event, options = {}) {
if (!event.error) { if (!event.error) {
@ -144,7 +147,7 @@ class EmailEventStorage {
/// We can get events out of order, so only save the last one /// We can get events out of order, so only save the last one
return; return;
} }
// Update the existing failure // Update the existing failure
await existing.save({ await existing.save({
severity, severity,
@ -162,7 +165,11 @@ class EmailEventStorage {
} }
async handleComplained(event) { async handleComplained(event) {
return this.unsubscribeFromNewsletters(event); await this.#models.EmailSpamComplaintEvent.add({
member_id: event.memberId,
email_id: event.emailId,
email_address: event.email
});
} }
async unsubscribeFromNewsletters(event) { async unsubscribeFromNewsletters(event) {