From 62a10c3a045eba9df0348959c51e96f2b573e574 Mon Sep 17 00:00:00 2001
From: Piotr Skorupa <pskorupa@gitlab.com>
Date: Tue, 21 Nov 2023 03:16:37 +0000
Subject: [PATCH] Rename application_instrumentation feature flag

Rename `application_instrumentation` FF to
`internal_events_for_product_analytics`.
---
 Gemfile                                       |  2 +
 Gemfile.checksum                              |  1 +
 Gemfile.lock                                  |  5 ++
 .../internal_events_for_product_analytics.yml |  8 +++
 lib/gitlab/internal_events.rb                 | 24 ++++++++
 spec/lib/gitlab/internal_events_spec.rb       | 60 +++++++++++++++++++
 .../editor_unique_counter_spec.rb             | 10 ++--
 7 files changed, 106 insertions(+), 4 deletions(-)
 create mode 100644 config/feature_flags/development/internal_events_for_product_analytics.yml

diff --git a/Gemfile b/Gemfile
index 30d8bc7622a44..e428a545780ab 100644
--- a/Gemfile
+++ b/Gemfile
@@ -638,3 +638,5 @@ gem 'net-protocol', '~> 0.1.3' # rubocop:todo Gemfile/MissingFeatureCategory
 gem 'net-http', '= 0.1.1' # rubocop:todo Gemfile/MissingFeatureCategory
 
 gem 'duo_api', '~> 1.3' # rubocop:todo Gemfile/MissingFeatureCategory
+
+gem 'gitlab-sdk', feature_category: :application_instrumentation
diff --git a/Gemfile.checksum b/Gemfile.checksum
index bd22ef4e64c0e..905ce96746bf5 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -218,6 +218,7 @@
 {"name":"gitlab-mail_room","version":"0.0.23","platform":"ruby","checksum":"23564fa4dab24ec5011d4c64a801fc0228301d5b0f046a26a1d8e96e36c19997"},
 {"name":"gitlab-markup","version":"1.9.0","platform":"ruby","checksum":"7eda045a08ec2d110084252fa13a8c9eac8bdac0e302035ca7db4b82bcbd7ed4"},
 {"name":"gitlab-net-dns","version":"0.9.2","platform":"ruby","checksum":"f726d978479d43810819f12a45c0906d775a07e34df111bbe693fffbbef3059d"},
+{"name":"gitlab-sdk","version":"0.2.3","platform":"ruby","checksum":"e891278a20860ab1f861312813dce5f2e73081bcc10def2ae4ee138b10a2d0d6"},
 {"name":"gitlab-styles","version":"11.0.0","platform":"ruby","checksum":"0dd8ec066ce9955ac51d3616c6bfded30f75bb526f39ff392ece6f43d5b9406b"},
 {"name":"gitlab_chronic_duration","version":"0.12.0","platform":"ruby","checksum":"0d766944d415b5c831f176871ee8625783fc0c5bfbef2d79a3a616f207ffc16d"},
 {"name":"gitlab_omniauth-ldap","version":"2.2.0","platform":"ruby","checksum":"bb4d20acb3b123ed654a8f6a47d3fac673ece7ed0b6992edb92dca14bad2838c"},
diff --git a/Gemfile.lock b/Gemfile.lock
index 809d2a19eac5a..c1742a91fc115 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -703,6 +703,10 @@ GEM
       oauth2 (>= 1.4.4, < 3)
     gitlab-markup (1.9.0)
     gitlab-net-dns (0.9.2)
+    gitlab-sdk (0.2.3)
+      activesupport (>= 5.2.0)
+      rake (~> 13.0)
+      snowplow-tracker (~> 0.8.0)
     gitlab-styles (11.0.0)
       rubocop (~> 1.57.1)
       rubocop-graphql (~> 0.18)
@@ -1883,6 +1887,7 @@ DEPENDENCIES
   gitlab-rspec!
   gitlab-safe_request_store!
   gitlab-schema-validation!
+  gitlab-sdk
   gitlab-secret_detection!
   gitlab-sidekiq-fetcher!
   gitlab-styles (~> 11.0.0)
diff --git a/config/feature_flags/development/internal_events_for_product_analytics.yml b/config/feature_flags/development/internal_events_for_product_analytics.yml
new file mode 100644
index 0000000000000..0ed4f0cf7445a
--- /dev/null
+++ b/config/feature_flags/development/internal_events_for_product_analytics.yml
@@ -0,0 +1,8 @@
+---
+name: internal_events_for_product_analytics
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/136154
+rollout_issue_url: 
+milestone: '16.7'
+type: development
+group: group::analytics instrumentation
+default_enabled: false
diff --git a/lib/gitlab/internal_events.rb b/lib/gitlab/internal_events.rb
index c775182fb4063..c4fddede4b85b 100644
--- a/lib/gitlab/internal_events.rb
+++ b/lib/gitlab/internal_events.rb
@@ -8,6 +8,7 @@ module InternalEvents
 
     class << self
       include Gitlab::Tracking::Helpers
