Commit 7dc271d5 authored by Vijay Hawoldar's avatar Vijay Hawoldar Committed by Mayra Cabrera

Add a new service GitlabSubscriptions::CheckLastTermService

To be used to query our CustomersDot application to determine
if the current subscription for the given namespace is on it's
last term or not
parent f11f9e37
# frozen_string_literal: true
# Service to determine if the given namespace has a future renewal
# created in the CustomersDot application
#
# If there is a problem querying CustomersDot, it assumes there is no
# future renewal
#
# returns true, false
module GitlabSubscriptions
class CheckFutureRenewalService
def initialize(namespace_id:)
@namespace_id = namespace_id
end
def execute
return false unless Feature.enabled?(:gitlab_subscription_future_renewal, default_enabled: :yaml)
future_renewal
end
private
attr_reader :namespace_id
def client
Gitlab::SubscriptionPortal::Client
end
def last_term_request
response = client.subscription_last_term(namespace_id)
if response[:success]
response[:last_term] == false
else
nil
end
end
def cache
Rails.cache
end
def cache_key
"subscription:future_renewal:namespace:#{namespace_id}"
end
def future_renewal
cache.fetch(cache_key, skip_nil: true, expires_in: 1.day) { last_term_request } || false
end
end
end
---
name: gitlab_subscription_future_renewal
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56235
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/324157
milestone: '13.11'
type: development
group: group::utilization
default_enabled: false
......@@ -119,10 +119,16 @@ module Gitlab
subscribable && ((is_admin && subscribable.notify_admins?) || subscribable.notify_users?)
end
def subscription_future_renewal?
return if self_managed? || namespace.nil?
::GitlabSubscriptions::CheckFutureRenewalService.new(namespace_id: namespace.id).execute
end
def require_notification?
return false if expiring_auto_renew? || ::License.future_dated.present?
auto_renew_choice_exists? && expired_subscribable_within_notification_window?
auto_renew_choice_exists? && expired_subscribable_within_notification_window? && !subscription_future_renewal?
end
def auto_renew_choice_exists?
......
......@@ -34,7 +34,7 @@ module Gitlab
)
if !response[:success] || response.dig(:data, 'errors').present?
return { success: false, errors: response.dig(:data, 'errors') }
return error(response.dig(:data, 'errors'))
end
response = response.dig(:data, 'data', 'cloudActivationActivate')
......@@ -42,7 +42,7 @@ module Gitlab
if response['errors'].blank?
{ success: true, license_key: response['licenseKey'] }
else
{ success: false, errors: response['errors'] }
error(response['errors'])
end
end
......@@ -71,7 +71,7 @@ module Gitlab
free_upgrade_plan_id: free_upgrade
}
else
{ success: false }
error
end
end
......@@ -90,7 +90,8 @@ module Gitlab
if response['errors'].present?
exception = SubscriptionPortal::Client::ResponseError.new("Received an error from CustomerDot")
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(exception, query: query, response: response)
return { success: false }
return error
end
{
......@@ -100,6 +101,26 @@ module Gitlab
}
end
def subscription_last_term(namespace_id)
return error('Must provide a namespace ID') unless namespace_id
query = <<~GQL
query($namespaceId: ID!) {
subscription(namespaceId: $namespaceId) {
lastTerm
}
}
GQL
response = execute_graphql_query({ query: query, variables: { namespaceId: namespace_id } })
if response[:success]
{ success: true, last_term: response.dig(:data, 'data', 'subscription', 'lastTerm') }
else
error(response.dig(:data, :errors))
end
end
private
def execute_graphql_query(params)
......@@ -115,6 +136,13 @@ module Gitlab
def graphql_endpoint
EE::SUBSCRIPTIONS_GRAPHQL_URL
end
def error(errors = nil)
{
success: false,
errors: errors
}.compact
end
end
end
end
......
......@@ -110,7 +110,15 @@ RSpec.describe Gitlab::ExpiringSubscriptionMessage do
end
context 'with namespace' do
let(:namespace) { double(:namespace, name: 'No Limit Records') }
let(:has_future_renewal) { false }
let_it_be(:namespace) { create(:group, name: 'No Limit Records') }
before do
allow_next_instance_of(GitlabSubscriptions::CheckFutureRenewalService, namespace_id: namespace.id) do |service|
allow(service).to receive(:execute).and_return(has_future_renewal)
end
end
it 'has an expiration blocking message' do
expect(subject).to include("You didn't renew your subscription for No Limit Records so it was downgraded to the free plan")
......@@ -127,6 +135,12 @@ RSpec.describe Gitlab::ExpiringSubscriptionMessage do
expect(subject).to include("We tried to automatically renew your subscription for No Limit Records on 2020-03-01 but something went wrong so your subscription was downgraded to the free plan. Don't worry, your data is safe. We suggest you check your payment method and get in touch with our support team (support@gitlab.com). They'll gladly help with your subscription renewal.")
end
end
context 'when there is a future renewal' do
let(:has_future_renewal) { true }
it { is_expected.to be_nil }
end
end
end
......@@ -184,7 +198,15 @@ RSpec.describe Gitlab::ExpiringSubscriptionMessage do
context 'with namespace' do
using RSpec::Parameterized::TableSyntax
let(:namespace) { double(:namespace, name: 'No Limit Records') }
let(:has_future_renewal) { false }
let_it_be(:namespace) { create(:group, name: 'No Limit Records') }
before do
allow_next_instance_of(GitlabSubscriptions::CheckFutureRenewalService, namespace_id: namespace.id) do |service|
allow(service).to receive(:execute).and_return(has_future_renewal)
end
end
where plan: %w(gold ultimate)
......@@ -227,6 +249,12 @@ RSpec.describe Gitlab::ExpiringSubscriptionMessage do
expect(subject).to be nil
end
end
context 'when there is a future renewal' do
let(:has_future_renewal) { true }
it { is_expected.to be_nil }
end
end
end
......
......@@ -182,4 +182,66 @@ RSpec.describe Gitlab::SubscriptionPortal::Clients::Graphql do
end
end
end
describe '#subscription_last_term' do
let(:query) do
<<~GQL
query($namespaceId: ID!) {
subscription(namespaceId: $namespaceId) {
lastTerm
}
}
GQL
end
it 'returns success' do
expected_args = {
query: query,
variables: {
namespaceId: 'namespace-id'
}
}
expected_response = {
success: true,
data: {
"data" => {
"subscription" => {
"lastTerm" => true
}
}
}
}
expect(client).to receive(:execute_graphql_query).with(expected_args).and_return(expected_response)
result = client.subscription_last_term('namespace-id')
expect(result).to eq({ success: true, last_term: true })
end
it 'returns failure' do
error = "some error"
expect(client).to receive(:execute_graphql_query).and_return(
{
success: false,
data: {
errors: error
}
}
)
result = client.subscription_last_term('failing-namespace-id')
expect(result).to eq({ success: false, errors: error })
end
context 'with no namespace_id' do
it 'returns failure' do
expect(client).not_to receive(:execute_graphql_query)
expect(client.subscription_last_term(nil)).to eq({ success: false, errors: 'Must provide a namespace ID' })
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSubscriptions::CheckFutureRenewalService, :use_clean_rails_memory_store_caching do
using RSpec::Parameterized::TableSyntax
describe '#execute' do
let(:namespace) { create(:namespace) }
let(:namespace_id) { namespace.id }
let(:cache_key) { "subscription:future_renewal:namespace:#{namespace_id}" }
subject(:execute_service) { described_class.new(namespace_id: namespace_id).execute }
where(:in_last_term, :expected_response) do
true | false
false | true
end
with_them do
let(:response) { { success: true, last_term: in_last_term } }
before do
allow(Gitlab::SubscriptionPortal::Client).to receive(:subscription_last_term).and_return(response)
end
it 'returns the correct value' do
expect(execute_service).to eq expected_response
end
it 'caches the query response' do
expect(Rails.cache).to receive(:fetch).with(cache_key, skip_nil: true, expires_in: 1.day).and_call_original
execute_service
end
end
context 'with an unsuccessful CustomersDot query' do
it 'assumes no future renewal' do
allow(Gitlab::SubscriptionPortal::Client).to receive(:subscription_last_term).and_return({
success: false
})
expect(execute_service).to be false
end
end
context 'when the `gitlab_subscription_future_renewal` feature flag is disabled' do
before do
stub_feature_flags(gitlab_subscription_future_renewal: false)
end
it { is_expected.to be false }
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