Commit ba20f4e4 authored by Vijay Hawoldar's avatar Vijay Hawoldar Committed by Luke Duncalfe

Refactor Gitlab::SubscriptionPortal::Client into concerns

Our subscriptions portal (CustomersDot) is growing it's
support for GraphQL. Currently all queries are routed through
one Client class, this commit splits it into REST and Graphql
concerns accordingly to make it more manageable
parent d4638e5d
......@@ -3,6 +3,7 @@
module EE
SUBSCRIPTIONS_URL = ::Gitlab::SubscriptionPortal::SUBSCRIPTIONS_URL
SUBSCRIPTIONS_COMPARISON_URL = "https://about.gitlab.com/pricing/gitlab-com/feature-comparison".freeze
SUBSCRIPTIONS_GRAPHQL_URL = "#{SUBSCRIPTIONS_URL}/graphql".freeze
SUBSCRIPTIONS_MORE_MINUTES_URL = "#{SUBSCRIPTIONS_URL}/buy_pipeline_minutes".freeze
SUBSCRIPTIONS_MORE_STORAGE_URL = "#{SUBSCRIPTIONS_URL}/buy_storage".freeze
SUBSCRIPTIONS_PLANS_URL = "#{SUBSCRIPTIONS_URL}/plans".freeze
......
......@@ -3,99 +3,12 @@
module Gitlab
module SubscriptionPortal
class Client
class << self
def generate_trial(params)
http_post("trials", admin_headers, params)
end
def create_customer(params)
http_post("api/customers", admin_headers, params)
end
def create_subscription(params, email, token)
http_post("subscriptions", customer_headers(email, token), params)
end
def payment_form_params(payment_type)
http_get("payment_forms/#{payment_type}", admin_headers)
end
def payment_method(id)
http_get("api/payment_methods/#{id}", admin_headers)
end
def activate(activation_code)
uuid = Gitlab::CurrentSettings.uuid
query = <<~GQL
mutation {
cloudActivationActivate(input: { activationCode: "#{activation_code}", instanceIdentifier: "#{uuid}" }) {
authenticationToken
errors
}
}
GQL
response = http_post("graphql", admin_headers, { query: query }).dig(:data, 'data', 'cloudActivationActivate')
if response['errors'].blank?
{ success: true, authentication_token: response['authenticationToken'] }
else
{ success: false, errors: response['errors'] }
end
end
def plan_upgrade_offer(namespace_id)
query = <<~GQL
{
subscription(namespaceId: "#{namespace_id}") {
eoaStarterBronzeEligible
assistedUpgradePlanId
freeUpgradePlanId
}
}
GQL
response = http_post("graphql", admin_headers, { query: query }).dig(:data)
if response['errors'].blank?
eligible = response.dig('data', 'subscription', 'eoaStarterBronzeEligible')
assisted_upgrade = response.dig('data', 'subscription', 'assistedUpgradePlanId')
free_upgrade = response.dig('data', 'subscription', 'freeUpgradePlanId')
{
success: true,
eligible_for_free_upgrade: eligible,
assisted_upgrade_plan_id: assisted_upgrade,
free_upgrade_plan_id: free_upgrade
}
else
{ success: false }
end
end
include SubscriptionPortal::Clients::REST
include SubscriptionPortal::Clients::Graphql
class << self
private
def http_get(path, headers)
response = Gitlab::HTTP.get("#{base_url}/#{path}", headers: headers)
parse_response(response)
rescue *Gitlab::HTTP::HTTP_ERRORS => e
{ success: false, data: { errors: e.message } }
end
def http_post(path, headers, params = {})
response = Gitlab::HTTP.post("#{base_url}/#{path}", body: params.to_json, headers: headers)
parse_response(response)
rescue *Gitlab::HTTP::HTTP_ERRORS => e
{ success: false, data: { errors: e.message } }
end
def base_url
EE::SUBSCRIPTIONS_URL
end
def json_headers
{
'Accept' => 'application/json',
......
# frozen_string_literal: true
module Gitlab
module SubscriptionPortal
module Clients
module Graphql
extend ActiveSupport::Concern
class_methods do
def activate(activation_code)
uuid = Gitlab::CurrentSettings.uuid
query = <<~GQL
mutation {
cloudActivationActivate(input: { activationCode: "#{activation_code}", instanceIdentifier: "#{uuid}" }) {
authenticationToken
errors
}
}
GQL
response = execute_graphql_query(query).dig(:data, 'data', 'cloudActivationActivate')
if response['errors'].blank?
{ success: true, authentication_token: response['authenticationToken'] }
else
{ success: false, errors: response['errors'] }
end
end
def plan_upgrade_offer(namespace_id)
query = <<~GQL
{
subscription(namespaceId: "#{namespace_id}") {
eoaStarterBronzeEligible
assistedUpgradePlanId
freeUpgradePlanId
}
}
GQL
response = execute_graphql_query(query).dig(:data)
if response['errors'].blank?
eligible = response.dig('data', 'subscription', 'eoaStarterBronzeEligible')
assisted_upgrade = response.dig('data', 'subscription', 'assistedUpgradePlanId')
free_upgrade = response.dig('data', 'subscription', 'freeUpgradePlanId')
{
success: true,
eligible_for_free_upgrade: eligible,
assisted_upgrade_plan_id: assisted_upgrade,
free_upgrade_plan_id: free_upgrade
}
else
{ success: false }
end
end
private
def execute_graphql_query(query)
response = ::Gitlab::HTTP.post(
graphql_endpoint,
headers: admin_headers,
body: {
query: query
}.to_json
)
parse_response(response)
end
def graphql_endpoint
EE::SUBSCRIPTIONS_GRAPHQL_URL
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module SubscriptionPortal
module Clients
module REST
extend ActiveSupport::Concern
class_methods do
def generate_trial(params)
http_post("trials", admin_headers, params)
end
def create_customer(params)
http_post("api/customers", admin_headers, params)
end
def create_subscription(params, email, token)
http_post("subscriptions", customer_headers(email, token), params)
end
def payment_form_params(payment_type)
http_get("payment_forms/#{payment_type}", admin_headers)
end
def payment_method(id)
http_get("api/payment_methods/#{id}", admin_headers)
end
private
def base_url
EE::SUBSCRIPTIONS_URL
end
def http_get(path, headers)
response = Gitlab::HTTP.get("#{base_url}/#{path}", headers: headers)
parse_response(response)
rescue *Gitlab::HTTP::HTTP_ERRORS => e
{ success: false, data: { errors: e.message } }
end
def http_post(path, headers, params = {})
response = Gitlab::HTTP.post("#{base_url}/#{path}", body: params.to_json, headers: headers)
parse_response(response)
rescue *Gitlab::HTTP::HTTP_ERRORS => e
{ success: false, data: { errors: e.message } }
end
end
end
end
end
end
......@@ -3,234 +3,8 @@
require 'spec_helper'
RSpec.describe Gitlab::SubscriptionPortal::Client do
let(:http_response) { nil }
let(:httparty_response) do
double(code: http_response.code, response: http_response, body: {}, parsed_response: {})
end
subject { described_class }
let(:http_method) { :post }
shared_examples 'when response is successful' do
let(:http_response) { Net::HTTPSuccess.new(1.0, '201', 'OK') }
it 'has a successful status' do
allow(Gitlab::HTTP).to receive(http_method).and_return(httparty_response)
expect(subject[:success]).to eq(true)
end
end
shared_examples 'when response code is 422' do
let(:http_response) { Net::HTTPUnprocessableEntity.new(1.0, '422', 'Error') }
it 'has a unprocessable entity status' do
allow(Gitlab::HTTP).to receive(http_method).and_return(httparty_response)
expect(subject[:success]).to eq(false)
end
end
shared_examples 'when response code is 500' do
let(:http_response) { Net::HTTPServerError.new(1.0, '500', 'Error') }
it 'has a server error status' do
allow(Gitlab::HTTP).to receive(http_method).and_return(httparty_response)
expect(subject[:success]).to eq(false)
end
end
describe '#create_trial_account' do
subject do
described_class.generate_trial({})
end
it_behaves_like 'when response is successful'
it_behaves_like 'when response code is 422'
it_behaves_like 'when response code is 500'
end
describe '#create_subscription' do
subject do
described_class.create_subscription({}, 'customer@mail.com', 'token')
end
it_behaves_like 'when response is successful'
it_behaves_like 'when response code is 422'
it_behaves_like 'when response code is 500'
end
describe '#create_customer' do
subject do
described_class.create_customer({})
end
it_behaves_like 'when response is successful'
it_behaves_like 'when response code is 422'
it_behaves_like 'when response code is 500'
end
describe '#payment_form_params' do
subject do
described_class.payment_form_params('cc')
end
let(:http_method) { :get }
it_behaves_like 'when response is successful'
it_behaves_like 'when response code is 422'
it_behaves_like 'when response code is 500'
end
describe '#payment_method' do
subject do
described_class.payment_method('1')
end
let(:http_method) { :get }
it_behaves_like 'when response is successful'
it_behaves_like 'when response code is 422'
it_behaves_like 'when response code is 500'
end
describe '#activate' do
let(:authentication_token) { 'authentication_token' }
it 'returns success' do
expect(described_class).to receive(:http_post).and_return(
{
success: true,
data: {
"data" => {
"cloudActivationActivate" => {
"authenticationToken" => authentication_token,
"errors" => []
}
}
}
}
)
result = described_class.activate('activation_code_abc')
expect(result).to eq({ authentication_token: authentication_token, success: true })
end
it 'returns failure' do
expect(described_class).to receive(:http_post).and_return(
{
success: true,
data: {
"data" => {
"cloudActivationActivate" => {
"authenticationToken" => nil,
"errors" => ["invalid activation code"]
}
}
}
}
)
result = described_class.activate('activation_code_abc')
expect(result).to eq({ errors: ["invalid activation code"], success: false })
end
end
describe '#plan_upgrade_offer' do
let(:namespace_id) { 111 }
let(:headers) do
{
"Accept" => "application/json",
"Content-Type" => "application/json",
"X-Admin-Email" => "gl_com_api@gitlab.com",
"X-Admin-Token" => "customer_admin_token"
}
end
let(:params) do
{ query: <<~GQL
{
subscription(namespaceId: "{:namespace_id=>#{namespace_id}}") {
eoaStarterBronzeEligible
assistedUpgradePlanId
freeUpgradePlanId
}
}
GQL
}
end
subject(:plan_upgrade_offer) { described_class.plan_upgrade_offer(namespace_id: namespace_id) }
context 'when the response contains errors' do
before do
expect(described_class).to receive(:http_post).with('graphql', headers, params).and_return(response)
end
let(:response) do
{
success: true,
data: {
'errors' => [{ 'message' => 'this will be ignored' }]
}
}
end
it 'returns a failure' do
expect(plan_upgrade_offer).to eq({ success: false })
end
end
context 'when the response does not contain errors' do
using RSpec::Parameterized::TableSyntax
where(:eligible, :assisted_plan_id, :free_plan_id) do
true | '111111' | '111111'
true | '111111' | nil
true | nil | '111111'
end
with_them do
before do
allow(described_class).to receive(:http_post).and_return({
success: true,
data: { "data" => { "subscription" => {
"eoaStarterBronzeEligible" => eligible,
"assistedUpgradePlanId" => assisted_plan_id,
"freeUpgradePlanId" => free_plan_id
} } }
})
end
it 'returns the correct response' do
expect(plan_upgrade_offer).to eq({
success: true,
eligible_for_free_upgrade: eligible,
assisted_upgrade_plan_id: assisted_plan_id,
free_upgrade_plan_id: free_plan_id
})
end
end
context 'when subscription is nil' do
before do
allow(described_class).to receive(:http_post).and_return({
success: true,
data: { "data" => { "subscription" => nil } }
})
end
it 'returns the correct response' do
expect(plan_upgrade_offer).to eq({
success: true,
eligible_for_free_upgrade: nil,
assisted_upgrade_plan_id: nil,
free_upgrade_plan_id: nil
})
end
end
end
end
it { is_expected.to include_module Gitlab::SubscriptionPortal::Clients::Graphql }
it { is_expected.to include_module Gitlab::SubscriptionPortal::Clients::REST }
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::SubscriptionPortal::Clients::Graphql do
let(:client) { Gitlab::SubscriptionPortal::Client }
describe '#activate' do
let(:authentication_token) { 'authentication_token' }
it 'returns success' do
expect(client).to receive(:execute_graphql_query).and_return(
{
success: true,
data: {
"data" => {
"cloudActivationActivate" => {
"authenticationToken" => authentication_token,
"errors" => []
}
}
}
}
)
result = client.activate('activation_code_abc')
expect(result).to eq({ authentication_token: authentication_token, success: true })
end
it 'returns failure' do
expect(client).to receive(:execute_graphql_query).and_return(
{
success: true,
data: {
"data" => {
"cloudActivationActivate" => {
"authenticationToken" => nil,
"errors" => ["invalid activation code"]
}
}
}
}
)
result = client.activate('activation_code_abc')
expect(result).to eq({ errors: ["invalid activation code"], success: false })
end
end
describe '#plan_upgrade_offer' do
let(:namespace_id) { 111 }
subject(:plan_upgrade_offer) { client.plan_upgrade_offer(namespace_id: namespace_id) }
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 'returns a failure' do
expect(plan_upgrade_offer).to eq({ success: false })
end
end
context 'when the response does not contain errors' do
using RSpec::Parameterized::TableSyntax
where(:eligible, :assisted_plan_id, :free_plan_id) do
true | '111111' | '111111'
true | '111111' | nil
true | nil | '111111'
end
with_them do
before do
allow(client).to receive(:execute_graphql_query).and_return({
success: true,
data: { "data" => { "subscription" => {
"eoaStarterBronzeEligible" => eligible,
"assistedUpgradePlanId" => assisted_plan_id,
"freeUpgradePlanId" => free_plan_id
} } }
})
end
it 'returns the correct response' do
expect(plan_upgrade_offer).to eq({
success: true,
eligible_for_free_upgrade: eligible,
assisted_upgrade_plan_id: assisted_plan_id,
free_upgrade_plan_id: free_plan_id
})
end
end
context 'when subscription is nil' do
before do
allow(client).to receive(:execute_graphql_query).and_return({
success: true,
data: { "data" => { "subscription" => nil } }
})
end
it 'returns the correct response' do
expect(plan_upgrade_offer).to eq({
success: true,
eligible_for_free_upgrade: nil,
assisted_upgrade_plan_id: nil,
free_upgrade_plan_id: nil
})
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::SubscriptionPortal::Clients::REST do
let(:client) { Gitlab::SubscriptionPortal::Client }
let(:http_response) { nil }
let(:http_method) { :post }
let(:gitlab_http_response) do
double(code: http_response.code, response: http_response, body: {}, parsed_response: {})
end
shared_examples 'when response is successful' do
let(:http_response) { Net::HTTPSuccess.new(1.0, '201', 'OK') }
it 'has a successful status' do
allow(Gitlab::HTTP).to receive(http_method).and_return(gitlab_http_response)
expect(subject[:success]).to eq(true)
end
end
shared_examples 'when response code is 422' do
let(:http_response) { Net::HTTPUnprocessableEntity.new(1.0, '422', 'Error') }
it 'has a unprocessable entity status' do
allow(Gitlab::HTTP).to receive(http_method).and_return(gitlab_http_response)
expect(subject[:success]).to eq(false)
end
end
shared_examples 'when response code is 500' do
let(:http_response) { Net::HTTPServerError.new(1.0, '500', 'Error') }
it 'has a server error status' do
allow(Gitlab::HTTP).to receive(http_method).and_return(gitlab_http_response)
expect(subject[:success]).to eq(false)
end
end
describe '#create_trial_account' do
subject do
client.generate_trial({})
end
it_behaves_like 'when response is successful'
it_behaves_like 'when response code is 422'
it_behaves_like 'when response code is 500'
end
describe '#create_subscription' do
subject do
client.create_subscription({}, 'customer@mail.com', 'token')
end
it_behaves_like 'when response is successful'
it_behaves_like 'when response code is 422'
it_behaves_like 'when response code is 500'
end
describe '#create_customer' do
subject do
client.create_customer({})
end
it_behaves_like 'when response is successful'
it_behaves_like 'when response code is 422'
it_behaves_like 'when response code is 500'
end
describe '#payment_form_params' do
subject do
client.payment_form_params('cc')
end
let(:http_method) { :get }
it_behaves_like 'when response is successful'
it_behaves_like 'when response code is 422'
it_behaves_like 'when response code is 500'
end
describe '#payment_method' do
subject do
client.payment_method('1')
end
let(:http_method) { :get }
it_behaves_like 'when response is successful'
it_behaves_like 'when response code is 422'
it_behaves_like 'when response code is 500'
end
end
......@@ -32,7 +32,7 @@ RSpec.describe 'Activate a subscription' do
end
before do
allow(Gitlab::SubscriptionPortal::Client).to receive(:http_post).and_return(remote_response)
allow(Gitlab::SubscriptionPortal::Client).to receive(:execute_graphql_query).and_return(remote_response)
end
it 'persists authentication token' 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