fix(email): handle sweego complaints as hard bounces
This commit is contained in:
parent
61984cd1a6
commit
3cc07f5e9f
@ -126,13 +126,13 @@ export class SweegoWebhookService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async processEvent(event: SweegoEvent): Promise<void> {
|
async processEvent(event: SweegoEvent): Promise<void> {
|
||||||
if (event.event_type !== 'soft-bounce' && event.event_type !== 'hard_bounce') {
|
if (event.event_type !== 'soft-bounce' && event.event_type !== 'hard_bounce' && event.event_type !== 'complaint') {
|
||||||
Logger.debug({eventType: event.event_type, recipient: event.recipient}, 'Sweego event received (ignored)');
|
Logger.debug({eventType: event.event_type, recipient: event.recipient}, 'Sweego event received (ignored)');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.event_type === 'hard_bounce') {
|
if (event.event_type === 'hard_bounce' || event.event_type === 'complaint') {
|
||||||
await this.handleHardBounce(event);
|
await this.handleHardBounceOrComplaint(event);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ export class SweegoWebhookService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleHardBounce(event: SweegoEvent): Promise<void> {
|
private async handleHardBounceOrComplaint(event: SweegoEvent): Promise<void> {
|
||||||
Logger.warn(
|
Logger.warn(
|
||||||
{
|
{
|
||||||
recipient: event.recipient,
|
recipient: event.recipient,
|
||||||
@ -150,7 +150,7 @@ export class SweegoWebhookService {
|
|||||||
details: event.details,
|
details: event.details,
|
||||||
eventId: event.event_id,
|
eventId: event.event_id,
|
||||||
},
|
},
|
||||||
'Processing hard bounce - marking email as invalid',
|
'Processing hard bounce or complaint - marking email as invalid',
|
||||||
);
|
);
|
||||||
|
|
||||||
const user = await this.userRepository.findByEmail(event.recipient);
|
const user = await this.userRepository.findByEmail(event.recipient);
|
||||||
|
|||||||
@ -170,6 +170,43 @@ describe('SweegoWebhookService', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('marks complaints as unverified', async () => {
|
||||||
|
const userRepo = createMockUserRepository();
|
||||||
|
const gateway = createMockGatewayService();
|
||||||
|
const service = new SweegoWebhookService(userRepo, gateway);
|
||||||
|
|
||||||
|
const mockUser = {
|
||||||
|
id: BigInt(124),
|
||||||
|
email: 'complaint@example.com',
|
||||||
|
emailBounced: false,
|
||||||
|
emailVerified: true,
|
||||||
|
suspiciousActivityFlags: 0,
|
||||||
|
toRow: () => ({}),
|
||||||
|
};
|
||||||
|
(userRepo.findByEmail as ReturnType<typeof vi.fn>).mockResolvedValue(mockUser);
|
||||||
|
(userRepo.patchUpsert as ReturnType<typeof vi.fn>).mockResolvedValue(null);
|
||||||
|
|
||||||
|
const event: SweegoEvent = {
|
||||||
|
event_type: 'complaint',
|
||||||
|
timestamp: '2026-01-29T14:21:46.729251+00:00',
|
||||||
|
recipient: 'complaint@example.com',
|
||||||
|
event_id: 'event-2',
|
||||||
|
};
|
||||||
|
|
||||||
|
await service.processEvent(event);
|
||||||
|
|
||||||
|
expect(userRepo.findByEmail).toHaveBeenCalledWith('complaint@example.com');
|
||||||
|
expect(userRepo.patchUpsert).toHaveBeenCalledWith(
|
||||||
|
mockUser.id,
|
||||||
|
{
|
||||||
|
email_bounced: true,
|
||||||
|
email_verified: false,
|
||||||
|
suspicious_activity_flags: SuspiciousActivityFlags.REQUIRE_REVERIFIED_EMAIL,
|
||||||
|
},
|
||||||
|
mockUser.toRow(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it('skips already bounced users', async () => {
|
it('skips already bounced users', async () => {
|
||||||
const userRepo = createMockUserRepository();
|
const userRepo = createMockUserRepository();
|
||||||
const gateway = createMockGatewayService();
|
const gateway = createMockGatewayService();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user