Commit 975f573c authored by Etienne Baqué's avatar Etienne Baqué

Merge branch 'vij-filter-members-by-state' into 'master'

Allow filtering of members by state in REST API

See merge request gitlab-org/gitlab!74638
parents d337289b aee6367c
...@@ -109,6 +109,7 @@ GET /projects/:id/members/all ...@@ -109,6 +109,7 @@ GET /projects/:id/members/all
| `id` | integer/string | yes | The ID or [URL-encoded path of the project or group](index.md#namespaced-path-encoding) owned by the authenticated user | | `id` | integer/string | yes | The ID or [URL-encoded path of the project or group](index.md#namespaced-path-encoding) owned by the authenticated user |
| `query` | string | no | A query string to search for members | | `query` | string | no | A query string to search for members |
| `user_ids` | array of integers | no | Filter the results on the given user IDs | | `user_ids` | array of integers | no | Filter the results on the given user IDs |
| `state` | string | no | Filter results by member state, one of `awaiting`, `active` or `created` **(PREMIUM)** |
```shell ```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/members/all" curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/members/all"
......
...@@ -20,6 +20,10 @@ module EE ...@@ -20,6 +20,10 @@ module EE
params :optional_filter_params_ee do params :optional_filter_params_ee do
optional :with_saml_identity, type: Grape::API::Boolean, desc: "List only members with linked SAML identity" optional :with_saml_identity, type: Grape::API::Boolean, desc: "List only members with linked SAML identity"
end end
params :optional_state_filter_ee do
optional :state, type: String, desc: 'Filter results by member state', values: %w(awaiting active created)
end
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
...@@ -35,6 +39,8 @@ module EE ...@@ -35,6 +39,8 @@ module EE
end end
end end
members = members.with_state(params[:state]) if params[:state].present?
members members
end end
......
...@@ -7,5 +7,17 @@ FactoryBot.modify do ...@@ -7,5 +7,17 @@ FactoryBot.modify do
member.wait member.wait
end end
end end
trait :active do
after(:create) do |member|
member.activate
end
end
trait :created do
after(:create) do |member|
member.update!(state: Member::STATE_CREATED)
end
end
end end
end end
...@@ -7,5 +7,17 @@ FactoryBot.modify do ...@@ -7,5 +7,17 @@ FactoryBot.modify do
member.wait member.wait
end end
end end
trait :active do
after(:create) do |member|
member.activate
end
end
trait :created do
after(:create) do |member|
member.update!(state: Member::STATE_CREATED)
end
end
end end
end end
...@@ -1104,4 +1104,69 @@ RSpec.describe API::Members do ...@@ -1104,4 +1104,69 @@ RSpec.describe API::Members do
end end
end end
end end
context 'filtering project and group members' do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:owner) { create(:user) }
let(:params) { { state: state } }
before do
group.add_owner(owner)
end
subject do
get api("/#{source_type}/#{source.id}/members/all", owner), params: params
json_response
end
shared_examples 'filtered results' do
context 'for active members' do
let(:state) { 'active' }
it 'returns only active members' do
expect(subject.map { |u| u['id'] }).to match_array [active_member.user_id, owner.id]
end
end
context 'for awaiting members' do
let(:state) { 'awaiting' }
it 'returns only awaiting members' do
expect(subject.map { |u| u['id'] }).to match_array [awaiting_member.user_id]
end
end
context 'for created members' do
let(:state) { 'created' }
it 'returns only created members' do
expect(subject.map { |u| u['id'] }).to match_array [created_member.user_id]
end
end
end
context 'for group sources' do
let(:source_type) { 'groups' }
let(:source) { group }
it_behaves_like 'filtered results' do
let_it_be(:awaiting_member) { create(:group_member, :awaiting, group: group) }
let_it_be(:active_member) { create(:group_member, :active, group: group) }
let_it_be(:created_member) { create(:group_member, :created, group: group) }
end
end
context 'for project sources' do
let(:source_type) { 'projects' }
let(:source) { project }
it_behaves_like 'filtered results' do
let_it_be(:awaiting_member) { create(:project_member, :awaiting, project: project) }
let_it_be(:active_member) { create(:project_member, :active, project: project) }
let_it_be(:created_member) { create(:project_member, :created, project: project) }
end
end
end
end end
...@@ -8,6 +8,9 @@ module API ...@@ -8,6 +8,9 @@ module API
params :optional_filter_params_ee do params :optional_filter_params_ee do
end end
params :optional_state_filter_ee do
end
def find_source(source_type, id) def find_source(source_type, id)
public_send("find_#{source_type}!", id) # rubocop:disable GitlabSecurity/PublicSend public_send("find_#{source_type}!", id) # rubocop:disable GitlabSecurity/PublicSend
end end
......
...@@ -41,6 +41,7 @@ module API ...@@ -41,6 +41,7 @@ module API
optional :query, type: String, desc: 'A query string to search for members' optional :query, type: String, desc: 'A query string to search for members'
optional :user_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Array of user ids to look up for membership' optional :user_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Array of user ids to look up for membership'
optional :show_seat_info, type: Boolean, desc: 'Show seat information for members' optional :show_seat_info, type: Boolean, desc: 'Show seat information for members'
use :optional_state_filter_ee
use :pagination use :pagination
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