2021-03-08 18:12:59 +05:30
# frozen_string_literal: true
module Gitlab
module Usage
class MetricDefinition
METRIC_SCHEMA_PATH = Rails . root . join ( 'config' , 'metrics' , 'schema.json' )
2021-04-17 20:07:23 +05:30
BASE_REPO_PATH = 'https://gitlab.com/gitlab-org/gitlab/-/blob/master'
2021-04-29 21:17:54 +05:30
SKIP_VALIDATION_STATUSES = %w[ deprecated removed ] . to_set . freeze
2021-03-08 18:12:59 +05:30
attr_reader :path
attr_reader :attributes
def initialize ( path , opts = { } )
@path = path
@attributes = opts
end
def key
2021-03-11 19:13:27 +05:30
key_path
2021-03-08 18:12:59 +05:30
end
def to_h
attributes
end
2021-04-29 21:17:54 +05:30
def json_schema_path
return '' unless has_json_schema?
2021-06-08 01:23:25 +05:30
" #{ BASE_REPO_PATH } / #{ attributes [ :value_json_schema ] } "
2021-04-29 21:17:54 +05:30
end
def has_json_schema?
2021-06-08 01:23:25 +05:30
attributes [ :value_type ] == 'object' && attributes [ :value_json_schema ] . present?
2021-04-29 21:17:54 +05:30
end
2021-04-17 20:07:23 +05:30
def yaml_path
" #{ BASE_REPO_PATH } #{ path . delete_prefix ( Rails . root . to_s ) } "
end
2021-03-08 18:12:59 +05:30
def validate!
2021-03-11 19:13:27 +05:30
unless skip_validation?
self . class . schemer . validate ( attributes . stringify_keys ) . each do | error |
2021-04-29 21:17:54 +05:30
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-03-11 19:13:27 +05:30
end
2021-03-08 18:12:59 +05:30
end
end
alias_method :to_dictionary , :to_h
class << self
def paths
2021-04-29 21:17:54 +05:30
@paths || = [ Rails . root . join ( 'config' , 'metrics' , '[^agg]*' , '*.yml' ) ]
2021-03-08 18:12:59 +05:30
end
2021-04-29 21:17:54 +05:30
def definitions ( skip_validation : false )
@skip_validation = skip_validation
2021-03-08 18:12:59 +05:30
@definitions || = load_all!
end
2021-06-08 01:23:25 +05:30
def all
@all || = definitions . map { | _key_path , definition | definition }
end
2021-03-08 18:12:59 +05:30
def schemer
@schemer || = :: JSONSchemer . schema ( Pathname . new ( METRIC_SCHEMA_PATH ) )
end
2021-04-29 21:17:54 +05:30
def dump_metrics_yaml
@metrics_yaml || = definitions . values . map ( & :to_h ) . map ( & :deep_stringify_keys ) . to_yaml
end
2021-03-08 18:12:59 +05:30
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! )
2021-06-08 01:23:25 +05:30
rescue StandardError = > e
2021-04-17 20:07:23 +05:30
Gitlab :: ErrorTracking . track_and_raise_for_dev_exception ( Gitlab :: Usage :: Metric :: InvalidMetricError . new ( e . message ) )
2021-03-08 18:12:59 +05:30
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-04-17 20:07:23 +05:30
Gitlab :: ErrorTracking . track_and_raise_for_dev_exception ( Gitlab :: Usage :: Metric :: InvalidMetricError . new ( " Metric ' #{ definition . key } ' is already defined in ' #{ previous . path } ' " ) )
2021-03-08 18:12:59 +05:30
end
definitions [ definition . key ] = definition
end
end
end
private
def method_missing ( method , * args )
attributes [ method ] || super
end
2021-03-11 19:13:27 +05:30
def skip_validation?
2021-04-29 21:17:54 +05:30
! ! attributes [ :skip_validation ] || @skip_validation || SKIP_VALIDATION_STATUSES . include? ( attributes [ :status ] )
2021-03-11 19:13:27 +05:30
end
2021-03-08 18:12:59 +05:30
end
end
end
2021-06-08 01:23:25 +05:30
Gitlab :: Usage :: MetricDefinition . prepend_mod_with ( 'Gitlab::Usage::MetricDefinition' )