diff --git a/config/metrics/counts_all/20210216180232_projects_jira_dvcs_cloud_active.yml b/config/metrics/counts_all/20210216180232_projects_jira_dvcs_cloud_active.yml index 29edaa6ab3b4140daa818b7b4730327c52011a88..5bf8e1d6e7820b86da26af027518c9332cb22dca 100644 --- a/config/metrics/counts_all/20210216180232_projects_jira_dvcs_cloud_active.yml +++ b/config/metrics/counts_all/20210216180232_projects_jira_dvcs_cloud_active.yml @@ -9,6 +9,9 @@ value_type: number status: active time_frame: all data_source: database +instrumentation_class: CountProjectsWithJiraDvcsIntegrationMetric +options: + cloud: true distribution: - ce - ee diff --git a/config/metrics/counts_all/20210216180234_projects_jira_dvcs_server_active.yml b/config/metrics/counts_all/20210216180234_projects_jira_dvcs_server_active.yml index 9673956c7e14239370647b624bb9811fd2249ee9..bfb402c257ea98b24930c4db4c771ff4c32d8600 100644 --- a/config/metrics/counts_all/20210216180234_projects_jira_dvcs_server_active.yml +++ b/config/metrics/counts_all/20210216180234_projects_jira_dvcs_server_active.yml @@ -9,6 +9,9 @@ value_type: number status: active time_frame: all data_source: database +instrumentation_class: CountProjectsWithJiraDvcsIntegrationMetric +options: + cloud: false distribution: - ce - ee diff --git a/ee/config/metrics/counts_all/20210216180236_projects_jira_issuelist_active.yml b/ee/config/metrics/counts_all/20210216180236_projects_jira_issuelist_active.yml index 7384333f8485f520964ade79e95f575b67633d40..bbee0595a21ec9f17f05902beb6a4b605142e547 100644 --- a/ee/config/metrics/counts_all/20210216180236_projects_jira_issuelist_active.yml +++ b/ee/config/metrics/counts_all/20210216180236_projects_jira_issuelist_active.yml @@ -9,6 +9,7 @@ value_type: number status: active time_frame: all data_source: database +instrumentation_class: CountProjectsWithJiraIssuelistActiveMetric distribution: - ee tier: diff --git a/ee/lib/ee/gitlab/usage_data.rb b/ee/lib/ee/gitlab/usage_data.rb index aa72901a7195557eb58026108b12879c3ebf1d7f..86b00724094e543a44b266d1f4179e4189b06885 100644 --- a/ee/lib/ee/gitlab/usage_data.rb +++ b/ee/lib/ee/gitlab/usage_data.rb @@ -139,13 +139,6 @@ def system_usage_data end end - override :jira_usage - def jira_usage - super.merge( - projects_jira_issuelist_active: projects_jira_issuelist_active - ) - end - # Omitted because no user, creator or author associated: `auto_devops_disabled`, `auto_devops_enabled` # Omitted because not in use anymore: `gcp_clusters`, `gcp_clusters_disabled`, `gcp_clusters_enabled` # rubocop:disable CodeReuse/ActiveRecord @@ -335,17 +328,6 @@ def merge_requests_with_overridden_project_rules(time_period = nil) ) end - def projects_jira_issuelist_active - # rubocop: disable UsageData/LargeTable: - min_id = minimum_id(::Integrations::JiraTrackerData.where(issues_enabled: true), :integration_id) - max_id = maximum_id(::Integrations::JiraTrackerData.where(issues_enabled: true), :integration_id) - # rubocop: enable UsageData/LargeTable: - # rubocop: disable UsageData/DistinctCountByLargeForeignKey - distinct_count(::Integrations::Jira.active.left_outer_joins(:jira_tracker_data).where(jira_tracker_data: { issues_enabled: true }), start: min_id, finish: max_id) - # rubocop: enable UsageData/DistinctCountByLargeForeignKey - end - # rubocop:enable CodeReuse/ActiveRecord - # rubocop:disable CodeReuse/ActiveRecord def projects_with_sectional_code_owner_rules(time_period) distinct_count( diff --git a/ee/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_issuelist_active_metric.rb b/ee/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_issuelist_active_metric.rb new file mode 100644 index 0000000000000000000000000000000000000000..df8f1dc1bec3470435a27bade2f7864e4af46548 --- /dev/null +++ b/ee/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_issuelist_active_metric.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Gitlab + module Usage + module Metrics + module Instrumentations + class CountProjectsWithJiraIssuelistActiveMetric < DatabaseMetric + operation :distinct_count + + start do + ::Integrations::JiraTrackerData.where(issues_enabled: true).minimum(:integration_id) + end + + finish do + ::Integrations::JiraTrackerData.where(issues_enabled: true).maximum(:integration_id) + end + + relation do + ::Integrations::Jira + .active + .left_outer_joins(:jira_tracker_data) + .where(jira_tracker_data: { issues_enabled: true }) + end + end + end + end + end +end diff --git a/ee/spec/lib/ee/gitlab/usage_data_spec.rb b/ee/spec/lib/ee/gitlab/usage_data_spec.rb index bfd41f42c5e6270b603e8947c246909346136e0c..4d01f72628012ade77a7ee5af6436d46fd48fe6f 100644 --- a/ee/spec/lib/ee/gitlab/usage_data_spec.rb +++ b/ee/spec/lib/ee/gitlab/usage_data_spec.rb @@ -96,7 +96,6 @@ ldap_users operations_dashboard_default_dashboard operations_dashboard_users_with_projects_added - projects_jira_issuelist_active projects_mirrored_with_pipelines_enabled projects_reporting_ci_cd_back_to_github status_page_issues @@ -108,7 +107,6 @@ expect(count_data[:status_page_projects]).to eq(1) expect(count_data[:status_page_issues]).to eq(1) expect(count_data[:issues_with_health_status]).to eq(2) - expect(count_data[:projects_jira_issuelist_active]).to eq(1) expect(count_data[:epic_issues]).to eq(2) end diff --git a/ee/spec/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_issuelist_active_metric_spec.rb b/ee/spec/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_issuelist_active_metric_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..e0420c91012c92c5a9e334b5204f382734875bab --- /dev/null +++ b/ee/spec/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_issuelist_active_metric_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountProjectsWithJiraIssuelistActiveMetric, + feature_category: :integrations do + let_it_be(:project_1) { create(:project) } + let_it_be(:project_2) { create(:project) } + let_it_be(:jira_project_with_issuelist) do + create(:jira_integration, project: project_1, issues_enabled: true, project_key: 'foo') + end + + let_it_be(:jira_project_without_issuelist) do + create(:jira_integration, project: project_2, issues_enabled: false, project_key: 'bar') + end + + let(:expected_value) { 1 } + let(:expected_query) do + 'SELECT COUNT(DISTINCT "integrations"."id") FROM "integrations" ' \ + 'LEFT OUTER JOIN "jira_tracker_data" ON "jira_tracker_data"."integration_id" = "integrations"."id" ' \ + 'WHERE "integrations"."type_new" = \'Integrations::Jira\' AND "integrations"."active" = TRUE ' \ + 'AND "jira_tracker_data"."issues_enabled" = TRUE' + end + + it_behaves_like 'a correct instrumented metric value and query', { time_frame: 'all', data_source: 'database' } +end diff --git a/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_dvcs_integration_metric.rb b/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_dvcs_integration_metric.rb new file mode 100644 index 0000000000000000000000000000000000000000..25a45a259e2876a5817643271277bb2798e36527 --- /dev/null +++ b/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_dvcs_integration_metric.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module Usage + module Metrics + module Instrumentations + class CountProjectsWithJiraDvcsIntegrationMetric < DatabaseMetric + operation :count + + def initialize(metric_definition) + super + + raise ArgumentError, "option 'cloud' must be a boolean" unless [true, false].include?(options[:cloud]) + end + + relation do |options| + ProjectFeatureUsage.with_jira_dvcs_integration_enabled(cloud: options[:cloud]) + end + end + end + end + end +end diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index 72168bce782f8804fb6842261ecf7031f10ce479..2da16cf8725156bb28e3d5982262ae7cf685bb99 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -286,16 +286,9 @@ def integrations_usage response[:"instances_#{name}_active"] = count(Integration.active.where(instance: true, type: type)) response[:"projects_inheriting_#{name}_active"] = count(Integration.active.where.not(project: nil).where.not(inherit_from_id: nil).where(type: type)) response[:"groups_inheriting_#{name}_active"] = count(Integration.active.where.not(group: nil).where.not(inherit_from_id: nil).where(type: type)) - end.merge(jira_usage, jira_import_usage) + end.merge(jira_import_usage) # rubocop: enable UsageData/LargeTable: end - - def jira_usage - { - projects_jira_dvcs_cloud_active: count(ProjectFeatureUsage.with_jira_dvcs_integration_enabled), - projects_jira_dvcs_server_active: count(ProjectFeatureUsage.with_jira_dvcs_integration_enabled(cloud: false)) - } - end # rubocop: enable CodeReuse/ActiveRecord def jira_import_usage diff --git a/rubocop/rubocop-code_reuse.yml b/rubocop/rubocop-code_reuse.yml index c1babff4b12ead898fd985e47f9f80a7c5e4cc4d..f96de5caf994d1e8a7880d8c3c2dc46f148cf8bb 100644 --- a/rubocop/rubocop-code_reuse.yml +++ b/rubocop/rubocop-code_reuse.yml @@ -42,3 +42,4 @@ CodeReuse/ActiveRecord: - ee/lib/tasks/**/*.rake - ee/lib/ee/gitlab/background_migration/**/*.rb - ee/lib/gitlab/llm/open_ai/response_modifiers/tanuki_bot.rb + - ee/lib/gitlab/usage/metrics/instrumentations/**/*.rb diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_dvcs_integration_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_dvcs_integration_metric_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..a2d86fc50444202ae7d9cd844af0a96bd1c1a6d5 --- /dev/null +++ b/spec/lib/gitlab/usage/metrics/instrumentations/count_projects_with_jira_dvcs_integration_metric_spec.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountProjectsWithJiraDvcsIntegrationMetric, + feature_category: :integrations do + describe 'metric value and query' do + let_it_be_with_reload(:project_1) { create(:project) } + let_it_be_with_reload(:project_2) { create(:project) } + let_it_be_with_reload(:project_3) { create(:project) } + + before do + project_1.feature_usage.log_jira_dvcs_integration_usage(cloud: false) + project_2.feature_usage.log_jira_dvcs_integration_usage(cloud: false) + project_3.feature_usage.log_jira_dvcs_integration_usage(cloud: true) + end + + context 'when counting cloud integrations' do + let(:expected_value) { 1 } + let(:expected_query) do + 'SELECT COUNT("project_feature_usages"."project_id") FROM "project_feature_usages" ' \ + 'WHERE "project_feature_usages"."jira_dvcs_cloud_last_sync_at" IS NOT NULL' + end + + it_behaves_like 'a correct instrumented metric value and query', { time_frame: 'all', options: { cloud: true } } + end + + context 'when counting non-cloud integrations' do + let(:expected_value) { 2 } + let(:expected_query) do + 'SELECT COUNT("project_feature_usages"."project_id") FROM "project_feature_usages" ' \ + 'WHERE "project_feature_usages"."jira_dvcs_server_last_sync_at" IS NOT NULL' + end + + it_behaves_like 'a correct instrumented metric value and query', { time_frame: 'all', options: { cloud: false } } + end + end + + it "raises an exception if option is not present" do + expect do + described_class.new(options: {}, time_frame: 'all') + end.to raise_error(ArgumentError, %r{must be a boolean}) + end + + it "raises an exception if option has invalid value" do + expect do + described_class.new(options: { cloud: 'yes' }, time_frame: 'all') + end.to raise_error(ArgumentError, %r{must be a boolean}) + end +end diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb index 73f7a79dd5be5aa93055a6006df5b0b75b9f65f9..8ac3b0c134b819066269272f989ef57fa581af7a 100644 --- a/spec/support/helpers/usage_data_helpers.rb +++ b/spec/support/helpers/usage_data_helpers.rb @@ -50,8 +50,6 @@ module UsageDataHelpers projects_asana_active projects_jenkins_active projects_jira_active - projects_jira_dvcs_cloud_active - projects_jira_dvcs_server_active projects_slack_active projects_slack_slash_commands_active projects_custom_issue_tracker_active