Skip to content

Commit ce5d553

Browse files
committed
Track complaint vs delivery failure independently
1 parent d6433c3 commit ce5d553

File tree

4 files changed

+34
-7
lines changed

4 files changed

+34
-7
lines changed

tests/unit/email/ses/test_models.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ def test_can_deliver(self, db_session):
3434
status.deliver()
3535

3636
assert status.save().status == "Delivered"
37+
assert not email.is_having_delivery_issues
38+
assert not email.spam_complaint
3739

3840
def test_delivery_resets_transient_bounces(self, db_session):
3941
email = EmailFactory.create(transient_bounces=3)
@@ -66,6 +68,7 @@ def test_soft_bounced_unverifies_when_going_over(self, db_session):
6668
assert email.transient_bounces == MAX_TRANSIENT_BOUNCES + 1
6769
assert not email.verified
6870
assert email.is_having_delivery_issues
71+
assert not email.spam_complaint
6972

7073
def test_soft_bounce_after_delivery_does_nothing(self, db_session):
7174
email = EmailFactory.create(transient_bounces=3)
@@ -88,6 +91,7 @@ def test_hard_bounce_unverifies_email(self, db_session):
8891
assert status.save().status == "Bounced"
8992
assert not email.verified
9093
assert email.is_having_delivery_issues
94+
assert not email.spam_complaint
9195

9296
def test_hard_bounce_resets_transient_bounces(self, db_session):
9397
email = EmailFactory.create(transient_bounces=3)
@@ -108,7 +112,8 @@ def test_complain_unverifies_email(self, db_session):
108112

109113
assert status.save().status == "Complained"
110114
assert not email.verified
111-
assert email.is_having_delivery_issues
115+
assert not email.is_having_delivery_issues
116+
assert email.spam_complaint
112117

113118
def test_complain_resets_transient_bounces(self, db_session):
114119
email = EmailFactory.create(transient_bounces=3)

warehouse/accounts/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ class Email(db.ModelBase):
113113
verified = Column(Boolean, nullable=False)
114114

115115
# Deliverability information
116+
spam_complaint = Column(
117+
Boolean,
118+
nullable=False,
119+
server_default=sql.false(),
120+
)
116121
is_having_delivery_issues = Column(
117122
Boolean,
118123
nullable=False,

warehouse/email/ses/models.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,16 +94,22 @@ def complain(self):
9494

9595
# Outputs
9696
@_machine.output()
97-
def _flag_email(self):
98-
self._unverify_email(self._get_email())
97+
def _handle_bounce(self):
98+
self._unverify_for_delivery_failure(self._get_email())
99+
100+
@_machine.output()
101+
def _handle_complaint(self):
102+
email = self._get_email()
103+
email.verified = False
104+
email.spam_complaint = True
99105

100106
@_machine.output()
101107
def _incr_transient_bounce(self):
102108
email = self._get_email()
103109
email.transient_bounces += 1
104110

105111
if email.transient_bounces > MAX_TRANSIENT_BOUNCES:
106-
self._unverify_email(email)
112+
self._unverify_for_delivery_failure(email)
107113

108114
@_machine.output()
109115
def _reset_transient_bounce(self):
@@ -115,7 +121,7 @@ def _reset_transient_bounce(self):
115121
accepted.upon(deliver, enter=delivered, outputs=[_reset_transient_bounce],
116122
collector=lambda iterable: list(iterable)[-1])
117123
accepted.upon(bounce, enter=bounced,
118-
outputs=[_reset_transient_bounce, _flag_email],
124+
outputs=[_reset_transient_bounce, _handle_bounce],
119125
collector=lambda iterable: list(iterable)[-1])
120126
accepted.upon(soft_bounce, enter=soft_bounced,
121127
outputs=[_incr_transient_bounce],
@@ -125,7 +131,7 @@ def _reset_transient_bounce(self):
125131
# really want to treat this as a bounce. We'll record the event
126132
# for posterity though.
127133
delivered.upon(soft_bounce, enter=delivered, outputs=[])
128-
delivered.upon(complain, enter=complained, outputs=[_flag_email],
134+
delivered.upon(complain, enter=complained, outputs=[_handle_complaint],
129135
collector=lambda iterable: list(iterable)[-1])
130136

131137
# Serialization / Deserialization
@@ -155,7 +161,7 @@ def _get_email(self):
155161
.filter(EmailAddress.email == self._email_message.to)
156162
.one())
157163

158-
def _unverify_email(self, email):
164+
def _unverify_for_delivery_failure(self, email):
159165
# Unverify the email, and mark why
160166
email.verified = False
161167
email.is_having_delivery_issues = True

warehouse/migrations/versions/7f0d1b5af8c7_add_tables_for_storing_ses_information.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,16 @@ def upgrade():
111111
unique=True,
112112
)
113113

114+
op.add_column(
115+
"accounts_email",
116+
sa.Column(
117+
"spam_complaint",
118+
sa.Boolean(),
119+
server_default=sa.false(),
120+
nullable=False,
121+
),
122+
)
123+
114124
op.add_column(
115125
"accounts_email",
116126
sa.Column(
@@ -135,6 +145,7 @@ def upgrade():
135145
def downgrade():
136146
op.drop_column("accounts_email", "transient_bounces")
137147
op.drop_column("accounts_email", "is_having_delivery_issues")
148+
op.drop_column("accounts_email", "spam_complaint")
138149
op.drop_index(op.f("ix_ses_events_event_id"), table_name="ses_events")
139150
op.drop_table("ses_events")
140151
op.drop_index(op.f("ix_ses_emails_message_id"), table_name="ses_emails")

0 commit comments

Comments
 (0)