From db6857668e2672caea5f9d0671b07d9374d25c8b Mon Sep 17 00:00:00 2001
From: Mohamed Hamda <mhamda@gitlab.com>
Date: Tue, 27 Feb 2024 20:35:46 +0000
Subject: [PATCH] Change lib to a service Change the bulk assign lib to a
 service

---
 doc/raketasks/user_management.md              |  2 +-
 .../duo_pro/bulk_user_assignment.rb           | 97 +++++++++++++++++++
 ee/lib/duo_pro/bulk_user_assignment.rb        | 96 ------------------
 .../tasks/duo_pro/bulk_user_assignment.rake   |  2 +-
 .../duo_pro/bulk_user_assignment_spec.rb      |  2 +-
 .../duo_pro/bulk_user_assignment_spec.rb      |  3 +-
 6 files changed, 102 insertions(+), 100 deletions(-)
 create mode 100644 ee/app/services/gitlab_subscriptions/duo_pro/bulk_user_assignment.rb
 delete mode 100644 ee/lib/duo_pro/bulk_user_assignment.rb
 rename ee/spec/{lib => services/gitlab_subscriptions}/duo_pro/bulk_user_assignment_spec.rb (98%)

diff --git a/doc/raketasks/user_management.md b/doc/raketasks/user_management.md
index c5a5febc0cb52..5c42a65c8e777 100644
--- a/doc/raketasks/user_management.md
+++ b/doc/raketasks/user_management.md
@@ -190,7 +190,7 @@ DETAILS:
 
 > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142189) in GitLab 16.9.
 
-The Rake task for bulk user assignment is available in GitLab 16.9 and later. For GitLab 16.8, use the script [`bulk_user_assignment.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/lib/duo_pro/bulk_user_assignment.rb) instead.
+The Rake task for bulk user assignment is available in GitLab 16.9 and later. For GitLab 16.8, use the script [`bulk_user_assignment.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/services/gitlab_subscriptions/duo_pro/bulk_user_assignment.rb) instead.
 
 To perform bulk user assignment for GitLab Duo Pro, you can use the following Rake task:
 
