Skip to content

Commit 42de934

Browse files
committed
support alternative email addresses for mailing lists
1 parent 555d98f commit 42de934

8 files changed

Lines changed: 70 additions & 3 deletions

File tree

app/controllers/admin/mailing_lists_controller.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ def set_mailing_list
5454
end
5555

5656
def mailing_list_params
57-
params.require(:mailing_list).permit(:identifier, :display_name, :email, :description)
57+
permitted = params.require(:mailing_list).permit(:identifier, :display_name, :email, :description, :alternate_emails)
58+
if permitted[:alternate_emails].is_a?(String)
59+
permitted[:alternate_emails] = permitted[:alternate_emails].split(/[\r\n,]+/).map(&:strip).reject(&:blank?)
60+
end
61+
permitted
5862
end
5963
end

app/models/mailing_list.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,16 @@ class MailingList < ApplicationRecord
66

77
validates :identifier, presence: true, uniqueness: true
88
validates :display_name, presence: true
9+
10+
def all_emails
11+
[email, *alternate_emails].compact.reject(&:blank?).map(&:downcase)
12+
end
13+
14+
def self.email_lookup_index
15+
index = {}
16+
where.not(email: nil).find_each do |ml|
17+
ml.all_emails.each { |e| index[e] = ml }
18+
end
19+
index
20+
end
921
end

app/services/imap_idle_runner.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ def resolve_mailing_lists_from_mail(mail)
181181
all_addresses.concat(Array(mail.to)) if mail.to
182182
all_addresses.concat(Array(mail.cc)) if mail.cc
183183

184-
known_lists = MailingList.where.not(email: nil).index_by { |ml| ml.email.downcase }
184+
known_lists = MailingList.email_lookup_index
185185
all_addresses.filter_map { |addr| known_lists[addr.to_s.downcase] }.uniq
186186
end
187187

app/views/admin/mailing_lists/_form.html.slim

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
= f.label :email
1818
= f.text_field :email, placeholder: "e.g. pgsql-hackers@lists.postgresql.org", class: "form-input"
1919

20+
.form-group
21+
= f.label :alternate_emails, "Alternate emails (one per line)"
22+
= f.text_area :alternate_emails, value: mailing_list.alternate_emails.join("\n"), placeholder: "e.g. pgsql-hackers@postgresql.org", rows: 3, class: "form-input"
23+
2024
.form-group
2125
= f.label :description
2226
= f.text_area :description, placeholder: "Optional description", rows: 3, class: "form-input"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class AddAlternateEmailsToMailingLists < ActiveRecord::Migration[8.0]
2+
def change
3+
add_column :mailing_lists, :alternate_emails, :string, array: true, default: []
4+
end
5+
end

db/schema.rb

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

spec/models/mailing_list_spec.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@
99
it { is_expected.to validate_uniqueness_of(:identifier) }
1010
end
1111

12+
describe ".email_lookup_index" do
13+
it "indexes by primary and alternate emails" do
14+
ml = create(:mailing_list, email: "pgsql-hackers@lists.postgresql.org",
15+
alternate_emails: ["pgsql-hackers@postgresql.org"])
16+
index = described_class.email_lookup_index
17+
expect(index["pgsql-hackers@lists.postgresql.org"]).to eq(ml)
18+
expect(index["pgsql-hackers@postgresql.org"]).to eq(ml)
19+
end
20+
end
21+
1222
describe "associations" do
1323
it { is_expected.to have_many(:message_mailing_lists) }
1424
it { is_expected.to have_many(:messages).through(:message_mailing_lists) }

spec/services/imap_idle_runner_spec.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,37 @@
8282
expect(msg.mailing_lists).to include(hackers_list)
8383
end
8484

85+
it "resolves list from alternate email in CC" do
86+
hackers_list.update!(alternate_emails: ["pgsql-hackers@postgresql.org"])
87+
state = ImapSyncState.for_label("INBOX")
88+
allow(imap_client).to receive(:connect!).and_return(true)
89+
allow(imap_client).to receive(:disconnect!).and_return(true)
90+
expect(imap_client).to receive(:uids_after).with(0).and_return([203])
91+
raw = <<~MAIL
92+
From: Test <test@example.com>
93+
To: peter@eisentraut.org
94+
CC: pgsql-hackers@postgresql.org
95+
Subject: Re: SQL Property Graph Queries
96+
Date: Fri, 1 Jan 2021 12:00:00 +0000
97+
Message-ID: <uid-203@example.com>
98+
MIME-Version: 1.0
99+
Content-Type: text/plain; charset=UTF-8
100+
101+
Body
102+
MAIL
103+
expect(imap_client).to receive(:uid_fetch_rfc822).with(203).and_return(raw)
104+
expect(imap_client).to receive(:mark_seen).with(203)
105+
expect(imap_client).to receive(:idle_once).and_return(:timeout)
106+
allow(imap_client).to receive(:uids_after).with(203).and_return([])
107+
108+
runner = described_class.new(client: imap_client, label: "INBOX")
109+
runner.run(max_cycles: 1, idle_timeout: 1)
110+
111+
msg = Message.find_by(message_id: "uid-203@example.com")
112+
expect(msg).to be_present
113+
expect(msg.mailing_lists).to include(hackers_list)
114+
end
115+
85116
it "skips message when no list can be resolved" do
86117
state = ImapSyncState.for_label("INBOX")
87118
allow(imap_client).to receive(:connect!).and_return(true)

0 commit comments

Comments
 (0)