Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/travis/api/app/endpoint/authorization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ def create_state

def state_ok?(state, provider = :github)
cookie_state = request.cookies[cookie_name(provider)]
state = CGI.unescape(state)
state == cookie_state and redis.srem('github:states', state.to_s.split(":::", 1))
end

Expand Down
10 changes: 10 additions & 0 deletions lib/travis/api/v3/billing_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,16 @@ def create_auto_refill(plan_id, is_enabled)
handle_errors_and_respond(response)
end

def share(plan_id, receiver)
response = connection.post("/v2/subscriptions/#{plan_id}/share", {plan: plan_id, receiver: receiver, requested_by: @user_id })
handle_errors_and_respond(response)
end

def delete_share(plan_id, receiver)
response = connection.delete("/v2/subscriptions/#{plan_id}/share", {plan: plan_id, receiver: receiver, requested_by: @user_id })
handle_errors_and_respond(response)
end

def update_auto_refill(addon_id, threshold, amount)
response = connection.patch('/auto_refill', {id: addon_id, threshold: threshold, amount: amount})
handle_errors_and_respond(response)
Expand Down
14 changes: 14 additions & 0 deletions lib/travis/api/v3/models/plan_share.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module Travis::API::V3
class Models::PlanShare
attr_reader :plan_id, :donor, :receiver, :shared_by, :created_at, :admin_revoked, :credits_consumed
def initialize(attributes = {})
@plan_id = attributes.fetch('plan_id')
@donor = attributes.fetch('donor')
@receiver = attributes.fetch('receiver')
@shared_by = attributes.fetch('shared_by')
@created_at = attributes.fetch('created_at')
@admin_revoked = attributes.fetch('admin_revoked')
@credits_consumed = attributes.fetch('credits_consumed')
end
end
end
3 changes: 2 additions & 1 deletion lib/travis/api/v3/models/v2_subscription.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class Models::V2Subscription

attr_reader :id, :plan, :permissions, :source, :billing_info, :credit_card_info, :owner, :status, :valid_to, :canceled_at,
:client_secret, :payment_intent, :addons, :auto_refill, :available_standalone_addons, :created_at, :scheduled_plan_name,
:cancellation_requested, :current_trial, :defer_pause
:cancellation_requested, :current_trial, :defer_pause, :plan_shares

def initialize(attributes = {})
@id = attributes.fetch('id')
Expand Down Expand Up @@ -38,6 +38,7 @@ def initialize(attributes = {})
@current_trial = Models::V2Trial.new(current_trial)
end
@defer_pause = attributes.fetch('defer_pause', false)
@plan_shares = attributes['plan_shares'] && attributes['plan_shares'].map { |sp| Models::PlanShare.new(sp) }
end
end

Expand Down
10 changes: 10 additions & 0 deletions lib/travis/api/v3/queries/v2_subscription.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ def update_auto_refill(user_id, addon_id)
client.update_auto_refill(addon_id, threshold, amount)
end

def share(user_id, receiver_id)
client = BillingClient.new(user_id)
client.share(params['subscription.id'], receiver_id)
end

def delete_share(user_id, receiver_id)
client = BillingClient.new(user_id)
client.delete_share(params['subscription.id'], receiver_id)
end

private

def recaptcha_client
Expand Down
6 changes: 6 additions & 0 deletions lib/travis/api/v3/renderer/plan_share.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Travis::API::V3
class Renderer::PlanShare < ModelRenderer
representation(:standard, :plan_id, :donor, :receiver, :shared_by, :created_at, :admin_revoked, :credits_consumed)
representation(:minimal, :plan_id, :donor, :receiver, :shared_by, :created_at, :admin_revoked, :credits_consumed)
end
end
6 changes: 6 additions & 0 deletions lib/travis/api/v3/renderer/plan_shares.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Travis::API::V3
class Renderer::PlanShares < CollectionRenderer
type :plan_shares
collection_key :plan_shares
end
end
3 changes: 2 additions & 1 deletion lib/travis/api/v3/renderer/v2_subscription.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module Travis::API::V3
class Renderer::V2Subscription < ModelRenderer
representation(:standard, :id, :plan, :addons, :auto_refill, :status, :valid_to, :canceled_at, :source, :owner, :client_secret, :billing_info, :credit_card_info, :payment_intent, :created_at, :scheduled_plan_name, :cancellation_requested, :current_trial, :defer_pause)
representation(:standard, :id, :plan, :addons, :auto_refill, :status, :valid_to, :canceled_at, :source, :owner, :client_secret, :billing_info, :credit_card_info, :payment_intent, :created_at, :scheduled_plan_name, :cancellation_requested, :current_trial, :defer_pause, :plan_shares)

