Commit 636c36aa authored by James Lopez's avatar James Lopez

Merge branch '323357-mlunoe-subscription-plans-service' into 'master'

Feat(subscriptions helper): fetch ci minutes data

See merge request gitlab-org/gitlab!56216
parents ce6ed97f 169404d6
...@@ -8,6 +8,7 @@ module SubscriptionsHelper ...@@ -8,6 +8,7 @@ module SubscriptionsHelper
setup_for_company: (current_user.setup_for_company == true).to_s, setup_for_company: (current_user.setup_for_company == true).to_s,
full_name: current_user.name, full_name: current_user.name,
available_plans: subscription_available_plans.to_json, available_plans: subscription_available_plans.to_json,
ci_minutes_plans: ci_minutes_plans.to_json,
plan_id: params[:plan_id], plan_id: params[:plan_id],
namespace_id: params[:namespace_id], namespace_id: params[:namespace_id],
new_user: new_user?.to_s, new_user: new_user?.to_s,
...@@ -43,6 +44,26 @@ module SubscriptionsHelper ...@@ -43,6 +44,26 @@ module SubscriptionsHelper
plans_data.reject { |plan_data| plan_data[:deprecated] } plans_data.reject { |plan_data| plan_data[:deprecated] }
end end
def ci_minutes_plans
return if ::Feature.disabled?(:new_route_ci_minutes_purchase, default_enabled: :yaml)
strong_memoize(:ci_minutes_plans) do
fields = %w[
name
code
active
free
price_per_month
price_per_year
features
about_page_href
hide_deprecated_card
]
Gitlab::SubscriptionPortal::Client.plan_data("CI_1000_MINUTES_PLAN", fields)[:plans]
end
end
def group_data def group_data
current_user.manageable_groups_eligible_for_subscription.with_counts(archived: false).map do |namespace| current_user.manageable_groups_eligible_for_subscription.with_counts(archived: false).map do |namespace|
{ {
......
...@@ -6,6 +6,8 @@ module Gitlab ...@@ -6,6 +6,8 @@ module Gitlab
include SubscriptionPortal::Clients::REST include SubscriptionPortal::Clients::REST
include SubscriptionPortal::Clients::Graphql include SubscriptionPortal::Clients::Graphql
ResponseError = Class.new(StandardError)
class << self class << self
private private
......
...@@ -57,7 +57,7 @@ module Gitlab ...@@ -57,7 +57,7 @@ module Gitlab
} }
GQL GQL
response = execute_graphql_query({ query: query }).dig(:data) response = execute_graphql_query({ query: query })[:data]
if response['errors'].blank? if response['errors'].blank?
eligible = response.dig('data', 'subscription', 'eoaStarterBronzeEligible') eligible = response.dig('data', 'subscription', 'eoaStarterBronzeEligible')
...@@ -75,6 +75,31 @@ module Gitlab ...@@ -75,6 +75,31 @@ module Gitlab
end end
end end
def plan_data(plan_tags, fields)
query = <<~GQL
query($tags: [PlanTag!]) {
plans(planTags: $tags) {
deprecated
#{(fields - ['deprecated']).map { |field| "#{field}: #{field.to_s.camelize(:lower)}" }.join(" ")}
}
}
GQL
response = execute_graphql_query({ query: query, variables: { tags: plan_tags } })[:data]
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 }
end
{
success: true,
plans: response.dig('data', 'plans')
.reject { |plan| plan['deprecated'] }
}
end
private private
def execute_graphql_query(params) def execute_graphql_query(params)
......
...@@ -3,13 +3,17 @@ ...@@ -3,13 +3,17 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Subscriptions Content Security Policy' do RSpec.describe 'Subscriptions Content Security Policy' do
include SubscriptionPortalHelpers
subject { response_headers['Content-Security-Policy'] } subject { response_headers['Content-Security-Policy'] }
let_it_be(:default_csp_values) { "'self' https://some-cdn.test" } let_it_be(:default_csp_values) { "'self' https://some-cdn.test" }
let_it_be(:zuora_url) { 'https://*.zuora.com' } let_it_be(:zuora_url) { 'https://*.zuora.com' }
let(:plan_tags) { 'CI_1000_MINUTES_PLAN' }
before do before do
stub_request(:get, /.*gitlab_plans.*/).to_return(status: 200, body: "{}") stub_request(:get, /.*gitlab_plans.*/).to_return(status: 200, body: "{}")
stub_plan_data_request(plan_tags)
expect_next_instance_of(SubscriptionsController) do |controller| expect_next_instance_of(SubscriptionsController) do |controller|
expect(controller).to receive(:current_content_security_policy).and_return(csp) expect(controller).to receive(:current_content_security_policy).and_return(csp)
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe SubscriptionsHelper do RSpec.describe SubscriptionsHelper do
include SubscriptionPortalHelpers
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
let_it_be(:free_plan) do let_it_be(:free_plan) do
...@@ -23,12 +25,15 @@ RSpec.describe SubscriptionsHelper do ...@@ -23,12 +25,15 @@ RSpec.describe SubscriptionsHelper do
[free_plan, bronze_plan] [free_plan, bronze_plan]
end end
let(:plan_tags) { 'CI_1000_MINUTES_PLAN' }
before do before do
stub_feature_flags(hide_deprecated_billing_plans: false) stub_feature_flags(hide_deprecated_billing_plans: false)
allow(helper).to receive(:params).and_return(plan_id: 'bronze_id', namespace_id: nil) allow(helper).to receive(:params).and_return(plan_id: 'bronze_id', namespace_id: nil)
allow_next_instance_of(GitlabSubscriptions::FetchSubscriptionPlansService) do |instance| allow_next_instance_of(GitlabSubscriptions::FetchSubscriptionPlansService) do |instance|
allow(instance).to receive(:execute).and_return(raw_plan_data) allow(instance).to receive(:execute).and_return(raw_plan_data)
end end
stub_plan_data_request(plan_tags)
end end
describe '#subscription_data' do describe '#subscription_data' do
...@@ -46,6 +51,7 @@ RSpec.describe SubscriptionsHelper do ...@@ -46,6 +51,7 @@ RSpec.describe SubscriptionsHelper do
it { is_expected.to include(setup_for_company: 'false') } it { is_expected.to include(setup_for_company: 'false') }
it { is_expected.to include(full_name: 'First Last') } it { is_expected.to include(full_name: 'First Last') }
it { is_expected.to include(available_plans: '[{"id":"bronze_id","code":"bronze","price_per_year":48.0,"name":"Bronze Plan"}]') } it { is_expected.to include(available_plans: '[{"id":"bronze_id","code":"bronze","price_per_year":48.0,"name":"Bronze Plan"}]') }
it { is_expected.to include(ci_minutes_plans: '[{"name":"1000 CI minutes pack","code":"ci_minutes","active":true,"deprecated":false,"free":null,"price_per_month":0.8333333333333334,"price_per_year":10,"features":null,"about_page_href":null,"hide_deprecated_card":false}]') }
it { is_expected.to include(plan_id: 'bronze_id') } it { is_expected.to include(plan_id: 'bronze_id') }
it { is_expected.to include(namespace_id: group.id.to_s) } it { is_expected.to include(namespace_id: group.id.to_s) }
it { is_expected.to include(group_data: %Q{[{"id":#{group.id},"name":"My Namespace","users":1}]}) } it { is_expected.to include(group_data: %Q{[{"id":#{group.id},"name":"My Namespace","users":1}]}) }
...@@ -103,6 +109,14 @@ RSpec.describe SubscriptionsHelper do ...@@ -103,6 +109,14 @@ RSpec.describe SubscriptionsHelper do
it { is_expected.to include(available_plans: '[]') } it { is_expected.to include(available_plans: '[]') }
end end
end end
context 'when ff new_route_ci_minutes_purchase is disabled' do
before do
stub_feature_flags(new_route_ci_minutes_purchase: false)
end
it { is_expected.not_to include(ci_minutes_plans: '[{"name":"1000 CI minutes pack","code":"ci_minutes","active":true,"deprecated":false,"free":null,"price_per_month":0.8333333333333334,"price_per_year":10,"features":null,"about_page_href":null,"hide_deprecated_card":false}]') }
end
end end
describe '#plan_title' do describe '#plan_title' do
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::SubscriptionPortal::Clients::Graphql do RSpec.describe Gitlab::SubscriptionPortal::Clients::Graphql do
include SubscriptionPortalHelpers
let(:client) { Gitlab::SubscriptionPortal::Client } let(:client) { Gitlab::SubscriptionPortal::Client }
describe '#activate' do describe '#activate' do
...@@ -123,4 +125,61 @@ RSpec.describe Gitlab::SubscriptionPortal::Clients::Graphql do ...@@ -123,4 +125,61 @@ RSpec.describe Gitlab::SubscriptionPortal::Clients::Graphql do
end end
end end
end end
describe '#plan_data' do
let(:plan_tags) { 'CI_1000_MINUTES_PLAN' }
subject(:plan_data) { client.plan_data(plan_tags, stubbed_plan_data_query_fields) }
context 'when the response contains errors' do
before do
expect(client).to receive(:execute_graphql_query).and_return(response)
end
let(:response) do
{
success: true,
data: {
'errors' => [{ 'message' => 'this will be ignored' }]
}
}
end
it 'logs an error and returns a failure' do
expect(Gitlab::ErrorTracking)
.to receive(:track_and_raise_for_dev_exception)
.with(
a_kind_of(Gitlab::SubscriptionPortal::Client::ResponseError),
query: include(*stubbed_plan_data_query_fields_camelized), response: response[:data])
expect(plan_data).to eq({ success: false })
end
end
context 'when the response does not contain errors' do
before do
allow(client).to receive(:execute_graphql_query).and_return({ data: Gitlab::Json.parse(stubbed_plan_data_response_body) })
end
it 'filters out the deprecated plans' do
expect(plan_data).to match({
success: true,
plans: contain_exactly(include('deprecated' => false))
})
end
context 'when plans is an empty array' do
before do
allow(client).to receive(:execute_graphql_query).and_return({
success: true,
data: { "data" => { "plans" => [] } }
})
end
it 'returns the correct response' do
expect(plan_data).to eq({ success: true, plans: [] })
end
end
end
end
end end
...@@ -21,6 +21,24 @@ module SubscriptionPortalHelpers ...@@ -21,6 +21,24 @@ module SubscriptionPortalHelpers
) )
end end
def stub_plan_data_request(plan_tags)
stub_full_request("#{EE::SUBSCRIPTIONS_URL}/graphql", method: :post)
.with(
body: include(*plan_tags, *stubbed_plan_data_query_fields_camelized),
headers: {
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'X-Admin-Email' => EE::SUBSCRIPTION_PORTAL_ADMIN_EMAIL,
'X-Admin-Token' => EE::SUBSCRIPTION_PORTAL_ADMIN_TOKEN
}
)
.to_return(
status: 200,
headers: { 'Content-Type' => 'application/json' },
body: stubbed_plan_data_response_body
)
end
private private
def stubbed_eoa_eligibility_response_body def stubbed_eoa_eligibility_response_body
...@@ -34,4 +52,55 @@ module SubscriptionPortalHelpers ...@@ -34,4 +52,55 @@ module SubscriptionPortalHelpers
} }
}.to_json }.to_json
end end
def stubbed_plan_data_query_fields
%w[
name
code
active
free
price_per_month
price_per_year
features
about_page_href
hide_deprecated_card
]
end
def stubbed_plan_data_query_fields_camelized
stubbed_plan_data_query_fields.map { |field| field.to_s.camelize(:lower) }
end
def stubbed_plan_data_response_body
{
"data": {
"plans": [
{
"name": "1000 CI minutes pack",
"code": "ci_minutes",
"active": true,
"deprecated": false,
"free": nil,
"price_per_month": 0.8333333333333334,
"price_per_year": 10,
"features": nil,
"about_page_href": nil,
"hide_deprecated_card": false
},
{
"name": "Deprecated 1000 CI minutes pack",
"code": "deprecated_ci_minutes",
"active": true,
"deprecated": true,
"free": nil,
"price_per_month": 1,
"price_per_year": 12,
"features": nil,
"about_page_href": nil,
"hide_deprecated_card": false
}
]
}
}.to_json
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