From 7cc3770e8786cffaa64f6ac0a4de946e2fec1a09 Mon Sep 17 00:00:00 2001
From: Suraj Tripathi <stripathi@gitlab.com>
Date: Tue, 16 Apr 2024 18:57:29 +0000
Subject: [PATCH] Added MemberApprovalFinder

- Modified Controllers to use the Finder
- Modified Controllers spec to account for empty
- Added Finder spec
- Added Ability check in Finder

EE: true
Changelog: added
---
 .../ee/groups/group_members_controller.rb     | 10 +-
 .../ee/projects/project_members_controller.rb | 10 +-
 .../member_approval_finder.rb                 | 55 +++++++++++
 .../member_approval_finder_spec.rb            | 96 +++++++++++++++++++
 .../groups/group_members_controller_spec.rb   |  4 +-
 .../project_members_controller_spec.rb        |  4 +-
 6 files changed, 165 insertions(+), 14 deletions(-)
 create mode 100644 ee/app/finders/member_management/member_approval_finder.rb
 create mode 100644 ee/spec/finders/member_management/member_approval_finder_spec.rb

diff --git a/ee/app/controllers/ee/groups/group_members_controller.rb b/ee/app/controllers/ee/groups/group_members_controller.rb
index b6783688e7715..74af9530b8e10 100644
--- a/ee/app/controllers/ee/groups/group_members_controller.rb
+++ b/ee/app/controllers/ee/groups/group_members_controller.rb
@@ -165,12 +165,12 @@ def banned_members(params: {})
 
       def pending_promotion_members
         return unless can?(current_user, :admin_group_member, group)
-        return unless ::Feature.enabled?(:member_promotion_management, type: :wip)
-        return unless ::Gitlab::CurrentSettings.enable_member_promotion_management?
 
-        ::Members::MemberApproval.pending_member_approvals(group.id)
-            .page(params[:promotion_requests_page])
-            .per(MEMBER_PER_PAGE_LIMIT)
+        ::MemberManagement::MemberApprovalFinder
+          .new(current_user: current_user, params: params, source: group)
+          .execute
+          .page(params[:promotion_requests_page])
+          .per(MEMBER_PER_PAGE_LIMIT)
       end
     end
   end
diff --git a/ee/app/controllers/ee/projects/project_members_controller.rb b/ee/app/controllers/ee/projects/project_members_controller.rb
index 8a87d8080cc19..57ea29be89114 100644
--- a/ee/app/controllers/ee/projects/project_members_controller.rb
+++ b/ee/app/controllers/ee/projects/project_members_controller.rb
@@ -38,12 +38,12 @@ def non_invited_members
 
       def pending_promotion_members
         return unless can?(current_user, :admin_project_member, project)
-        return unless ::Feature.enabled?(:member_promotion_management, type: :wip)
-        return unless ::Gitlab::CurrentSettings.enable_member_promotion_management?
 
-        ::Members::MemberApproval.pending_member_approvals(project.project_namespace_id)
-            .page(params[:promotion_requests_page])
-            .per(MEMBER_PER_PAGE_LIMIT)
+        ::MemberManagement::MemberApprovalFinder
+          .new(current_user: current_user, params: params, source: project)
+          .execute
+          .page(params[:promotion_requests_page])
+          .per(MEMBER_PER_PAGE_LIMIT)
       end
     end
   end
