Commit 5cf87e7d authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch '26001-notification-release-be' into 'master'

Add "New release" to the custom notification (BE)

Closes #26001

See merge request gitlab-org/gitlab!17877
parents 6eab4b16 2eb9c962
# frozen_string_literal: true
module Emails
module Releases
def new_release_email(user_id, release, reason = nil)
@release = release
@project = @release.project
@target_url = namespace_project_releases_url(
namespace_id: @project.namespace,
project_id: @project
)
user = User.find(user_id)
mail(
to: user.notification_email_for(@project.group),
subject: subject(release_email_subject)
)
end
private
def release_email_subject
release_info = [@release.name, @release.tag].select(&:presence).join(' - ')
"New release: #{release_info}"
end
end
end
......@@ -16,6 +16,7 @@ class Notify < BaseMailer
include Emails::Members
include Emails::AutoDevops
include Emails::RemoteMirrors
include Emails::Releases
helper MilestonesHelper
helper MergeRequestsHelper
......
......@@ -25,6 +25,7 @@ class NotificationSetting < ApplicationRecord
end
EMAIL_EVENTS = [
:new_release,
:new_note,
:new_issue,
:reopen_issue,
......
......@@ -26,10 +26,12 @@ class Release < ApplicationRecord
validates_associated :milestone_releases, message: -> (_, obj) { obj[:value].map(&:errors).map(&:full_messages).join(",") }
scope :sorted, -> { order(released_at: :desc) }
scope :with_project_and_namespace, -> { includes(project: :namespace) }
delegate :repository, to: :project
after_commit :create_evidence!, on: :create
after_commit :notify_new_release, on: :create
def commit
strong_memoize(:commit) do
......@@ -73,6 +75,10 @@ class Release < ApplicationRecord
def create_evidence!
CreateEvidenceWorker.perform_async(self.id)
end
def notify_new_release
NewReleaseWorker.perform_async(id)
end
end
Release.prepend_if_ee('EE::Release')
......@@ -28,6 +28,10 @@ module NotificationRecipientService
Builder::ProjectMaintainers.new(*args).notification_recipients
end
def self.build_new_release_recipients(*args)
Builder::NewRelease.new(*args).notification_recipients
end
module Builder
class Base
def initialize(*)
......@@ -359,6 +363,26 @@ module NotificationRecipientService
end
end
class NewRelease < Base
attr_reader :target
def initialize(target)
@target = target
end
def build!
add_recipients(target.project.authorized_users, :custom, nil)
end
def custom_action
:new_release
end
def acting_user
target.author
end
end
class MergeRequestUnmergeable < Base
attr_reader :target
def initialize(merge_request)
......
......@@ -289,6 +289,15 @@ class NotificationService
end
end
# Notify users when a new release is created
def send_new_release_notifications(release)
recipients = NotificationRecipientService.build_new_release_recipients(release)
recipients.each do |recipient|
mailer.new_release_email(recipient.user.id, release, recipient.reason).deliver_later
end
end
# Members
def new_access_request(member)
return true unless member.notifiable?(:subscription)
......
- release_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: @target_url }
- description_details = { tag: @release.tag, name: @project.name, release_link_start: release_link_start, release_link_end: '</a>'.html_safe }
%div{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif;" }
%p
= _("A new Release %{tag} for %{name} was published. Visit the %{release_link_start}Releases page%{release_link_end} to read more about it.").html_safe % description_details
%p
%h4= _("Assets:")
%ul
- @release.links.each do |link|
%li= link_to(link.name, link.url)
- @release.sources.each do |source|
%li= link_to(_("Download %{format}") % { format: source.format }, source.url)
%p
%h4= _("Release notes:")
= markdown_field(@release, :description)
<%= _("A new Release %{tag} for %{name} was published. Visit the Releases page to read more about it:").html_safe % { tag: @release.tag, name: @project.name } %> <%= @target_url %>
<%= _("Assets:") %>
<% @release.links.each do |link| -%>
- <%= link.name %>: <%= link.url %>
<% end -%>
<% @release.sources.each do |source| -%>
- <%= _("Download %{format}:") % { format: source.format } %> <%= source.url %>
<% end -%>
<%= _("Release notes:") %>
<%= @release.description %>
......@@ -119,6 +119,8 @@
- container_repository:delete_container_repository
- container_repository:cleanup_container_repository
- notifications:new_release
- default
- mailers # ActionMailer::DeliveryJob.queue_name
......
# frozen_string_literal: true
class NewReleaseWorker
include ApplicationWorker
queue_namespace :notifications
def perform(release_id)
release = Release.with_project_and_namespace.find_by_id(release_id)
return unless release
NotificationService.new.send_new_release_notifications(release)
end
end
---
title: Add 'New release' to the project custom notifications
merge_request: 17877
author:
type: added
......@@ -24,6 +24,7 @@
- [process_commit, 3]
- [new_note, 2]
- [new_issue, 2]
- [notifications, 2]
- [new_merge_request, 2]
- [pipeline_processing, 5]
- [pipeline_creation, 4]
......
# frozen_string_literal: true
class AddNewReleaseToNotificationSettings < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :notification_settings, :new_release, :boolean
end
end
......@@ -2509,6 +2509,7 @@ ActiveRecord::Schema.define(version: 2019_10_16_220135) do
t.boolean "issue_due"
t.boolean "new_epic"
t.string "notification_email"
t.boolean "new_release"
t.index ["source_id", "source_type"], name: "index_notification_settings_on_source_id_and_source_type"
t.index ["user_id", "source_id", "source_type"], name: "index_notifications_on_user_id_and_source_id_and_source_type", unique: true
t.index ["user_id"], name: "index_notification_settings_on_user_id"
......
......@@ -65,6 +65,18 @@ project.
![Releases list](img/releases.png)
## Notification for Releases
> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/26001) in GitLab 12.4.
You can be notified by email when a new Release is created for your project.
To subscribe to these notifications, navigate to your **Project**'s landing page, then click on the
bell icon. Choose **Custom** from the dropdown menu. The
following modal window will be then displayed, from which you can select **New release** to complete your subscription to new Releases notifications.
![Custom notification - New release](img/custom_notifications_new_release_v12_4.png)
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
......
......@@ -84,6 +84,7 @@ Below is the table of events users can be notified of:
| User added to group | User | Sent when user is added to group |
| Group access level changed | User | Sent when user group access level is changed |
| Project moved | Project members (1) | (1) not disabled |
| New release | Project members | Custom notification |
### Issue / Epics / Merge request events
......
......@@ -12,6 +12,7 @@ describe NotificationSetting do
it 'appends EE specific events' do
expect(subject).to eq(
[
:new_release,
:new_note,
:new_issue,
:reopen_issue,
......@@ -38,6 +39,7 @@ describe NotificationSetting do
it 'returns CE list' do
expect(subject).to eq(
[
:new_release,
:new_note,
:new_issue,
:reopen_issue,
......@@ -63,6 +65,7 @@ describe NotificationSetting do
it 'appends EE specific events' do
expect(subject).to eq(
[
:new_release,
:new_note,
:new_issue,
:reopen_issue,
......
......@@ -659,6 +659,12 @@ msgstr ""
msgid "A merge request approval is required when the license compliance report contains a blacklisted license."
msgstr ""
msgid "A new Release %{tag} for %{name} was published. Visit the %{release_link_start}Releases page%{release_link_end} to read more about it."
msgstr ""
msgid "A new Release %{tag} for %{name} was published. Visit the Releases page to read more about it:"
msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
......@@ -2009,6 +2015,9 @@ msgstr ""
msgid "Assets"
msgstr ""
msgid "Assets:"
msgstr ""
msgid "Assign"
msgstr ""
......@@ -5709,6 +5718,12 @@ msgstr ""
msgid "Download"
msgstr ""
msgid "Download %{format}"
msgstr ""
msgid "Download %{format}:"
msgstr ""
msgid "Download CSV"
msgstr ""
......@@ -11139,6 +11154,9 @@ msgstr ""
msgid "NotificationEvent|New note"
msgstr ""
msgid "NotificationEvent|New release"
msgstr ""
msgid "NotificationEvent|Reassign issue"
msgstr ""
......@@ -13492,6 +13510,9 @@ msgstr ""
msgid "Release notes"
msgstr ""
msgid "Release notes:"
msgstr ""
msgid "Release title"
msgstr ""
......
......@@ -14,3 +14,4 @@ N_('NotificationEvent|Close merge request')
N_('NotificationEvent|Reassign merge request')
N_('NotificationEvent|Merge merge request')
N_('NotificationEvent|Failed pipeline')
N_('NotificationEvent|New release')
# frozen_string_literal: true
require 'spec_helper'
require 'email_spec'
describe Emails::Releases do
include EmailSpec::Matchers
include_context 'gitlab email notification'
describe '#new_release_email' do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let(:release) { create(:release, project: project) }
subject { Notify.new_release_email(user.id, release) }
it_behaves_like 'an email sent from GitLab'
context 'when the release has a name' do
it 'shows the correct subject' do
expected_subject = "#{release.project.name} | New release: #{release.name} - #{release.tag}"
is_expected.to have_subject(expected_subject)
end
end
context 'when the release does not have a name' do
it 'shows the correct subject' do
release.name = nil
expected_subject = "#{release.project.name} | New release: #{release.tag}"
is_expected.to have_subject(expected_subject)
end
end
it 'contains a message with the new release tag' do
message = "A new Release #{release.tag} for #{release.project.name} was published."
is_expected.to have_body_text(message)
end
it 'contains the release assets' do
is_expected.to have_body_text('Assets:')
release.sources do |source|
is_expected.to have_body_text("Download #{source.format}")
end
end
it 'contains the release notes' do
is_expected.to have_body_text('Release notes:')
is_expected.to have_body_text(release.description)
end
end
end
......@@ -98,6 +98,7 @@ RSpec.describe NotificationSetting do
it 'returns email events' do
expect(subject).to include(
:new_release,
:new_note,
:new_issue,
:reopen_issue,
......
......@@ -109,4 +109,24 @@ RSpec.describe Release do
end
end
end
describe '#notify_new_release' do
context 'when a release is created' do
it 'instantiates NewReleaseWorker to send notifications' do
expect(NewReleaseWorker).to receive(:perform_async)
create(:release)
end
end
context 'when a release is updated' do
let!(:release) { create(:release) }
it 'does not send any new notification' do
expect(NewReleaseWorker).not_to receive(:perform_async)
release.update!(description: 'new description')
end
end
end
end
......@@ -678,6 +678,27 @@ describe NotificationService, :mailer do
end
end
describe '#send_new_release_notifications' do
context 'when recipients for a new release exist' do
let(:release) { create(:release) }
it 'calls new_release_email for each relevant recipient' do
user_1 = create(:user)
user_2 = create(:user)
user_3 = create(:user)
recipient_1 = NotificationRecipient.new(user_1, :custom, custom_action: :new_release)
recipient_2 = NotificationRecipient.new(user_2, :custom, custom_action: :new_release)
allow(NotificationRecipientService).to receive(:build_new_release_recipients).and_return([recipient_1, recipient_2])
release
should_email(user_1)
should_email(user_2)
should_not_email(user_3)
end
end
end
describe 'Participating project notification settings have priority over group and global settings if available' do
let!(:group) { create(:group) }
let!(:maintainer) { group.add_owner(create(:user, username: 'maintainer')).user }
......
# frozen_string_literal: true
require 'spec_helper'
describe NewReleaseWorker do
let(:release) { create(:release) }
it 'sends a new release notification' do
expect_any_instance_of(NotificationService).to receive(:send_new_release_notifications).with(release)
described_class.new.perform(release.id)
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