From e9c42179686dab6e4d732c8a321aadf5fd5f34da Mon Sep 17 00:00:00 2001 From: Jonas Larsen <jlarsen@gitlab.com> Date: Tue, 28 May 2024 19:08:12 +0000 Subject: [PATCH] Encapsulate attributes in MetricDefinition --- lib/gitlab/tracking/event_definition.rb | 11 +-- lib/gitlab/usage/metric.rb | 2 +- lib/gitlab/usage/metric_definition.rb | 78 +++++++++++++++---- lib/service_ping/build_payload.rb | 4 +- .../gitlab/tracking/event_definition_spec.rb | 2 +- spec/lib/gitlab/usage/metric_spec.rb | 4 +- .../gitlab/usage/service_ping_report_spec.rb | 4 +- .../code_review_events_spec.rb | 8 +- .../matchers/internal_events_matchers.rb | 2 +- 9 files changed, 76 insertions(+), 39 deletions(-) diff --git a/lib/gitlab/tracking/event_definition.rb b/lib/gitlab/tracking/event_definition.rb index 2f3174329c740..fa880e41e4ba7 100644 --- a/lib/gitlab/tracking/event_definition.rb +++ b/lib/gitlab/tracking/event_definition.rb @@ -90,15 +90,10 @@ def find_event_selection_rules { name: attributes[:action], time_framed?: true, filter: {} } ] Gitlab::Usage::MetricDefinition.definitions.each_value do |metric_definition| - metric_definition.attributes[:events]&.each do |event_selection_rule| - if event_selection_rule[:name] == attributes[:action] - result << { - name: attributes[:action], - time_framed?: %w[7d 28d].include?(metric_definition.attributes[:time_frame]), - filter: event_selection_rule[:filter] || {} - } - end + matching_event_selection_rules = metric_definition.event_selection_rules.select do |event_selection_rule| + event_selection_rule[:name] == attributes[:action] end + result.concat(matching_event_selection_rules) end result.uniq end diff --git a/lib/gitlab/usage/metric.rb b/lib/gitlab/usage/metric.rb index 1efd8ded77cc5..ffa73f799ab3c 100644 --- a/lib/gitlab/usage/metric.rb +++ b/lib/gitlab/usage/metric.rb @@ -39,7 +39,7 @@ def unflatten_key_path(value) def instrumentation_object instrumentation_class = "Gitlab::Usage::Metrics::Instrumentations::#{definition.instrumentation_class}" - @instrumentation_object ||= instrumentation_class.constantize.new(definition.attributes) + @instrumentation_object ||= instrumentation_class.constantize.new(definition.raw_attributes) end end end diff --git a/lib/gitlab/usage/metric_definition.rb b/lib/gitlab/usage/metric_definition.rb index 65a50391c1899..cb3af222b141a 100644 --- a/lib/gitlab/usage/metric_definition.rb +++ b/lib/gitlab/usage/metric_definition.rb @@ -11,7 +11,6 @@ class MetricDefinition InvalidError = Class.new(RuntimeError) attr_reader :path - attr_reader :attributes def initialize(path, opts = {}) @path = path @@ -19,28 +18,49 @@ def initialize(path, opts = {}) end def key - attributes[:key_path] + @attributes[:key_path] end alias_method :key_path, :key - def events events_from_new_structure || events_from_old_structure || {} end + def event_selection_rules + return [] unless @attributes[:events] + + @attributes[:events].map do |event| + { + name: event[:name], + time_framed?: time_framed?, + filter: event[:filter] || {} + } + end + end + def instrumentation_class if internal_events? events.each_value.first.nil? ? "TotalCountMetric" : "RedisHLLMetric" else - attributes[:instrumentation_class] + @attributes[:instrumentation_class] end end + # This method can be removed when the refactoring is complete. It is only here to + # limit access to @attributes in a gradual manner. + def raw_attributes + @attributes + end + def status - attributes[:status] + @attributes[:status] end def value_json_schema - attributes[:value_json_schema] + @attributes[:value_json_schema] + end + + def value_type + @attributes[:value_type] end def to_context @@ -50,7 +70,7 @@ def to_context end def to_h - attributes + @attributes end def json_schema @@ -62,15 +82,15 @@ def json_schema def json_schema_path return '' unless has_json_schema? - Rails.root.join(attributes[:value_json_schema]) + Rails.root.join(@attributes[:value_json_schema]) end def has_json_schema? - attributes[:value_type] == 'object' && attributes[:value_json_schema].present? + @attributes[:value_type] == 'object' && @attributes[:value_json_schema].present? end def validation_errors - SCHEMA.validate(attributes.deep_stringify_keys).map do |error| + SCHEMA.validate(@attributes.deep_stringify_keys).map do |error| <<~ERROR_MSG --------------- VALIDATION ERROR --------------- Metric file: #{path} @@ -81,16 +101,40 @@ def validation_errors end end + def product_group + @attributes[:product_group] + end + + def time_frame + @attributes[:time_frame] + end + + def time_framed? + %w[7d 28d].include?(time_frame) + end + + def active? + status == 'active' + end + + def broken? + status == 'broken' + end + def available? - AVAILABLE_STATUSES.include?(attributes[:status]) + AVAILABLE_STATUSES.include?(status) end def valid_service_ping_status? - VALID_SERVICE_PING_STATUSES.include?(attributes[:status]) + VALID_SERVICE_PING_STATUSES.include?(status) + end + + def data_category + @attributes[:data_category] end def data_source - attributes[:data_source] + @attributes[:data_source] end def internal_events? @@ -113,12 +157,12 @@ def all end def not_removed - all.select { |definition| definition.attributes[:status] != 'removed' }.index_by(&:key_path) + all.select { |definition| definition.status != 'removed' }.index_by(&:key_path) end def with_instrumentation_class all.select do |definition| - (definition.internal_events? || definition.attributes[:instrumentation_class].present?) && definition.available? + (definition.internal_events? || definition.instrumentation_class.present?) && definition.available? end end @@ -164,14 +208,14 @@ def load_all_from_path!(definitions, glob_path) private def events_from_new_structure - events = attributes[:events] + events = @attributes[:events] return unless events events.to_h { |event| [event[:name], event[:unique]&.to_sym] } end def events_from_old_structure - options_events = attributes.dig(:options, :events) + options_events = @attributes.dig(:options, :events) return unless options_events options_events.index_with { nil } diff --git a/lib/service_ping/build_payload.rb b/lib/service_ping/build_payload.rb index 0f19cd55a4cb8..0fffd16795b51 100644 --- a/lib/service_ping/build_payload.rb +++ b/lib/service_ping/build_payload.rb @@ -47,9 +47,7 @@ def has_metric_definition?(key_path) end def metric_category(key_path) - metric_definitions[key_path] - &.attributes - &.fetch(:data_category, ::ServicePing::PermitDataCategories::OPTIONAL_CATEGORY) + metric_definitions[key_path]&.data_category || ::ServicePing::PermitDataCategories::OPTIONAL_CATEGORY end def metric_definitions diff --git a/spec/lib/gitlab/tracking/event_definition_spec.rb b/spec/lib/gitlab/tracking/event_definition_spec.rb index 6b3d777eae3f9..6eeb49b1de7e4 100644 --- a/spec/lib/gitlab/tracking/event_definition_spec.rb +++ b/spec/lib/gitlab/tracking/event_definition_spec.rb @@ -65,7 +65,7 @@ def write_metric(metric, path, content) it 'has event definitions for all events used in Internal Events metric definitions', :aggregate_failures do from_metric_definitions = Gitlab::Usage::MetricDefinition.not_removed .values - .select { |m| m.attributes[:data_source] == 'internal_events' } + .select(&:internal_events?) .flat_map { |m| m.events&.keys } .compact .uniq diff --git a/spec/lib/gitlab/usage/metric_spec.rb b/spec/lib/gitlab/usage/metric_spec.rb index 42d2f394ce3de..8cb052421f981 100644 --- a/spec/lib/gitlab/usage/metric_spec.rb +++ b/spec/lib/gitlab/usage/metric_spec.rb @@ -25,7 +25,7 @@ let(:issue_count_metric_definiton) do double(:issue_count_metric_definiton, - attributes.merge({ attributes: attributes }) + attributes.merge({ raw_attributes: attributes }) ) end @@ -49,7 +49,7 @@ let(:instrumentation_class) { "UnavailableMetric" } let(:issue_count_metric_definiton) do double(:issue_count_metric_definiton, - attributes.merge({ attributes: attributes, instrumentation_class: instrumentation_class }) + attributes.merge({ raw_attributes: attributes, instrumentation_class: instrumentation_class }) ) end diff --git a/spec/lib/gitlab/usage/service_ping_report_spec.rb b/spec/lib/gitlab/usage/service_ping_report_spec.rb index 098661986395f..2890522306c94 100644 --- a/spec/lib/gitlab/usage/service_ping_report_spec.rb +++ b/spec/lib/gitlab/usage/service_ping_report_spec.rb @@ -143,7 +143,7 @@ def build_payload_from_queries(payload, accumulator = [], key_path = []) end def type_cast_to_defined_type(value, metric_definition) - case metric_definition&.attributes&.fetch(:value_type) + case metric_definition&.value_type when "string" value.to_s when "number" @@ -181,7 +181,7 @@ def type_cast_to_defined_type(value, metric_definition) metric_definition = metric_definitions[key_path.join('.')] # Skip broken metrics since they are usually overriden to return -1 - next if metric_definition&.attributes&.fetch(:status) == 'broken' + next if metric_definition&.broken? value = type_cast_to_defined_type(value, metric_definition) payload_value = service_ping_payload.dig(*key_path) diff --git a/spec/lib/gitlab/usage_data_counters/code_review_events_spec.rb b/spec/lib/gitlab/usage_data_counters/code_review_events_spec.rb index 61e78c1c27f6d..434506d40bf14 100644 --- a/spec/lib/gitlab/usage_data_counters/code_review_events_spec.rb +++ b/spec/lib/gitlab/usage_data_counters/code_review_events_spec.rb @@ -20,8 +20,8 @@ ] all_code_review_events = Gitlab::Usage::MetricDefinition.all.flat_map do |definition| - next [] unless definition.attributes[:key_path].include?('.code_review.') && - definition.attributes[:status] == 'active' && + next [] unless definition.key_path.include?('.code_review.') && + definition.active? && definition.events.count == 1 definition.events.keys @@ -37,8 +37,8 @@ end def code_review_aggregated_metric?(definition) - definition.attributes[:product_group] == 'code_review' && - definition.attributes[:status] == 'active' && + definition.product_group == 'code_review' && + definition.active? && definition.events.count > 1 end end diff --git a/spec/support/matchers/internal_events_matchers.rb b/spec/support/matchers/internal_events_matchers.rb index 1fac2e185b22b..29b9a20be8eb0 100644 --- a/spec/support/matchers/internal_events_matchers.rb +++ b/spec/support/matchers/internal_events_matchers.rb @@ -384,7 +384,7 @@ def build_matcher # to be passed to a change matcher def metric_value_tracker(key_path, metric_definition) proc do - stub_usage_data_connections if metric_definition.attributes[:data_source] == 'database' + stub_usage_data_connections if metric_definition.data_source == 'database' metric = Gitlab::Usage::Metric.new(metric_definition) instrumentation_object = stub_metric_timeframe(metric) -- GitLab