2021-01-05 04:10:15 -05:00
# frozen_string_literal: true
module Gitlab
module Usage
class MetricDefinition
METRIC_SCHEMA_PATH = Rails . root . join ( 'config' , 'metrics' , 'schema.json' )
2021-03-05 10:09:12 -05:00
BASE_REPO_PATH = 'https://gitlab.com/gitlab-org/gitlab/-/blob/master'
2021-04-06 08:09:21 -04:00
SKIP_VALIDATION_STATUSES = %w[ deprecated removed ] . to_set . freeze
2021-01-05 04:10:15 -05:00
attr_reader :path
attr_reader :attributes
def initialize ( path , opts = { } )
@path = path
@attributes = opts
end
def key
2021-01-21 04:08:52 -05:00
key_path
2021-01-05 04:10:15 -05:00
end
def to_h
attributes
end
2021-03-25 14:09:07 -04:00
def json_schema_path
return '' unless has_json_schema?
" #{ BASE_REPO_PATH } / #{ attributes [ :object_json_schema ] } "
end
def has_json_schema?
attributes [ :value_type ] == 'object' && attributes [ :object_json_schema ] . present?
end
2021-03-05 10:09:12 -05:00
def yaml_path
" #{ BASE_REPO_PATH } #{ path . delete_prefix ( Rails . root . to_s ) } "
end
2021-01-05 04:10:15 -05:00
def validate!
2021-01-25 07:09:07 -05:00
unless skip_validation?
self . class . schemer . validate ( attributes . stringify_keys ) . each do | error |
2021-03-16 14:11:53 -04:00
error_message = << ~ ERROR_MSG
Error type : #{error['type']}
Data : #{error['data']}
Path : #{error['data_pointer']}
Details : #{error['details']}
Metric file : #{path}
ERROR_MSG
Gitlab :: ErrorTracking . track_and_raise_for_dev_exception ( Gitlab :: Usage :: Metric :: InvalidMetricError . new ( error_message ) )
2021-01-25 07:09:07 -05:00
end
2021-01-05 04:10:15 -05:00
end
end
alias_method :to_dictionary , :to_h
class << self
def paths
2021-03-30 08:10:51 -04:00
@paths || = [ Rails . root . join ( 'config' , 'metrics' , '[^agg]*' , '*.yml' ) ]
2021-01-05 04:10:15 -05:00
end
2021-03-25 08:09:19 -04:00
def definitions ( skip_validation : false )
@skip_validation = skip_validation
2021-01-05 04:10:15 -05:00
@definitions || = load_all!
end
def schemer
@schemer || = :: JSONSchemer . schema ( Pathname . new ( METRIC_SCHEMA_PATH ) )
end
2021-04-09 05:09:10 -04:00
def dump_metrics_yaml
@metrics_yaml || = definitions . values . map ( & :to_h ) . map ( & :deep_stringify_keys ) . to_yaml
end
2021-01-05 04:10:15 -05:00
private
def load_all!
paths . each_with_object ( { } ) do | glob_path , definitions |
load_all_from_path! ( definitions , glob_path )
end
end
def load_from_file ( path )
definition = File . read ( path )
definition = YAML . safe_load ( definition )
definition . deep_symbolize_keys!
self . new ( path , definition ) . tap ( & :validate! )
rescue = > e
2021-03-15 14:09:05 -04:00
Gitlab :: ErrorTracking . track_and_raise_for_dev_exception ( Gitlab :: Usage :: Metric :: InvalidMetricError . new ( e . message ) )
2021-01-05 04:10:15 -05:00
end
def load_all_from_path! ( definitions , glob_path )
Dir . glob ( glob_path ) . each do | path |
definition = load_from_file ( path )
if previous = definitions [ definition . key ]
2021-03-15 14:09:05 -04:00
Gitlab :: ErrorTracking . track_and_raise_for_dev_exception ( Gitlab :: Usage :: Metric :: InvalidMetricError . new ( " Metric ' #{ definition . key } ' is already defined in ' #{ previous . path } ' " ) )
2021-01-05 04:10:15 -05:00
end
definitions [ definition . key ] = definition
end
end
end
private
def method_missing ( method , * args )
attributes [ method ] || super
end
2021-01-25 07:09:07 -05:00
def skip_validation?
2021-04-06 08:09:21 -04:00
! ! attributes [ :skip_validation ] || @skip_validation || SKIP_VALIDATION_STATUSES . include? ( attributes [ :status ] )
2021-01-25 07:09:07 -05:00
end
2021-01-05 04:10:15 -05:00
end
end
end
2021-01-07 16:10:18 -05:00
Gitlab :: Usage :: MetricDefinition . prepend_if_ee ( 'EE::Gitlab::Usage::MetricDefinition' )