Commit e0c38bc5 authored by Mikołaj Wawrzyniak's avatar Mikołaj Wawrzyniak

Merge branch '346259_manual_renewal_banner' into 'master'

Introduce manual renewal banner logic

See merge request gitlab-org/gitlab!75872
parents 2c229f97 b882a5d3
---
name: automated_email_provision
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75872
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/348317
milestone: '14.6'
type: development
group: group::license
default_enabled: false
...@@ -9,6 +9,7 @@ module EE ...@@ -9,6 +9,7 @@ module EE
SUBSCRIPTIONS_MANAGE_URL = ::Gitlab::SubscriptionPortal.subscriptions_manage_url.freeze SUBSCRIPTIONS_MANAGE_URL = ::Gitlab::SubscriptionPortal.subscriptions_manage_url.freeze
SUBSCRIPTIONS_PLANS_URL = ::Gitlab::SubscriptionPortal.subscriptions_plans_url.freeze SUBSCRIPTIONS_PLANS_URL = ::Gitlab::SubscriptionPortal.subscriptions_plans_url.freeze
SUBSCRIPTIONS_GITLAB_PLANS_URL = ::Gitlab::SubscriptionPortal.subscriptions_gitlab_plans_url.freeze SUBSCRIPTIONS_GITLAB_PLANS_URL = ::Gitlab::SubscriptionPortal.subscriptions_gitlab_plans_url.freeze
SUBSCRIPTIONS_EDIT_ACCOUNT_URL = ::Gitlab::SubscriptionPortal.edit_account_url.freeze
SUBSCRIPTION_PORTAL_ADMIN_EMAIL = ::Gitlab::SubscriptionPortal.subscription_portal_admin_email.freeze SUBSCRIPTION_PORTAL_ADMIN_EMAIL = ::Gitlab::SubscriptionPortal.subscription_portal_admin_email.freeze
SUBSCRIPTION_PORTAL_ADMIN_TOKEN = ::Gitlab::SubscriptionPortal.subscription_portal_admin_token.freeze SUBSCRIPTION_PORTAL_ADMIN_TOKEN = ::Gitlab::SubscriptionPortal.subscription_portal_admin_token.freeze
CUSTOMER_SUPPORT_URL = ::Gitlab::Saas.customer_support_url.freeze CUSTOMER_SUPPORT_URL = ::Gitlab::Saas.customer_support_url.freeze
......
# frozen_string_literal: true
module Gitlab
class ManualBanner
REMINDER_DAYS = 14.days
def initialize(actionable:)
@actionable = actionable
end
def display?
return false if Gitlab::CurrentSettings.should_check_namespace_plan?
return false unless Feature.enabled?(:automated_email_provision)
require_notification?
end
def subject
return unless display?
banner_subject
end
def body
return unless display?
banner_body
end
def display_error_version?
raise NotImplementedError
end
private
attr_reader :actionable
def require_notification?
raise NotImplementedError
end
def banner_subject
raise NotImplementedError
end
def banner_body
raise NotImplementedError
end
def renewal_service_email
email = Gitlab::SubscriptionPortal::RENEWAL_SERVICE_EMAIL
"<a href='mailto:#{email}'>#{email}</a>".html_safe
end
def customers_dot_url
"<a href='#{EE::SUBSCRIPTIONS_EDIT_ACCOUNT_URL}'>Customers Portal</a>".html_safe
end
end
end
# frozen_string_literal: true
module Gitlab
class ManualRenewalBanner < Gitlab::ManualBanner
def display_error_version?
actionable.expired?
end
private
def require_notification?
return false unless actionable&.will_expire?
return false if ::License.future_dated.present?
within_notification_window?
end
def within_notification_window?
(actionable.expires_at - REMINDER_DAYS) <= Date.today
end
def banner_subject
plan = actionable.plan.titleize
expires_at = actionable.expires_at.to_s
if display_error_version?
_('Your %{plan} subscription expired on %{expiry_date}') % { plan: plan, expiry_date: expires_at }
else
_('Your %{plan} subscription expires on %{expiry_date}') % { plan: plan, expiry_date: expires_at }
end
end
def banner_body
if display_error_version?
_(
'Your subscription is now expired. To renew, export your license usage file and email it to ' \
'%{renewal_service_email}. A new license will be emailed to the email address registered in the ' \
'%{customers_dot}. You can upload this license to your instance. To use Free tier, remove your ' \
'current license.'
).html_safe % { renewal_service_email: renewal_service_email, customers_dot: customers_dot_url }
else
_(
'To renew, export your license usage file and email it to %{renewal_service_email}. ' \
'A new license will be emailed to the email address registered in the %{customers_dot}. ' \
'You can upload this license to your instance.'
).html_safe % { renewal_service_email: renewal_service_email, customers_dot: customers_dot_url }
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::ManualBanner do
let(:manual_banner) { described_class.new(actionable: nil) }
describe '#display?' do
subject(:display?) { manual_banner.display? }
let(:should_check_namespace_plan?) { false } # indicates a self-managed instance
let(:feature_flag_enabled) { true }
before do
allow(Gitlab::CurrentSettings).to receive(:should_check_namespace_plan?) { should_check_namespace_plan? }
stub_feature_flags(automated_email_provision: feature_flag_enabled)
end
context 'when on GitLab.com' do
let(:should_check_namespace_plan?) { true }
it { is_expected.to eq(false) }
end
context 'when feature flag :automated_email_provision is disabled' do
let(:feature_flag_enabled) { false }
it { is_expected.to eq(false) }
end
it { expect { display? }.to raise_error(NotImplementedError) }
end
describe '#subject' do
subject(:banner_subject) { manual_banner.subject }
before do
allow(manual_banner).to receive(:display?).and_return(display)
end
context 'when banner should not be displayed' do
let(:display) { false }
it { is_expected.to eq(nil) }
end
context 'when banner should be displayed' do
let(:display) { true }
it { expect { banner_subject }.to raise_error(NotImplementedError) }
end
end
describe '#body' do
subject(:banner_body) { manual_banner.body }
before do
allow(manual_banner).to receive(:display?).and_return(display)
end
context 'when banner should not be displayed' do
let(:display) { false }
it { is_expected.to eq(nil) }
end
context 'when banner should be displayed' do
let(:display) { true }
it { expect { banner_body }.to raise_error(NotImplementedError) }
end
end
describe 'display_error_version?' do
it { expect { manual_banner.display_error_version? }.to raise_error(NotImplementedError) }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::ManualRenewalBanner do
include ActionView::Helpers::SanitizeHelper
let(:manual_renewal_banner) { described_class.new(actionable: license) }
let(:license) { build(:license, expires_at: expires_at, plan: plan) }
let(:expires_at) { Date.current + 1.year }
let(:plan) { License::ULTIMATE_PLAN }
describe '#display?' do
subject(:display?) { manual_renewal_banner.display? }
let(:should_check_namespace_plan?) { false } # indicates a self-managed instance
let(:feature_flag_enabled) { true }
before do
allow(Gitlab::CurrentSettings).to receive(:should_check_namespace_plan?) { should_check_namespace_plan? }
stub_feature_flags(automated_email_provision: feature_flag_enabled)
end
context 'when on GitLab.com' do
let(:should_check_namespace_plan?) { true }
it { is_expected.to eq(false) }
end
context 'when feature flag :automated_email_provision is disabled' do
let(:feature_flag_enabled) { false }
it { is_expected.to eq(false) }
end
context 'when license does not expire' do
let(:expires_at) { nil }
it { is_expected.to eq(false) }
end
context 'when a future dated license is present' do
before do
allow(License).to receive(:future_dated).and_return(build(:license))
end
it { is_expected.to eq(false) }
end
context 'when expiration date is not within the notification window' do
let(:expires_at) { Date.tomorrow + described_class::REMINDER_DAYS }
it { is_expected.to eq(false) }
end
context 'when expiration date is within the notification window' do
context 'when notification window starts today' do
let(:expires_at) { Date.today + described_class::REMINDER_DAYS }
it { is_expected.to eq(true) }
end
context 'when notification window is already on going' do
let(:expires_at) { Date.yesterday + described_class::REMINDER_DAYS }
it { is_expected.to eq(true) }
end
end
end
describe '#subject' do
subject { manual_renewal_banner.subject }
before do
allow(manual_renewal_banner).to receive(:display?).and_return(display)
end
context 'when banner should not be displayed' do
let(:display) { false }
it { is_expected.to eq(nil) }
end
context 'when banner should be displayed' do
let(:display) { true }
context 'when license is not yet expired but within the notification window' do
shared_examples 'an expiring license' do
it { is_expected.to eq("Your #{plan.capitalize} subscription expires on #{license.expires_at}") }
end
context 'when notification date is today' do
let(:expires_at) { Date.today + described_class::REMINDER_DAYS }
it_behaves_like 'an expiring license'
end
context 'when notification date is within the next 14 days' do
let(:expires_at) { Date.yesterday + described_class::REMINDER_DAYS }
it_behaves_like 'an expiring license'
end
end
context 'when license is already expired' do
let(:expires_at) { Date.today }
it { is_expected.to eq("Your #{plan.capitalize} subscription expired on #{license.expires_at}") }
end
end
end
describe '#body' do
subject(:body) { strip_tags(manual_renewal_banner.body) }
before do
allow(manual_renewal_banner).to receive(:display?).and_return(display)
end
context 'when banner should not be displayed' do
let(:display) { false }
it { is_expected.to eq(nil) }
end
context 'when banner should be displayed' do
let(:display) { true }
context 'when license is not yet expired but within the notification window' do
shared_examples 'an expiring license' do
it 'returns a message to renew for an expiring license' do
expect(body).to eq(
"To renew, export your license usage file and email it to " \
"#{Gitlab::SubscriptionPortal::RENEWAL_SERVICE_EMAIL}. A new license will be emailed to the email " \
"address registered in the Customers Portal. You can upload this license to your instance."
)
end
end
context 'when notification date is today' do
let(:expires_at) { Date.today + described_class::REMINDER_DAYS }
it_behaves_like 'an expiring license'
end
context 'when notification date is within the next 14 days' do
let(:expires_at) { Date.yesterday + described_class::REMINDER_DAYS }
it_behaves_like 'an expiring license'
end
end
context 'when license is already expired' do
let(:expires_at) { Date.today }
it 'returns a message to renew for an expired license' do
expect(body).to eq(
"Your subscription is now expired. To renew, export your license usage file and email it to " \
"#{Gitlab::SubscriptionPortal::RENEWAL_SERVICE_EMAIL}. A new license will be emailed to the email " \
"address registered in the Customers Portal. You can upload this license to your instance. To use " \
"Free tier, remove your current license."
)
end
end
end
end
describe 'display_error_version?' do
subject(:display_error_version?) { manual_renewal_banner.display_error_version? }
context 'when license is not expired' do
it { is_expected.to eq(false) }
end
context 'when license is expired' do
let(:expires_at) { Date.today }
it { is_expected.to eq(true) }
end
end
end
...@@ -62,6 +62,10 @@ module Gitlab ...@@ -62,6 +62,10 @@ module Gitlab
"#{self.subscriptions_url}/gitlab/namespaces/#{group_id}/renew" "#{self.subscriptions_url}/gitlab/namespaces/#{group_id}/renew"
end end
def self.edit_account_url
"#{self.subscriptions_url}/customers/edit"
end
def self.subscription_portal_admin_email def self.subscription_portal_admin_email
ENV.fetch('SUBSCRIPTION_PORTAL_ADMIN_EMAIL', 'gl_com_api@gitlab.com') ENV.fetch('SUBSCRIPTION_PORTAL_ADMIN_EMAIL', 'gl_com_api@gitlab.com')
end end
...@@ -69,9 +73,14 @@ module Gitlab ...@@ -69,9 +73,14 @@ module Gitlab
def self.subscription_portal_admin_token def self.subscription_portal_admin_token
ENV.fetch('SUBSCRIPTION_PORTAL_ADMIN_TOKEN', 'customer_admin_token') ENV.fetch('SUBSCRIPTION_PORTAL_ADMIN_TOKEN', 'customer_admin_token')
end end
def self.renewal_service_email
'renewals-support@gitlab.com'
end
end end
end end
Gitlab::SubscriptionPortal.prepend_mod Gitlab::SubscriptionPortal.prepend_mod
Gitlab::SubscriptionPortal::SUBSCRIPTIONS_URL = Gitlab::SubscriptionPortal.subscriptions_url.freeze Gitlab::SubscriptionPortal::SUBSCRIPTIONS_URL = Gitlab::SubscriptionPortal.subscriptions_url.freeze
Gitlab::SubscriptionPortal::PAYMENT_FORM_URL = Gitlab::SubscriptionPortal.payment_form_url.freeze Gitlab::SubscriptionPortal::PAYMENT_FORM_URL = Gitlab::SubscriptionPortal.payment_form_url.freeze
Gitlab::SubscriptionPortal::RENEWAL_SERVICE_EMAIL = Gitlab::SubscriptionPortal.renewal_service_email.freeze
...@@ -36675,6 +36675,9 @@ msgstr "" ...@@ -36675,6 +36675,9 @@ msgstr ""
msgid "To receive alerts from manually configured Prometheus services, add the following URL and Authorization key to your Prometheus webhook config file. Learn more about %{linkStart}configuring Prometheus%{linkEnd} to send alerts to GitLab." msgid "To receive alerts from manually configured Prometheus services, add the following URL and Authorization key to your Prometheus webhook config file. Learn more about %{linkStart}configuring Prometheus%{linkEnd} to send alerts to GitLab."
msgstr "" msgstr ""
msgid "To renew, export your license usage file and email it to %{renewal_service_email}. A new license will be emailed to the email address registered in the %{customers_dot}. You can upload this license to your instance."
msgstr ""
msgid "To resolve this, try to:" msgid "To resolve this, try to:"
msgstr "" msgstr ""
...@@ -40473,6 +40476,12 @@ msgstr "" ...@@ -40473,6 +40476,12 @@ msgstr ""
msgid "Your %{host} account was signed in to from a new location" msgid "Your %{host} account was signed in to from a new location"
msgstr "" msgstr ""
msgid "Your %{plan} subscription expired on %{expiry_date}"
msgstr ""
msgid "Your %{plan} subscription expires on %{expiry_date}"
msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription expires on %{strong}%{expires_on}%{strong_close}. After that date, you cannot create issues or merge requests, or use many other features." msgid "Your %{strong}%{plan_name}%{strong_close} subscription expires on %{strong}%{expires_on}%{strong_close}. After that date, you cannot create issues or merge requests, or use many other features."
msgstr "" msgstr ""
...@@ -40764,6 +40773,9 @@ msgstr "" ...@@ -40764,6 +40773,9 @@ msgstr ""
msgid "Your subscription expired!" msgid "Your subscription expired!"
msgstr "" msgstr ""
msgid "Your subscription is now expired. To renew, export your license usage file and email it to %{renewal_service_email}. A new license will be emailed to the email address registered in the %{customers_dot}. You can upload this license to your instance. To use Free tier, remove your current license."
msgstr ""
msgid "Your subscription will expire in %{remaining_days} day." msgid "Your subscription will expire in %{remaining_days} day."
msgid_plural "Your subscription will expire in %{remaining_days} days." msgid_plural "Your subscription will expire in %{remaining_days} days."
msgstr[0] "" msgstr[0] ""
......
...@@ -63,6 +63,7 @@ RSpec.describe ::Gitlab::SubscriptionPortal do ...@@ -63,6 +63,7 @@ RSpec.describe ::Gitlab::SubscriptionPortal do
:subscriptions_plans_url | 'https://about.gitlab.com/pricing/' :subscriptions_plans_url | 'https://about.gitlab.com/pricing/'
:subscriptions_instance_review_url | 'https://customers.staging.gitlab.com/instance_review' :subscriptions_instance_review_url | 'https://customers.staging.gitlab.com/instance_review'
:subscriptions_gitlab_plans_url | 'https://customers.staging.gitlab.com/gitlab_plans' :subscriptions_gitlab_plans_url | 'https://customers.staging.gitlab.com/gitlab_plans'
:edit_account_url | 'https://customers.staging.gitlab.com/customers/edit'
end end
with_them do with_them 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