def billing_info
Renderer.render_model(model.billing_info, mode: :standard) unless model.billing_info.nil?
Expand All @@ -21,6 +21,7 @@ def payment_intent
def current_trial
Renderer.render_model(model.current_trial,mode: :standard) unless model.current_trial.nil?
end

end

class Renderer::V2BillingInfo < ModelRenderer
Expand Down
2 changes: 2 additions & 0 deletions lib/travis/api/v3/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,8 @@ module Routes
get :auto_refill, '/auto_refill'
patch :toggle_auto_refill, '/auto_refill'
patch :update_auto_refill, '/update_auto_refill'
post :share, '/share'
delete :share, '/share'
end

hidden_resource :trials do
Expand Down
14 changes: 14 additions & 0 deletions lib/travis/api/v3/services/v2_subscription/share.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module Travis::API::V3
class Services::V2Subscription::Share < Service
params :receiver_id, :receiver
def run!
raise LoginRequired unless access_control.full_access_or_logged_in?
if @env['REQUEST_METHOD'] == 'DELETE' then
query.delete_share(access_control.user.id, params['receiver_id'])
else
query.share(access_control.user.id, params['receiver_id'])
end
no_content
end
end
end
73 changes: 73 additions & 0 deletions spec/v3/services/v2_subscription/share_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
describe Travis::API::V3::Services::V2Subscription::Share, set_app: true, billing_spec_helper: true do
let(:billing_url) { 'http://billingfake.travis-ci.com' }
let(:billing_auth_key) { 'secret' }
let(:receiver) { FactoryBot.create(:org) }
let(:data) { {'plan'=> subscription_id, 'receiver_id' => receiver.id } }

before do
Travis.config.billing.url = billing_url
Travis.config.billing.auth_key = billing_auth_key
end

context 'unauthenticated' do
it 'responds 403 for post' do
post('/v3/v2_subscription/123/share', {})

expect(last_response.status).to eq(403)
end

it 'responds 403 for delete' do
delete('/v3/v2_subscription/123/share', {})

expect(last_response.status).to eq(403)
end
end

context 'authenticated create share' do
let(:user) { FactoryBot.create(:user) }
let(:token) { Travis::Api::App::AccessToken.create(user: user, app_id: 1) }
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}",
'CONTENT_TYPE' => 'application/json' }}
let(:subscription_id) { rand(999) }

let!(:stubbed_request) do
stub_billing_request(:post, "/v2/subscriptions/#{subscription_id}/share", auth_key: billing_auth_key, user_id: user.id)
.with(body: {
'plan' => subscription_id.to_s,
'receiver' => receiver.id,
'requested_by' => user.id
})
.to_return(status: 204)
end

it 'shares the subscription' do
post("/v3/v2_subscription/#{subscription_id}/share", JSON.generate(data), headers)

expect(last_response.status).to eq(204)
expect(stubbed_request).to have_been_made.once
end
end

context 'authenticated delete share' do
let(:user) { FactoryBot.create(:user) }
let(:token) { Travis::Api::App::AccessToken.create(user: user, app_id: 1) }
let(:headers) {{ 'HTTP_AUTHORIZATION' => "token #{token}",
'CONTENT_TYPE' => 'application/json' }}
let(:subscription_id) { rand(999) }

let!(:stubbed_request) do
stub_request(:delete, "#{billing_url}/v2/subscriptions/#{subscription_id}/share?plan=#{subscription_id}&receiver=#{receiver.id}&requested_by=#{user.id}").with(
headers: {
'X-Travis-User-Id' => user.id
}
)
.to_return(status: 204)
end

it 'deletes subscription share' do
delete("/v3/v2_subscription/#{subscription_id}/share", JSON.generate(data), headers)
expect(last_response.status).to eq(204)
expect(stubbed_request).to have_been_made.once
end
end
end
1 change: 1 addition & 0 deletions spec/v3/services/v2_subscriptions/all_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
'cancellation_requested' => false,
'current_trial' => nil,
'defer_pause' => false,
'plan_shares' => nil,
'plan' => {
'@type' => 'v2_plan_config',
'@representation' => 'standard',
Expand Down
1 change: 1 addition & 0 deletions spec/v3/services/v2_subscriptions/create_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@
'cancellation_requested' => false,
'current_trial' => nil,
'defer_pause' => false,
'plan_shares' => nil,
'plan' => {
'@type' => 'v2_plan_config',
'@representation' => 'standard',
Expand Down