+      include Gitlab::Utils::StrongMemoize
 
       def track_event(event_name, send_snowplow_event: true, **kwargs)
         raise UnknownEventError, "Unknown event: #{event_name}" unless EventDefinitions.known_event?(event_name)
@@ -23,6 +24,10 @@ def track_event(event_name, send_snowplow_event: true, **kwargs)
         increase_weekly_total_counter(event_name)
         update_unique_counter(event_name, kwargs)
         trigger_snowplow_event(event_name, kwargs) if send_snowplow_event
+
+        if Feature.enabled?(:internal_events_for_product_analytics)
+          send_application_instrumentation_event(event_name, kwargs)
+        end
       rescue StandardError => e
         Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e, event_name: event_name, kwargs: kwargs)
         nil
@@ -93,6 +98,25 @@ def track_struct_event(event_name, contexts:)
         Gitlab::ErrorTracking
           .track_and_raise_for_dev_exception(error, snowplow_category: category, snowplow_action: event_name)
       end
+
+      def send_application_instrumentation_event(event_name, kwargs)
+        return if gitlab_sdk_client.nil?
+
+        user = kwargs[:user]
+
+        gitlab_sdk_client.identify(user&.id)
+        gitlab_sdk_client.track(event_name)
+      end
+
+      def gitlab_sdk_client
+        app_id = ENV['GITLAB_ANALYTICS_ID']
+        host = ENV['GITLAB_ANALYTICS_URL']
+
+        return unless app_id.present? && host.present?
+
+        GitlabSDK::Client.new(app_id: app_id, host: host)
+      end
+      strong_memoize_attr :gitlab_sdk_client
     end
   end
 end
diff --git a/spec/lib/gitlab/internal_events_spec.rb b/spec/lib/gitlab/internal_events_spec.rb
index 68d6f3700af5e..26c2421e6499b 100644
--- a/spec/lib/gitlab/internal_events_spec.rb
+++ b/spec/lib/gitlab/internal_events_spec.rb
@@ -267,4 +267,64 @@ def validate_service_ping_context(service_ping_context)
       expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to have_received(:track_event)
     end
   end
+
+  describe 'Product Analytics tracking' do
+    let(:app_id) { 'foobar' }
+    let(:url) { 'http://localhost:4000' }
+
+    before do
+      described_class.clear_memoization(:gitlab_sdk_client)
+
+      stub_env('GITLAB_ANALYTICS_ID', app_id)
+      stub_env('GITLAB_ANALYTICS_URL', url)
+    end
+
+    subject(:track_event) { described_class.track_event(event_name, user: user) }
+
+    shared_examples 'does not send a Product Analytics event' do
+      it 'does not call the Product Analytics Ruby SDK' do
+        expect(GitlabSDK::Client).not_to receive(:new)
+
+        track_event
+      end
+    end
+
+    context 'when internal_events_for_product_analytics FF is enabled' do
+      before do
+        stub_feature_flags(internal_events_for_product_analytics: true)
+      end
+
+      it 'calls Product Analytics Ruby SDK', :aggregate_failures do
+        expect_next_instance_of(GitlabSDK::Client) do |sdk_client|
+          expect(sdk_client).to receive(:identify).with(user.id)
+          expect(sdk_client).to receive(:track).with(event_name)
+        end
+
+        track_event
+      end
+
+      context 'when GITLAB_ANALYTICS_ID is nil' do
+        let(:app_id) { nil }
+
+        it_behaves_like 'does not send a Product Analytics event'
+      end
+
+      context 'when GITLAB_ANALYTICS_URL is nil' do
+        let(:url) { nil }
+
+        it_behaves_like 'does not send a Product Analytics event'
+      end
+    end
+
+    context 'when internal_events_for_product_analytics FF is disabled' do
+      let(:app_id) { 'foobar' }
+      let(:url) { 'http://localhost:4000' }
+
+      before do
+        stub_feature_flags(internal_events_for_product_analytics: false)
+      end
+
+      it_behaves_like 'does not send a Product Analytics event'
+    end
+  end
 end
diff --git a/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb
index 71e9e7a8e7dfb..cbf4d3c82619a 100644
--- a/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb
@@ -19,9 +19,9 @@
 
     specify do
       aggregate_failures do
-        expect(track_action(author: user, project: project)).to be_truthy
-        expect(track_action(author: user2, project: project)).to be_truthy
-        expect(track_action(author: user3, project: project)).to be_truthy
+        track_action(author: user, project: project)
+        track_action(author: user2, project: project)
+        track_action(author: user3, project: project)
 
         expect(count_unique(date_from: time.beginning_of_week, date_to: 1.week.from_now)).to eq(3)
       end
@@ -30,7 +30,9 @@
     it_behaves_like 'internal event tracking'
 
     it 'does not track edit actions if author is not present' do
-      expect(track_action(author: nil, project: project)).to be_nil
+      track_action(author: nil, project: project)
+
+      expect(count_unique(date_from: time.beginning_of_week, date_to: 1.week.from_now)).to eq(0)
     end
   end
 
-- 
GitLab