Commit 49302c30 authored by Jason Goodman's avatar Jason Goodman Committed by James Lopez

Add Billable Memberships API

parent 204238b3
---
title: Add billable memberships API
merge_request: 58506
author:
type: added
# frozen_string_literal: true
module EE
module API
module Entities
class BillableMembership < Grape::Entity
expose :id
expose :source_id
expose :source_full_name do |member|
member.source.full_name
end
expose :created_at
expose :expires_at
expose :access_level do
expose :human_access, as: :string_value
expose :access_level, as: :integer_value
end
end
end
end
end
......@@ -78,6 +78,28 @@ module EE
present users, with: ::EE::API::Entities::BillableMember, current_user: current_user
end
desc 'Get the memberships of a billable user of a root group.' do
success ::EE::API::Entities::BillableMembership
end
params do
requires :user_id, type: Integer, desc: 'The user ID of the member'
use :pagination
end
get ":id/billable_members/:user_id/memberships" do
group = find_group!(params[:id])
bad_request! unless can?(current_user, :admin_group_member, group)
bad_request! if group.subgroup?
user = ::User.find(params[:user_id])
not_found!('User') unless group.billed_user_ids.include?(user.id)
memberships = user.members.in_hierarchy(group).including_source
present paginate(memberships), with: ::EE::API::Entities::BillableMembership
end
desc 'Removes a billable member from a group or project.'
params do
requires :user_id, type: Integer, desc: 'The user ID of the member'
......
......@@ -503,6 +503,115 @@ RSpec.describe API::Members do
end
end
describe 'GET /groups/:id/billable_members/:user_id/memberships' do
let_it_be(:developer) { create(:user) }
let_it_be(:guest) { create(:user) }
before_all do
group.add_developer(developer)
group.add_guest(guest)
end
it 'returns memberships for the billable group member' do
membership = developer.members.first
get api("/groups/#{group.id}/billable_members/#{developer.id}/memberships", owner)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq([{
'id' => membership.id,
'source_id' => group.id,
'source_full_name' => group.full_name,
'created_at' => membership.created_at.as_json,
'expires_at' => nil,
'access_level' => {
'string_value' => 'Developer',
'integer_value' => 30
}
}])
end
it 'returns not found when the user does not exist' do
get api("/groups/#{group.id}/billable_members/#{non_existing_record_id}/memberships", owner)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response).to eq({ 'message' => '404 Not found' })
end
it 'returns not found when the group does not exist' do
get api("/groups/#{non_existing_record_id}/billable_members/#{developer.id}/memberships", owner)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response).to eq({ 'message' => '404 Group Not Found' })
end
it 'returns not found when the user is not billable' do
create(:gitlab_subscription, :ultimate, namespace: group)
get api("/groups/#{group.id}/billable_members/#{guest.id}/memberships", owner)
expect(response).to have_gitlab_http_status(:not_found)
expect(json_response).to eq({ 'message' => '404 User Not Found' })
end
it 'returns bad request if the user cannot admin group members' do
get api("/groups/#{group.id}/billable_members/#{developer.id}/memberships", developer)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response).to eq({ 'message' => '400 Bad request' })
end
it 'returns bad request if the group is a subgroup' do
subgroup = create(:group, name: 'My SubGroup', parent: group)
subgroup.add_developer(developer)
get api("/groups/#{subgroup.id}/billable_members/#{developer.id}/memberships", owner)
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response).to eq({ 'message' => '400 Bad request' })
end
it 'excludes memberships outside the requested group hierarchy' do
other_group = create(:group, name: 'My Other Group')
other_group.add_developer(developer)
get api("/groups/#{group.id}/billable_members/#{developer.id}/memberships", owner)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.map { |m| m['source_full_name'] }).to eq([group.full_name])
end
it 'includes subgroup memberships' do
subgroup = create(:group, name: 'My SubGroup', parent: group)
subgroup.add_developer(developer)
get api("/groups/#{group.id}/billable_members/#{developer.id}/memberships", owner)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.map { |m| m['source_full_name'] }).to include(subgroup.full_name)
end
it 'includes project memberships' do
project = create(:project, name: 'My Project', group: group)
project.add_developer(developer)
get api("/groups/#{group.id}/billable_members/#{developer.id}/memberships", owner)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.map { |m| m['source_full_name'] }).to include(project.full_name)
end
it 'paginates results' do
subgroup = create(:group, name: 'SubGroup A', parent: group)
subgroup.add_developer(developer)
get api("/groups/#{group.id}/billable_members/#{developer.id}/memberships", owner), params: { page: 2, per_page: 1 }
expect(response).to have_gitlab_http_status(:ok)
expect(json_response.map { |m| m['source_full_name'] }).to eq([subgroup.full_name])
end
end
describe 'DELETE /groups/:id/billable_members/:user_id' do
context 'when the current user has insufficient rights' do
it 'returns 400' 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