Commit 0dfdedcc authored by Luke Duncalfe's avatar Luke Duncalfe

Merge branch 'vij-refactor-subscription-portal-clients' into 'master'

Refactor Gitlab::SubscriptionPortal::Client class

See merge request gitlab-org/gitlab!55507
parents a6f7fc24 ba20f4e4
......@@ -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