diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index f0917c9265b01ad806c92adf10979cc75506eb0e..5a7e0d41e49fb1288d164c0b90fef473a6639eec 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -421,6 +421,8 @@ - 1 - - product_analytics_initialize_snowplow_product_analytics - 1 +- - product_analytics_post_push + - 1 - - project_cache - 1 - - project_destroy diff --git a/ee/app/services/ee/git/branch_push_service.rb b/ee/app/services/ee/git/branch_push_service.rb index 99e9316d869e2af686661a3ba50e8bdd39120ea5..67f2a98d0e73c4020491690bbc644825e9882f97 100644 --- a/ee/app/services/ee/git/branch_push_service.rb +++ b/ee/app/services/ee/git/branch_push_service.rb @@ -10,12 +10,20 @@ def execute enqueue_elasticsearch_indexing enqueue_zoekt_indexing enqueue_update_external_pull_requests + enqueue_product_analytics_event_metrics super end private + def enqueue_product_analytics_event_metrics + return unless project.product_analytics_enabled? + return unless default_branch? + + ::ProductAnalytics::PostPushWorker.perform_async(project.id, newrev) + end + def enqueue_elasticsearch_indexing return unless should_index_commits? diff --git a/ee/app/workers/all_queues.yml b/ee/app/workers/all_queues.yml index 647cf4907ce5350e3dbb9478d0f983d34773ffdd..3d3ce3737071a6ce9b86aaa64d1d79d0334fcda5 100644 --- a/ee/app/workers/all_queues.yml +++ b/ee/app/workers/all_queues.yml @@ -1524,6 +1524,15 @@ :weight: 1 :idempotent: true :tags: [] +- :name: product_analytics_post_push + :worker_name: ProductAnalytics::PostPushWorker + :feature_category: :product_analytics + :has_external_dependencies: false + :urgency: :low + :resource_boundary: :unknown + :weight: 1 + :idempotent: true + :tags: [] - :name: project_import_schedule :worker_name: ProjectImportScheduleWorker :feature_category: :source_code_management diff --git a/ee/app/workers/product_analytics/post_push_worker.rb b/ee/app/workers/product_analytics/post_push_worker.rb new file mode 100644 index 0000000000000000000000000000000000000000..0b8c4bcdac81f450186fc155542cac0e0befd66a --- /dev/null +++ b/ee/app/workers/product_analytics/post_push_worker.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module ProductAnalytics + class PostPushWorker + include ApplicationWorker + + data_consistency :sticky + feature_category :product_analytics + idempotent! + + def perform(project_id, newrev) + @project = Project.find_by_id(project_id) + @commit = @project.repository.commit(newrev) + + track_event if commit_has_new_dashboard? + end + + private + + def commit_has_new_dashboard? + @commit.deltas.any? do |delta| + delta.new_path.start_with?(".gitlab/analytics/dashboards/") && delta.new_file + end + end + + def track_event + Gitlab::UsageDataCounters::HLLRedisCounter.track_usage_event( + :project_created_analytics_dashboard, + @project.id + ) + end + end +end diff --git a/ee/config/metrics/counts_28d/20230504085937_projects_with_analytics_dashboard_monthly.yml b/ee/config/metrics/counts_28d/20230504085937_projects_with_analytics_dashboard_monthly.yml new file mode 100644 index 0000000000000000000000000000000000000000..02571ca4918eb7a28361238b0686bf2a879769c3 --- /dev/null +++ b/ee/config/metrics/counts_28d/20230504085937_projects_with_analytics_dashboard_monthly.yml @@ -0,0 +1,21 @@ +--- +key_path: counts.projects_with_analytics_dashboard_monthly +description: Count of projects with an analytics dashboard +product_section: dev +product_stage: analyze +product_group: product_analytics +value_type: number +status: active +milestone: "16.0" +introduced_by_url: +time_frame: 28d +data_source: redis_hll +data_category: optional +instrumentation_class: RedisHLLMetric +distribution: +- ee +tier: +- ultimate +options: + events: + - project_created_analytics_dashboard diff --git a/ee/config/metrics/counts_7d/20230504085937_projects_with_analytics_dashboard_weekly.yml b/ee/config/metrics/counts_7d/20230504085937_projects_with_analytics_dashboard_weekly.yml new file mode 100644 index 0000000000000000000000000000000000000000..482e6a48a78a0d1ed1a39b06f3adc4b635c0a661 --- /dev/null +++ b/ee/config/metrics/counts_7d/20230504085937_projects_with_analytics_dashboard_weekly.yml @@ -0,0 +1,21 @@ +--- +key_path: counts.projects_with_analytics_dashboard_weekly +description: Count of projects with an analytics dashboard +product_section: dev +product_stage: analyze +product_group: product_analytics +value_type: number +status: active +milestone: "16.0" +introduced_by_url: +time_frame: 7d +data_source: redis_hll +data_category: optional +instrumentation_class: RedisHLLMetric +distribution: +- ee +tier: +- ultimate +options: + events: + - project_created_analytics_dashboard diff --git a/ee/spec/services/ee/git/branch_push_service_spec.rb b/ee/spec/services/ee/git/branch_push_service_spec.rb index 33d2947ee01cbe3a47417613d5ea78bf276c1894..822b4ff19424bf1801d062378679b2b6056a072d 100644 --- a/ee/spec/services/ee/git/branch_push_service_spec.rb +++ b/ee/spec/services/ee/git/branch_push_service_spec.rb @@ -184,5 +184,38 @@ end end end + + context 'Product Analytics' do + using RSpec::Parameterized::TableSyntax + + where(:flag_enabled, :default_branch, :licence_available, :called) do + true | 'master' | true | true + true | 'master' | false | false + true | 'other' | true | false + true | 'other' | false | false + false | 'master' | true | false + false | 'master' | false | false + false | 'other' | true | false + false | 'other' | false | false + end + + before do + stub_feature_flags(product_analytics_dashboards: flag_enabled) + stub_licensed_features(product_analytics: licence_available) + allow(project).to receive(:default_branch).and_return(default_branch) + end + + with_them do + it 'enqueues the worker if appropriate' do + if called + expect(::ProductAnalytics::PostPushWorker).to receive(:perform_async).once + else + expect(::ProductAnalytics::PostPushWorker).not_to receive(:perform_async) + end + + subject.execute + end + end + end end end diff --git a/ee/spec/workers/product_analytics/post_push_worker_spec.rb b/ee/spec/workers/product_analytics/post_push_worker_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..37a9918797f42bd8070327e782823679152a5a4f --- /dev/null +++ b/ee/spec/workers/product_analytics/post_push_worker_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ProductAnalytics::PostPushWorker, feature_category: :product_analytics do + include RepoHelpers + + let_it_be(:project) { create(:project, :repository) } + + let(:commit) { project.repository.commit } + + subject { described_class.new.perform(project.id, commit.sha) } + + shared_examples 'tracks a usage event' do + it 'tracks a usage event' do + expect(Gitlab::UsageDataCounters::HLLRedisCounter) + .to receive(:track_usage_event).with(:project_created_analytics_dashboard, project.id) + + subject + end + end + + shared_examples 'does not track a usage event' do + it 'does not track a usage event' do + expect(Gitlab::Utils::UsageData).not_to receive(:track_usage_event) + + subject + end + end + + context 'when the commit includes a new dashboard' do + before do + create_new_dashboard + end + + it_behaves_like 'tracks a usage event' + end + + context 'when the commit includes a new file that is not a dashboard' do + it_behaves_like 'does not track a usage event' + end + + private + + def create_new_dashboard + project.repository.create_file( + project.creator, + '.gitlab/analytics/dashboards/dashboard_hello/dashboard_hello.yml', + 'content', + message: 'Add dashboard', + branch_name: 'master' + ) + end +end diff --git a/lib/gitlab/usage_data_counters/known_events/product_analytics.yml b/lib/gitlab/usage_data_counters/known_events/product_analytics.yml new file mode 100644 index 0000000000000000000000000000000000000000..b61e2f4e5a2b5f705d51dec3e6930d1412408078 --- /dev/null +++ b/lib/gitlab/usage_data_counters/known_events/product_analytics.yml @@ -0,0 +1,2 @@ +- name: project_created_analytics_dashboard + aggregation: weekly