Commit 0ae48c6a authored by Paul Slaughter's avatar Paul Slaughter

Merge branch 'led/349690-add-pre-enforcement-storage-notifications-1' into 'master'

Add frontend code to support storage_enforcement_banner

See merge request gitlab-org/gitlab!79625
parents 7148f7c5 58d784aa
......@@ -11,6 +11,7 @@ const PERSISTENT_USER_CALLOUTS = [
'.js-eoa-bronze-plan-banner',
'.js-security-newsletter-callout',
'.js-approaching-seats-count-threshold',
'.js-storage-enforcement-banner',
];
const initCallouts = () => {
......
......@@ -25,16 +25,17 @@ module StorageHelper
end
def storage_enforcement_banner_info(namespace)
return unless can?(current_user, :admin_namespace, namespace)
return if namespace.paid?
return unless namespace.storage_enforcement_date && namespace.storage_enforcement_date >= Date.today
return if user_dismissed_storage_enforcement_banner?(namespace)
{
text: html_escape_once(s_("UsageQuota|From %{storage_enforcement_date} storage limits will apply to this namespace. " \
"View and manage your usage in %{strong_start}Group Settings > Usage quotas%{strong_end}.")).html_safe %
{ storage_enforcement_date: namespace.storage_enforcement_date, strong_start: "<strong>".html_safe, strong_end: "</strong>".html_safe },
"View and manage your usage in %{strong_start}%{namespace_type} settings &gt; Usage quotas%{strong_end}.")).html_safe %
{ storage_enforcement_date: namespace.storage_enforcement_date, strong_start: "<strong>".html_safe, strong_end: "</strong>".html_safe, namespace_type: namespace.type },
variant: 'warning',
callouts_path: group_callouts_path,
callouts_path: namespace.user_namespace? ? callouts_path : group_callouts_path,
callouts_feature_name: storage_enforcement_banner_user_callouts_feature_name(namespace),
learn_more_link: link_to(_('Learn more.'), help_page_path('/'), rel: 'noopener noreferrer', target: '_blank') # TBD: https://gitlab.com/gitlab-org/gitlab/-/issues/350632
}
......@@ -52,13 +53,17 @@ module StorageHelper
return :first if days_to_enforcement_date > 30
return :second if days_to_enforcement_date > 15 && days_to_enforcement_date <= 30
return :third if days_to_enforcement_date > 7 && days_to_enforcement_date <= 15
return :fourth if days_to_enforcement_date > 0 && days_to_enforcement_date <= 7
return :fourth if days_to_enforcement_date >= 0 && days_to_enforcement_date <= 7
end
def user_dismissed_storage_enforcement_banner?(namespace)
return false unless current_user
current_user.dismissed_callout_for_group?(feature_name: storage_enforcement_banner_user_callouts_feature_name(namespace),
group: namespace)
if namespace.user_namespace?
current_user.dismissed_callout?(feature_name: storage_enforcement_banner_user_callouts_feature_name(namespace))
else
current_user.dismissed_callout_for_group?(feature_name: storage_enforcement_banner_user_callouts_feature_name(namespace),
group: namespace)
end
end
end
......@@ -42,7 +42,11 @@ module Users
security_newsletter_callout: 39,
verification_reminder: 40, # EE-only
ci_deprecation_warning_for_types_keyword: 41,
security_training_feature_promotion: 42 # EE-only
security_training_feature_promotion: 42, # EE-only
storage_enforcement_banner_first_enforcement_threshold: 43,
storage_enforcement_banner_second_enforcement_threshold: 44,
storage_enforcement_banner_third_enforcement_threshold: 45,
storage_enforcement_banner_fourth_enforcement_threshold: 46
}
validates :feature_name,
......
......@@ -11,10 +11,10 @@ module Users
enum feature_name: {
invite_members_banner: 1,
approaching_seat_count_threshold: 2, # EE-only
storage_enforcement_banner_first_enforcement_threshold: 43,
storage_enforcement_banner_second_enforcement_threshold: 44,
storage_enforcement_banner_third_enforcement_threshold: 45,
storage_enforcement_banner_fourth_enforcement_threshold: 46
storage_enforcement_banner_first_enforcement_threshold: 3,
storage_enforcement_banner_second_enforcement_threshold: 4,
storage_enforcement_banner_third_enforcement_threshold: 5,
storage_enforcement_banner_fourth_enforcement_threshold: 6
}
validates :group, presence: true
......
......@@ -6,6 +6,9 @@
- display_namespace_storage_limit_alert!
- @left_sidebar = true
- content_for :flash_message do
= render "layouts/header/storage_enforcement_banner", namespace: @group
- content_for :page_specific_javascripts do
- if current_user
= javascript_tag do
......
- return unless current_user
- namespace = local_assigns.fetch(:namespace)
- banner_info = storage_enforcement_banner_info(namespace)
- return unless banner_info.present?
= render 'shared/global_alert', variant: :warning, alert_class: 'js-storage-enforcement-banner', alert_data: { feature_id: banner_info[:callouts_feature_name], dismiss_endpoint: banner_info[:callouts_path], group_id: namespace.id, defer_links: "true" } do
.gl-alert-body
= banner_info[:text]
= banner_info[:learn_more_link]
......@@ -5,4 +5,8 @@
- @left_sidebar = true
- enable_search_settings locals: { container_class: 'gl-my-5' }
- content_for :flash_message do
= render "layouts/header/storage_enforcement_banner", namespace: current_user.namespace
= render template: "layouts/application"
......@@ -18535,6 +18535,10 @@ Name of the feature that the callout is for.
| <a id="usercalloutfeaturenameenumsecurity_configuration_upgrade_banner"></a>`SECURITY_CONFIGURATION_UPGRADE_BANNER` | Callout feature name for security_configuration_upgrade_banner. |
| <a id="usercalloutfeaturenameenumsecurity_newsletter_callout"></a>`SECURITY_NEWSLETTER_CALLOUT` | Callout feature name for security_newsletter_callout. |
| <a id="usercalloutfeaturenameenumsecurity_training_feature_promotion"></a>`SECURITY_TRAINING_FEATURE_PROMOTION` | Callout feature name for security_training_feature_promotion. |
| <a id="usercalloutfeaturenameenumstorage_enforcement_banner_first_enforcement_threshold"></a>`STORAGE_ENFORCEMENT_BANNER_FIRST_ENFORCEMENT_THRESHOLD` | Callout feature name for storage_enforcement_banner_first_enforcement_threshold. |
| <a id="usercalloutfeaturenameenumstorage_enforcement_banner_fourth_enforcement_threshold"></a>`STORAGE_ENFORCEMENT_BANNER_FOURTH_ENFORCEMENT_THRESHOLD` | Callout feature name for storage_enforcement_banner_fourth_enforcement_threshold. |
| <a id="usercalloutfeaturenameenumstorage_enforcement_banner_second_enforcement_threshold"></a>`STORAGE_ENFORCEMENT_BANNER_SECOND_ENFORCEMENT_THRESHOLD` | Callout feature name for storage_enforcement_banner_second_enforcement_threshold. |
| <a id="usercalloutfeaturenameenumstorage_enforcement_banner_third_enforcement_threshold"></a>`STORAGE_ENFORCEMENT_BANNER_THIRD_ENFORCEMENT_THRESHOLD` | Callout feature name for storage_enforcement_banner_third_enforcement_threshold. |
| <a id="usercalloutfeaturenameenumsuggest_pipeline"></a>`SUGGEST_PIPELINE` | Callout feature name for suggest_pipeline. |
| <a id="usercalloutfeaturenameenumsuggest_popover_dismissed"></a>`SUGGEST_POPOVER_DISMISSED` | Callout feature name for suggest_popover_dismissed. |
| <a id="usercalloutfeaturenameenumtabs_position_highlight"></a>`TABS_POSITION_HIGHLIGHT` | Callout feature name for tabs_position_highlight. |
......@@ -39625,7 +39625,7 @@ msgstr ""
msgid "UsageQuota|File attachments and smaller design graphics."
msgstr ""
msgid "UsageQuota|From %{storage_enforcement_date} storage limits will apply to this namespace. View and manage your usage in %{strong_start}Group Settings &gt; Usage quotas%{strong_end}."
msgid "UsageQuota|From %{storage_enforcement_date} storage limits will apply to this namespace. View and manage your usage in %{strong_start}%{namespace_type} settings &gt; Usage quotas%{strong_end}."
msgstr ""
msgid "UsageQuota|Git repository."
......
......@@ -474,4 +474,69 @@ RSpec.describe 'Group' do
fill_in 'confirm_name_input', with: confirm_with
click_button 'Confirm'
end
describe 'storage_enforcement_banner', :js do
let_it_be(:group) { create(:group) }
let_it_be_with_refind(:user) { create(:user) }
before_all do
group.add_owner(user)
sign_in(user)
end
context 'with storage_enforcement_date set' do
let_it_be(:storage_enforcement_date) { Date.today + 30 }
before do
allow_next_found_instance_of(Group) do |g|
allow(g).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
end
end
it 'displays the banner in the group page' do
visit group_path(group)
expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
end
it 'does not display the banner in a paid group page' do
allow_next_found_instance_of(Group) do |g|
allow(g).to receive(:paid?).and_return(true)
end
visit group_path(group)
expect_page_not_to_have_storage_enforcement_banner
end
it 'does not display the banner if user has previously closed unless threshold has changed' do
visit group_path(group)
expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
find('.js-storage-enforcement-banner [data-testid="close-icon"]').click
page.refresh
expect_page_not_to_have_storage_enforcement_banner
storage_enforcement_date = Date.today + 13
allow_next_found_instance_of(Group) do |g|
allow(g).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
end
page.refresh
expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
end
end
context 'with storage_enforcement_date not set' do
# This test should break and be rewritten after the implementation of the storage_enforcement_date
# TBD: https://gitlab.com/gitlab-org/gitlab/-/issues/350632
it 'does not display the banner in the group page' do
visit group_path(group)
expect_page_not_to_have_storage_enforcement_banner
end
end
end
def expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
expect(page).to have_text "From #{storage_enforcement_date} storage limits will apply to this namespace"
end
def expect_page_not_to_have_storage_enforcement_banner
expect(page).not_to have_text "storage limits will apply to this namespace"
end
end
......@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'User visits their profile' do
let(:user) { create(:user) }
let_it_be_with_refind(:user) { create(:user) }
before do
sign_in(user)
......@@ -87,4 +87,53 @@ RSpec.describe 'User visits their profile' do
end
end
end
describe 'storage_enforcement_banner', :js do
context 'with storage_enforcement_date set' do
let_it_be(:storage_enforcement_date) { Date.today + 30 }
before do
allow_next_found_instance_of(Namespaces::UserNamespace) do |g|
allow(g).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
end
end
it 'displays the banner in the profile page' do
visit(profile_path)
expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
end
it 'does not display the banner if user has previously closed unless threshold has changed' do
visit(profile_path)
expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
find('.js-storage-enforcement-banner [data-testid="close-icon"]').click
page.refresh
expect_page_not_to_have_storage_enforcement_banner
storage_enforcement_date = Date.today + 13
allow_next_found_instance_of(Namespaces::UserNamespace) do |g|
allow(g).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
end
page.refresh
expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
end
end
context 'with storage_enforcement_date not set' do
# This test should break and be rewritten after the implementation of the storage_enforcement_date
# TBD: https://gitlab.com/gitlab-org/gitlab/-/issues/350632
it 'does not display the banner in the group page' do
visit(profile_path)
expect_page_not_to_have_storage_enforcement_banner
end
end
end
def expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
expect(page).to have_text "From #{storage_enforcement_date} storage limits will apply to this namespace"
end
def expect_page_not_to_have_storage_enforcement_banner
expect(page).not_to have_text "storage limits will apply to this namespace"
end
end
......@@ -57,6 +57,8 @@ RSpec.describe StorageHelper do
let_it_be(:paid_group) { create(:group) }
before do
allow(helper).to receive(:can?).with(current_user, :admin_namespace, free_group).and_return(true)
allow(helper).to receive(:can?).with(current_user, :admin_namespace, paid_group).and_return(true)
allow(helper).to receive(:current_user) { current_user }
allow(Gitlab).to receive(:com?).and_return(true)
allow(paid_group).to receive(:paid?).and_return(true)
......@@ -64,26 +66,37 @@ RSpec.describe StorageHelper do
describe "#storage_enforcement_banner_info" do
it 'returns nil when namespace is not free' do
expect(storage_enforcement_banner_info(paid_group)).to be(nil)
expect(helper.storage_enforcement_banner_info(paid_group)).to be(nil)
end
it 'returns nil when storage_enforcement_date is not set' do
allow(free_group).to receive(:storage_enforcement_date).and_return(nil)
expect(storage_enforcement_banner_info(free_group)).to be(nil)
expect(helper.storage_enforcement_banner_info(free_group)).to be(nil)
end
it 'returns a hash when storage_enforcement_date is set' do
storage_enforcement_date = Date.today + 30
allow(free_group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
expect(storage_enforcement_banner_info(free_group)).to eql({
text: "From #{storage_enforcement_date} storage limits will apply to this namespace. View and manage your usage in <strong>Group Settings &gt; Usage quotas</strong>.",
variant: 'warning',
callouts_feature_name: 'storage_enforcement_banner_second_enforcement_threshold',
callouts_path: '/-/users/group_callouts',
learn_more_link: '<a rel="noopener noreferrer" target="_blank" href="/help//">Learn more.</a>'
})
describe 'when storage_enforcement_date is set' do
let_it_be(:storage_enforcement_date) { Date.today + 30 }
before do
allow(free_group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
end
it 'returns nil when current_user do not have access usage quotas page' do
allow(helper).to receive(:can?).with(current_user, :admin_namespace, free_group).and_return(false)
expect(helper.storage_enforcement_banner_info(free_group)).to be(nil)
end
it 'returns a hash when current_user can access usage quotas page' do
expect(helper.storage_enforcement_banner_info(free_group)).to eql({
text: "From #{storage_enforcement_date} storage limits will apply to this namespace. View and manage your usage in <strong>Group settings &gt; Usage quotas</strong>.",
variant: 'warning',
callouts_feature_name: 'storage_enforcement_banner_second_enforcement_threshold',
callouts_path: '/-/users/group_callouts',
learn_more_link: '<a rel="noopener noreferrer" target="_blank" href="/help//">Learn more.</a>'
})
end
end
context 'when storage_enforcement_date is set and dismissed callout exists' do
......@@ -96,7 +109,7 @@ RSpec.describe StorageHelper do
allow(free_group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
end
it { expect(storage_enforcement_banner_info(free_group)).to be(nil) }
it { expect(helper.storage_enforcement_banner_info(free_group)).to be(nil) }
end
context 'callouts_feature_name' do
......@@ -106,7 +119,7 @@ RSpec.describe StorageHelper do
storage_enforcement_date = Date.today + days_from_now
allow(free_group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
storage_enforcement_banner_info(free_group)[:callouts_feature_name]
helper.storage_enforcement_banner_info(free_group)[:callouts_feature_name]
end
it 'returns first callouts_feature_name' do
......
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