diff --git a/ee/app/finders/member_management/member_approval_finder.rb b/ee/app/finders/member_management/member_approval_finder.rb
new file mode 100644
index 0000000000000..07cbae4daa318
--- /dev/null
+++ b/ee/app/finders/member_management/member_approval_finder.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+module MemberManagement
+  class MemberApprovalFinder
+    attr_reader :params
+
+    def initialize(current_user:, source:, params: {})
+      @current_user = current_user
+      @params = params
+      validate_and_set_source(source)
+    end
+
+    def execute
+      model = ::Members::MemberApproval
+      return model.none unless feature_and_settings_enabled?
+      return model.none unless allowed_to_query_member_approvals?
+
+      model.pending_member_approvals(@member_namespace_id)
+    end
+
+    private
+
+    attr_reader :current_user, :source, :member_namespace_id, :type
+
+    def validate_and_set_source(source)
+      case source
+      when ::Group
+        @member_namespace_id = source.id
+        @type = :group
+      when ::Project
+        @member_namespace_id = source.project_namespace.id
+        @type = :project
+      else
+        raise ArgumentError, 'Invalid source. Source should be either Group or Project.'
+      end
+      @source = source
+    end
+
+    def feature_and_settings_enabled?
+      ::Feature.enabled?(:member_promotion_management, type: :wip) &&
+        ::Gitlab::CurrentSettings.enable_member_promotion_management?
+    end
+
+    def allowed_to_query_member_approvals?
+      ability = case type
+                when :group
+                  :admin_group_member
+                when :project
+                  :admin_project_member
+                end
+
+      Ability.allowed?(current_user, ability, source)
+    end
+  end
+end
diff --git a/ee/spec/finders/member_management/member_approval_finder_spec.rb b/ee/spec/finders/member_management/member_approval_finder_spec.rb
new file mode 100644
index 0000000000000..c0dc95b008d76
--- /dev/null
+++ b/ee/spec/finders/member_management/member_approval_finder_spec.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe MemberManagement::MemberApprovalFinder, feature_category: :seat_cost_management do
+  let_it_be(:user) { create(:user) }
+  let_it_be(:group) { create(:group) }
+  let_it_be(:project) { create(:project) }
+  let(:source) { group }
+
+  subject(:finder) { described_class.new(current_user: user, params: {}, source: source) }
+
+  describe '#initialize' do
+    shared_examples 'invalid source arg' do
+      it 'raises an ArgumentError' do
+        expect do
+          finder
+        end.to raise_error(ArgumentError, 'Invalid source. Source should be either Group or Project.')
+      end
+    end
+
+    context 'when source is nil' do
+      let(:source) { nil }
+
+      it_behaves_like "invalid source arg"
+    end
+
+    context 'when source is neither Project or Group' do
+      let(:source) { create(:user_namespace) }
+
+      it_behaves_like "invalid source arg"
+    end
+  end
+
+  describe '#execute' do
+    shared_examples 'when feature and settings are enabled' do
+      let!(:pending_approval) { create(:member_approval, type, member_namespace: member_namespace) }
+
+      let!(:rejected_approval) { create(:member_approval, type, member_namespace: member_namespace, status: 2) }
+
+      context 'when user has admin access' do
+        before do
+          source.add_owner(user)
+        end
+
+        it 'returns pending member approvals for the source' do
+          expect(finder.execute).to contain_exactly(pending_approval)
+        end
+      end
+
+      context 'when user does not have admin access' do
+        before do
+          source.add_developer(user)
+        end
+
+        it 'returns empty' do
+          expect(finder.execute).to be_empty
+        end
+      end
+    end
+
+    before do
+      stub_feature_flags(member_promotion_management: true)
+      stub_application_setting(enable_member_promotion_management: true)
+    end
+
+    context 'when member promotion management feature is disabled' do
+      it 'returns nil' do
+        stub_feature_flags(member_promotion_management: false)
+        expect(finder.execute).to be_empty
+      end
+    end
+
+    context 'when member promotion management is disabled in settings' do
+      it 'returns nil' do
+        stub_application_setting(enable_member_promotion_management: false)
+        expect(finder.execute).to be_empty
+      end
+    end
+
+    context 'when group is provided' do
+      let(:member_namespace) { group }
+      let(:type) { :for_group_member }
+
+      it_behaves_like 'when feature and settings are enabled'
+    end
+
+    context 'when project is provided' do
+      let(:source) { project }
+      let(:member_namespace) { project.project_namespace }
+      let(:type) { :for_project_member }
+
+      it_behaves_like 'when feature and settings are enabled'
+    end
+  end
+end
diff --git a/ee/spec/requests/groups/group_members_controller_spec.rb b/ee/spec/requests/groups/group_members_controller_spec.rb
index 89ee3f6a2d6fd..e1343f0d65d77 100644
--- a/ee/spec/requests/groups/group_members_controller_spec.rb
+++ b/ee/spec/requests/groups/group_members_controller_spec.rb
@@ -136,10 +136,10 @@
           stub_feature_flags(member_promotion_management: false)
         end
 
-        it 'assigns @pending_promotion_members as nil' do
+        it 'assigns @pending_promotion_members as empty' do
           request
 
-          expect(assigns(:pending_promotion_members)).to eq(nil)
+          expect(assigns(:pending_promotion_members)).to be_empty
         end
       end
     end
diff --git a/ee/spec/requests/projects/project_members_controller_spec.rb b/ee/spec/requests/projects/project_members_controller_spec.rb
index 37e72ec8a5b23..ec098d329451f 100644
--- a/ee/spec/requests/projects/project_members_controller_spec.rb
+++ b/ee/spec/requests/projects/project_members_controller_spec.rb
@@ -69,10 +69,10 @@
           stub_feature_flags(member_promotion_management: false)
         end
 
-        it 'assigns @pending_promotion_members as nil' do
+        it 'assigns @pending_promotion_members be empty' do
           make_request
 
-          expect(assigns(:pending_promotion_members)).to eq(nil)
+          expect(assigns(:pending_promotion_members)).to be_empty
         end
       end
     end
-- 
GitLab