diff --git a/ee/app/services/gitlab_subscriptions/duo_pro/bulk_user_assignment.rb b/ee/app/services/gitlab_subscriptions/duo_pro/bulk_user_assignment.rb
new file mode 100644
index 0000000000000..030a828d0da80
--- /dev/null
+++ b/ee/app/services/gitlab_subscriptions/duo_pro/bulk_user_assignment.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+
+# GitLab Duo Pro Bulk User Assignment
+# 1. Set the `add_on_purchase` variable to point to your AddOnPurchase record
+# add_on_purchase = GitlabSubscriptions::AddOnPurchase.find_by(add_on: GitlabSubscriptions::AddOn.code_suggestions.last)
+# 2. Set the `usernames` variable to point to an array of usernames:
+#    usernames = ["user1", "user2", "user3", "user4", "user5"]
+#    If reading from a CSV file
+#    usernames =  CSV.read(FILE_PATH, headers: true).pluck('username')
+# 3. Execute the bulk assignment:
+#    GitlabSubscriptions::DuoPro::BulkUserAssignment.new(usernames, add_on_purchase).execute
+
+# Error Messages:
+# - `User is not found`
+# - `ERROR_NO_SEATS_AVAILABLE`: No more seats are available.
+# - `ERROR_INVALID_USER_MEMBERSHIP`: User is not eligible for assignment due to being inactive, a bot, or a ghost.
+module GitlabSubscriptions
+  module DuoPro
+    class BulkUserAssignment
+      include ::GitlabSubscriptions::SubscriptionHelper
+      attr_reader :usernames, :add_on_purchase, :successful_assignments, :failed_assignments
+
+      THROTTLE_BATCH_SIZE = 50
+      THROTTLE_SLEEP_DELAY = 0.5.seconds
+
+      def initialize(usernames, add_on_purchase)
+        @usernames = usernames
+        @add_on_purchase = add_on_purchase
+        @successful_assignments = []
+        @failed_assignments = []
+      end
+
+      def execute
+        return 'AddOn not purchased' unless add_on_purchase
+
+        process_users(usernames)
+
+        { successful_assignments: successful_assignments, failed_assignments: failed_assignments }
+      end
+
+      private
+
+      def process_users(usernames)
+        usernames.each.with_index(1) do |username, index|
+          user_to_be_assigned = User.find_by_username(username)
+
+          unless user_to_be_assigned
+            log_failed_assignment("User is not found: #{username}")
+            next
+          end
+
+          result = assign(user_to_be_assigned)
+
+          if result.errors.include?("NO_SEATS_AVAILABLE")
+            log_no_seats_available(result, username)
+            break
+          end
+
+          log_result(result, username)
+
+          sleep(THROTTLE_SLEEP_DELAY) if index % THROTTLE_BATCH_SIZE == 0
+        end
+      end
+
+      def assign(user)
+        service_class = if gitlab_com_subscription?
+                          ::GitlabSubscriptions::UserAddOnAssignments::Saas::CreateService
+                        else
+                          ::GitlabSubscriptions::UserAddOnAssignments::SelfManaged::CreateService
+                        end
+
+        service_class.new(add_on_purchase: add_on_purchase, user: user).execute
+      end
+
+      def log_no_seats_available(result, username)
+        log_failed_assignment("Failed to assign seat to user: #{username}, Errors: #{result.errors}")
+        log_failed_assignment("##No seats are left; users starting from @#{username} onwards were not assigned.##")
+      end
+
+      def log_successful_assignment(username)
+        successful_assignments << "User assigned: #{username}"
+      end
+
+      def log_failed_assignment(message)
+        failed_assignments << message
+      end
+
+      def log_result(result, username)
+        if result.errors.empty?
+          log_successful_assignment(username)
+        else
+          log_failed_assignment("Failed to assign seat to user: #{username}, Errors: #{result.errors}")
+        end
+      end
+    end
+  end
+end
diff --git a/ee/lib/duo_pro/bulk_user_assignment.rb b/ee/lib/duo_pro/bulk_user_assignment.rb
deleted file mode 100644
index 1ee20d6cea27c..0000000000000
--- a/ee/lib/duo_pro/bulk_user_assignment.rb
+++ /dev/null
@@ -1,96 +0,0 @@
-# frozen_string_literal: true
-
-# Duo Pro Bulk User Assignment
-# 1. Set the `add_on_purchase` variable to point to your AddOnPurchase record
-# add_on_purchase = GitlabSubscriptions::AddOnPurchase.find_by(add_on: GitlabSubscriptions::AddOn.code_suggestions.last)
-# 2. Set the `usernames` variable to point to an array of usernames:
-#    usernames = ["user1", "user2", "user3", "user4", "user5"]
-#    If reading from a CSV file
-#    usernames =  CSV.read(FILE_PATH, headers: true).pluck('username')
-# 3. Execute the bulk assignment:
-#    DuoPro::BulkUserAssignment.new(usernames, add_on_purchase).execute
-
-# Error Messages:
-# - `User is not found`
-# - `ERROR_NO_SEATS_AVAILABLE`: No more seats are available.
-# - `ERROR_INVALID_USER_MEMBERSHIP`: User is not eligible for assignment due to being inactive, a bot, or a ghost.
-
-module DuoPro
-  class BulkUserAssignment
-    include ::GitlabSubscriptions::SubscriptionHelper
-    attr_reader :usernames, :add_on_purchase, :successful_assignments, :failed_assignments
-
-    THROTTLE_BATCH_SIZE = 50
-    THROTTLE_SLEEP_DELAY = 0.5.seconds
-
-    def initialize(usernames, add_on_purchase)
-      @usernames = usernames
-      @add_on_purchase = add_on_purchase
-      @successful_assignments = []
-      @failed_assignments = []
-    end
-
-    def execute
-      return 'AddOn not purchased' unless add_on_purchase
-
-      process_users(usernames)
-
-      { successful_assignments: successful_assignments, failed_assignments: failed_assignments }
-    end
-
-    private
-
-    def process_users(usernames)
-      usernames.each.with_index(1) do |username, index|
-        user_to_be_assigned = User.find_by_username(username)
-
-        unless user_to_be_assigned
-          log_failed_assignment("User is not found: #{username}")
-          next
-        end
-
-        result = assign(user_to_be_assigned)
-
-        if result.errors.include?("NO_SEATS_AVAILABLE")
-          log_no_seats_available(result, username)
-          break
-        end
-
-        log_result(result, username)
-
-        sleep(THROTTLE_SLEEP_DELAY) if index % THROTTLE_BATCH_SIZE == 0
-      end
-    end
-
-    def assign(user)
-      service_class = if gitlab_com_subscription?
-                        ::GitlabSubscriptions::UserAddOnAssignments::Saas::CreateService
-                      else
-                        ::GitlabSubscriptions::UserAddOnAssignments::SelfManaged::CreateService
-                      end
-
-      service_class.new(add_on_purchase: add_on_purchase, user: user).execute
-    end
-
-    def log_no_seats_available(result, username)
-      log_failed_assignment("Failed to assign seat to user: #{username}, Errors: #{result.errors}")
-      log_failed_assignment("##No seats are left; users starting from @#{username} onwards were not assigned.##")
-    end
-
-    def log_successful_assignment(username)
-      successful_assignments << "User assigned: #{username}"
-    end
-
-    def log_failed_assignment(message)
-      failed_assignments << message
-    end
-
-    def log_result(result, username)
-      if result.errors.empty?
-        log_successful_assignment(username)
-      else
-        log_failed_assignment("Failed to assign seat to user: #{username}, Errors: #{result.errors}")
-      end
-    end
-  end
-end
diff --git a/ee/lib/tasks/duo_pro/bulk_user_assignment.rake b/ee/lib/tasks/duo_pro/bulk_user_assignment.rake
index bab70f9ec3d64..a474da6a84714 100644
--- a/ee/lib/tasks/duo_pro/bulk_user_assignment.rake
+++ b/ee/lib/tasks/duo_pro/bulk_user_assignment.rake
@@ -29,7 +29,7 @@ namespace :duo_pro do
       ERROR_MESSAGE
     end
 
-    result = DuoPro::BulkUserAssignment.new(user_names, add_on_purchase).execute
+    result = GitlabSubscriptions::DuoPro::BulkUserAssignment.new(user_names, add_on_purchase).execute
     display_results(result)
   end
 
diff --git a/ee/spec/lib/duo_pro/bulk_user_assignment_spec.rb b/ee/spec/services/gitlab_subscriptions/duo_pro/bulk_user_assignment_spec.rb
similarity index 98%
rename from ee/spec/lib/duo_pro/bulk_user_assignment_spec.rb
rename to ee/spec/services/gitlab_subscriptions/duo_pro/bulk_user_assignment_spec.rb
index a853f8002e7ca..6e920001bf9bb 100644
--- a/ee/spec/lib/duo_pro/bulk_user_assignment_spec.rb
+++ b/ee/spec/services/gitlab_subscriptions/duo_pro/bulk_user_assignment_spec.rb
@@ -2,7 +2,7 @@
 
 require 'spec_helper'
 
-RSpec.describe DuoPro::BulkUserAssignment, feature_category: :purchase do
+RSpec.describe GitlabSubscriptions::DuoPro::BulkUserAssignment, feature_category: :seat_cost_management do
   describe '#initialize' do
     subject(:bulk_assignment) { described_class.new([], nil) }
 
diff --git a/ee/spec/tasks/duo_pro/bulk_user_assignment_spec.rb b/ee/spec/tasks/duo_pro/bulk_user_assignment_spec.rb
index 2caee50034f9d..f3e101065b6bf 100644
--- a/ee/spec/tasks/duo_pro/bulk_user_assignment_spec.rb
+++ b/ee/spec/tasks/duo_pro/bulk_user_assignment_spec.rb
@@ -33,7 +33,8 @@
 
       before do
         add_on_purchase = create(:gitlab_subscription_add_on_purchase, :self_managed, quantity: 10, add_on: add_on)
-        allow_next_instance_of(DuoPro::BulkUserAssignment, %w[user1 user2 user3], add_on_purchase) do |instance|
+        allow_next_instance_of(GitlabSubscriptions::DuoPro::BulkUserAssignment, %w[user1 user2 user3],
+          add_on_purchase) do |instance|
           response = { successful_assignments: ['success'], failed_assignments: ['Failed'] }
           allow(instance).to receive(:execute).and_return(response)
         end
-- 
GitLab