Commit 1ef44964 authored by Sean McGivern's avatar Sean McGivern

Merge branch 'list-id' into 'master'

Add List-Id to notification emails

Closes #53493

See merge request gitlab-org/gitlab-ce!22817
parents 3821813d a96cd7cb
...@@ -98,4 +98,29 @@ module EmailsHelper ...@@ -98,4 +98,29 @@ module EmailsHelper
"#{string} on #{Gitlab.config.gitlab.host}" "#{string} on #{Gitlab.config.gitlab.host}"
end end
def create_list_id_string(project, list_id_max_length = 255)
project_path_as_domain = project.full_path.downcase
.split('/').reverse.join('/')
.gsub(%r{[^a-z0-9\/]}, '-')
.gsub(%r{\/+}, '.')
.gsub(/(\A\.+|\.+\z)/, '')
max_domain_length = list_id_max_length - Gitlab.config.gitlab.host.length - project.id.to_s.length - 2
if max_domain_length < 3
return project.id.to_s + "..." + Gitlab.config.gitlab.host
end
if project_path_as_domain.length > max_domain_length
project_path_as_domain = project_path_as_domain.slice(0, max_domain_length)
last_dot_index = project_path_as_domain[0..-2].rindex(".")
last_dot_index ||= max_domain_length - 2
project_path_as_domain = project_path_as_domain.slice(0, last_dot_index).concat("..")
end
project.id.to_s + "." + project_path_as_domain + "." + Gitlab.config.gitlab.host
end
end end
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
class Notify < BaseMailer class Notify < BaseMailer
include ActionDispatch::Routing::PolymorphicRoutes include ActionDispatch::Routing::PolymorphicRoutes
include GitlabRoutingHelper include GitlabRoutingHelper
include EmailsHelper
include Emails::Issues include Emails::Issues
include Emails::MergeRequests include Emails::MergeRequests
...@@ -194,6 +195,7 @@ class Notify < BaseMailer ...@@ -194,6 +195,7 @@ class Notify < BaseMailer
headers['X-GitLab-Project'] = @project.name headers['X-GitLab-Project'] = @project.name
headers['X-GitLab-Project-Id'] = @project.id headers['X-GitLab-Project-Id'] = @project.id
headers['X-GitLab-Project-Path'] = @project.full_path headers['X-GitLab-Project-Path'] = @project.full_path
headers['List-Id'] = "#{@project.full_path} <#{create_list_id_string(@project)}>"
end end
def add_unsubscription_headers_and_links def add_unsubscription_headers_and_links
......
---
title: Add project identifier as List-Id email Header to ease filtering
merge_request: 22817
author: Olivier Crête
type: added
...@@ -135,6 +135,7 @@ Notification emails include headers that provide extra content about the notific ...@@ -135,6 +135,7 @@ Notification emails include headers that provide extra content about the notific
| X-GitLab-Pipeline-Id | Only in pipeline emails, the ID of the pipeline the notification is for | | X-GitLab-Pipeline-Id | Only in pipeline emails, the ID of the pipeline the notification is for |
| X-GitLab-Reply-Key | A unique token to support reply by email | | X-GitLab-Reply-Key | A unique token to support reply by email |
| X-GitLab-NotificationReason | The reason for being notified. "mentioned", "assigned", etc | | X-GitLab-NotificationReason | The reason for being notified. "mentioned", "assigned", etc |
| List-Id | The path of the project in a RFC 2919 mailing list identifier useful for email organization, for example, with GMail filters |
#### X-GitLab-NotificationReason #### X-GitLab-NotificationReason
......
...@@ -73,4 +73,59 @@ describe EmailsHelper do ...@@ -73,4 +73,59 @@ describe EmailsHelper do
end end
end end
end end
describe '#create_list_id_string' do
using RSpec::Parameterized::TableSyntax
where(:full_path, :list_id_path) do
"01234" | "01234"
"5/0123" | "012.."
"45/012" | "012.."
"012" | "012"
"23/01" | "01.23"
"2/01" | "01.2"
"234/01" | "01.."
"4/2/0" | "0.2.4"
"45/2/0" | "0.2.."
"5/23/0" | "0.."
"0-2/5" | "5.0-2"
"0_2/5" | "5.0-2"
"0.2/5" | "5.0-2"
end
with_them do
it 'ellipcizes different variants' do
project = double("project")
allow(project).to receive(:full_path).and_return(full_path)
allow(project).to receive(:id).and_return(12345)
# Set a max length that gives only 5 chars for the project full path
max_length = "12345..#{Gitlab.config.gitlab.host}".length + 5
list_id = create_list_id_string(project, max_length)
expect(list_id).to eq("12345.#{list_id_path}.#{Gitlab.config.gitlab.host}")
expect(list_id).to satisfy { |s| s.length <= max_length }
end
end
end
describe 'Create realistic List-Id identifier' do
using RSpec::Parameterized::TableSyntax
where(:full_path, :list_id_path) do
"gitlab-org/gitlab-ce" | "gitlab-ce.gitlab-org"
"project-name/subproject_name/my.project" | "my-project.subproject-name.project-name"
end
with_them do
it 'Produces the right List-Id' do
project = double("project")
allow(project).to receive(:full_path).and_return(full_path)
allow(project).to receive(:id).and_return(12345)
list_id = create_list_id_string(project)
expect(list_id).to eq("12345.#{list_id_path}.#{Gitlab.config.gitlab.host}")
expect(list_id).to satisfy { |s| s.length <= 255 }
end
end
end
end end
shared_context 'gitlab email notification' do shared_context 'gitlab email notification' do
set(:project) { create(:project, :repository) } set(:project) { create(:project, :repository, name: 'a-known-name') }
set(:recipient) { create(:user, email: 'recipient@example.com') } set(:recipient) { create(:user, email: 'recipient@example.com') }
let(:gitlab_sender_display_name) { Gitlab.config.gitlab.email_display_name } let(:gitlab_sender_display_name) { Gitlab.config.gitlab.email_display_name }
...@@ -62,9 +62,11 @@ end ...@@ -62,9 +62,11 @@ end
shared_examples 'an email with X-GitLab headers containing project details' do shared_examples 'an email with X-GitLab headers containing project details' do
it 'has X-GitLab-Project headers' do it 'has X-GitLab-Project headers' do
aggregate_failures do aggregate_failures do
full_path_as_domain = "#{project.name}.#{project.namespace.path}"
is_expected.to have_header('X-GitLab-Project', /#{project.name}/) is_expected.to have_header('X-GitLab-Project', /#{project.name}/)
is_expected.to have_header('X-GitLab-Project-Id', /#{project.id}/) is_expected.to have_header('X-GitLab-Project-Id', /#{project.id}/)
is_expected.to have_header('X-GitLab-Project-Path', /#{project.full_path}/) is_expected.to have_header('X-GitLab-Project-Path', /#{project.full_path}/)
is_expected.to have_header('List-Id', "#{project.full_path} <#{project.id}.#{full_path_as_domain}.#{Gitlab.config.gitlab.host}>")
end end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment