From c55c42b5d471dfc0d22d9b02ee020e87b0ee376b Mon Sep 17 00:00:00 2001
From: Furkan Ayhan <furkanayhn@gmail.com>
Date: Tue, 19 Nov 2024 09:48:33 +0000
Subject: [PATCH] Add Rubocop rule for the Gitlab::HTTP_V2 usage

In the GitLab monolith codebase, we should use the `Gitlab::HTTP`
wrapper instead of `Gitlab::HTTP_V2` because we need to pass options
that are related to the GitLab instance setting.
---
 keeps/delete_old_feature_flags.rb       |  2 +-
 keeps/helpers/groups.rb                 |  2 +-
 keeps/helpers/reviewer_roulette.rb      |  2 +-
 keeps/quarantine_flaky_tests.rb         |  2 +-
 lib/gitlab/http.rb                      |  6 ----
 rubocop/cop/gitlab/http_v2.rb           | 42 +++++++++++++++++++++++++
 spec/rubocop/cop/gitlab/http_v2_spec.rb | 40 +++++++++++++++++++++++
 7 files changed, 86 insertions(+), 10 deletions(-)
 create mode 100644 rubocop/cop/gitlab/http_v2.rb
 create mode 100644 spec/rubocop/cop/gitlab/http_v2_spec.rb

diff --git a/keeps/delete_old_feature_flags.rb b/keeps/delete_old_feature_flags.rb
index dabb7ccba6d6..53ce1166a5f9 100644
--- a/keeps/delete_old_feature_flags.rb
+++ b/keeps/delete_old_feature_flags.rb
@@ -174,7 +174,7 @@ def get_rollout_issue(rollout_issue_url)
       matches = ROLLOUT_ISSUE_URL_REGEX.match(rollout_issue_url)
       return unless matches
 
-      response = Gitlab::HTTP_V2.try_get(
+      response = Gitlab::HTTP_V2.try_get( # rubocop:disable Gitlab/HttpV2 -- Not running inside rails application
         format(API_ISSUE_URL, project_path: CGI.escape(matches[:project_path]), issue_iid: matches[:issue_iid])
       )
 
diff --git a/keeps/helpers/groups.rb b/keeps/helpers/groups.rb
index 8a4bc2ed23b6..ae94e5006314 100644
--- a/keeps/helpers/groups.rb
+++ b/keeps/helpers/groups.rb
@@ -55,7 +55,7 @@ def groups
 
       def fetch_groups
         @groups_json ||= begin
-          response = Gitlab::HTTP_V2.get(GROUPS_JSON_URL)
+          response = Gitlab::HTTP_V2.get(GROUPS_JSON_URL) # rubocop:disable Gitlab/HttpV2 -- Not running inside rails application
 
           unless (200..299).cover?(response.code)
             raise Error,
diff --git a/keeps/helpers/reviewer_roulette.rb b/keeps/helpers/reviewer_roulette.rb
index de8c96397a4a..959c8d7ba2cf 100644
--- a/keeps/helpers/reviewer_roulette.rb
+++ b/keeps/helpers/reviewer_roulette.rb
@@ -49,7 +49,7 @@ def stats
       end
 
       def fetch_stats
-        response = Gitlab::HTTP_V2.get(STATS_JSON_URL)
+        response = Gitlab::HTTP_V2.get(STATS_JSON_URL) # rubocop:disable Gitlab/HttpV2 -- Not running inside rails application
 
         unless (200..299).cover?(response.code)
           raise Error, "Failed to get roulette stats with response code: #{response.code} and body:\n#{response.body}"
diff --git a/keeps/quarantine_flaky_tests.rb b/keeps/quarantine_flaky_tests.rb
index 31daa999bfca..a12e59219dd5 100644
--- a/keeps/quarantine_flaky_tests.rb
+++ b/keeps/quarantine_flaky_tests.rb
@@ -131,7 +131,7 @@ def query_api(url)
     end
 
     def get(url)
-      http_response = Gitlab::HTTP_V2.get(
+      http_response = Gitlab::HTTP_V2.get( # rubocop:disable Gitlab/HttpV2 -- Not running inside rails application
         url,
         headers: {
           'User-Agent' => "GitLab-Housekeeper/#{self.class.name}",
diff --git a/lib/gitlab/http.rb b/lib/gitlab/http.rb
index 8fa5dd1e0568..cd9248b5fc81 100644
--- a/lib/gitlab/http.rb
+++ b/lib/gitlab/http.rb
@@ -1,11 +1,5 @@
 # frozen_string_literal: true
 
-#
-# IMPORTANT: With the new development of the 'gitlab-http' gem (https://gitlab.com/gitlab-org/gitlab/-/issues/415686),
-# no additional change should be implemented in this class. This class will be removed after migrating all
-# the usages to the new gem.
-#
-
 module Gitlab
   class HTTP
     BlockedUrlError = Gitlab::HTTP_V2::BlockedUrlError
diff --git a/rubocop/cop/gitlab/http_v2.rb b/rubocop/cop/gitlab/http_v2.rb
new file mode 100644
index 000000000000..a65a2528109b
--- /dev/null
+++ b/rubocop/cop/gitlab/http_v2.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+begin
+  require 'gitlab-http'
+rescue LoadError
+  # Ignore the cop if the gem is not available
+  return
+end
+
+module RuboCop
+  module Cop
+    module Gitlab
+      class HttpV2 < RuboCop::Cop::Base
+        extend RuboCop::Cop::AutoCorrector
+
+        METHODS_LIST = ::Gitlab::HTTP_V2::SUPPORTED_HTTP_METHODS.join(', ').freeze
+        METHODS_PATTERN = ::Gitlab::HTTP_V2::SUPPORTED_HTTP_METHODS.map(&:inspect).join(' ').freeze
+
+        MSG_SEND = <<~MSG.freeze
+          Avoid calling `Gitlab::HTTP_V2` directly for the #{METHODS_LIST} methods.
+          Instead, use the `Gitlab::HTTP` wrapper.
+        MSG
+
+        def_node_matcher :http_v2_node?, <<~PATTERN
+          (send (const (const nil? :Gitlab) :HTTP_V2) {#{METHODS_PATTERN}} ...)
+        PATTERN
+
+        def on_send(node)
+          return unless http_v2_node?(node)
+
+          add_offense(node, message: MSG_SEND) do |corrector|
+            _, method_name, *arg_nodes = *node
+
+            replacement = "Gitlab::HTTP.#{method_name}(#{arg_nodes.map(&:source).join(', ')})"
+
+            corrector.replace(node.source_range, replacement)
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/spec/rubocop/cop/gitlab/http_v2_spec.rb b/spec/rubocop/cop/gitlab/http_v2_spec.rb
new file mode 100644
index 000000000000..c0af7f46ffee
--- /dev/null
+++ b/spec/rubocop/cop/gitlab/http_v2_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'rubocop_spec_helper'
+require_relative '../../../../rubocop/cop/gitlab/http_v2'
+
+RSpec.describe RuboCop::Cop::Gitlab::HttpV2, feature_category: :shared do
+  it 'flags the use of `Gitlab::HTTP_V2`' do
+    expect_offense(<<~RUBY)
+      Gitlab::HTTP_V2.get('https://gitlab.com')
+      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid calling `Gitlab::HTTP_V2` directly [...]
+    RUBY
+  end
+
+  it 'flags the use of `Gitlab::HTTP_V2` with multiple arguments' do
+    expect_offense(<<~RUBY)
+      Gitlab::HTTP_V2.get('https://gitlab.com', headers: { 'Content-Type' => 'application/json' })
+      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid calling `Gitlab::HTTP_V2` directly [...]
+    RUBY
+  end
+
+  it 'flags the use of `Gitlab::HTTP_V2` with multiple arguments and a block' do
+    expect_offense(<<~RUBY)
+      Gitlab::HTTP_V2.get('https://gitlab.com', headers: { 'Content-Type' => 'application/json' }) do |req|
+      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid calling `Gitlab::HTTP_V2` directly [...]
+      end
+    RUBY
+  end
+
+  it 'does not flag the use of `Gitlab::HTTP_V2::UrlBlocker.validate!' do
+    expect_no_offenses(<<~RUBY)
+      Gitlab::HTTP_V2::UrlBlocker.validate!('https://gitlab.com')
+    RUBY
+  end
+
+  it 'does not flag the use of `Gitlab::HTTP.get`' do
+    expect_no_offenses(<<~RUBY)
+      Gitlab::HTTP.get('https://gitlab.com')
+    RUBY
+  end
+end
-- 
GitLab