Commit a5dafe72 authored by Mark Chao's avatar Mark Chao

Merge branch...

Merge branch '326420-generate-instrumentation-files-with-custom-metric-definition-generator' into 'master'

Add generator for new metric instrumentation classes

See merge request gitlab-org/gitlab!62250
parents c502748d 59e1368a
......@@ -90,6 +90,7 @@ RSpec/FilePath:
- 'spec/frontend/fixtures/*'
- 'ee/spec/frontend/fixtures/*'
- 'spec/requests/api/v3/*'
- 'spec/fixtures/**/*'
# Configuration parameters: AllowSubject.
RSpec/MultipleMemoizedHelpers:
......@@ -664,3 +665,8 @@ Style/RegexpLiteralMixedPreserve:
- mixed
- mixed_preserve
EnforcedStyle: mixed_preserve
RSpec/TopLevelDescribePath:
Exclude:
- 'spec/fixtures/**/*.rb'
- 'ee/spec/fixtures/**/*.rb'
......@@ -85,3 +85,18 @@ module Gitlab
end
end
```
## Creating a new metric instrumentation class
To create a stub instrumentation for a Usage Ping metric, you can use a dedicated [generator](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/generators/gitlab/usage_metric_generator.rb):
The generator takes the class name as an argument and the following options:
- `--type=TYPE` Required. Indicates the metric type. It must be one of: `database`, `generic`, `redis_hll`.
- `--ee` Indicates if the metric is for EE.
```shell
rails generate gitlab:usage_metric CountIssues --type database
create lib/gitlab/usage/metrics/instrumentations/count_issues_metric.rb
create spec/lib/gitlab/usage/metrics/instrumentations/count_issues_metric_spec.rb
```
Description:
Creates a stub instrumentation for a Service Ping metric
Example:
rails generate gitlab:usage_metric CountIssues --type database
This will create:
lib/gitlab/usage/metrics/instrumentations/count_issues_metric.rb
spec/lib/gitlab/usage/metrics/instrumentations/count_issues_metric_spec.rb
# frozen_string_literal: true
module Gitlab
module Usage
module Metrics
module Instrumentations
class <%= class_name %>Metric < <%= metric_superclass %>Metric
def value
end
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::<%= class_name %>Metric do
it_behaves_like 'a correct instrumented metric value', {}, 1
end
# frozen_string_literal: true
require 'rails/generators'
module Gitlab
class UsageMetricGenerator < Rails::Generators::Base
CE_DIR = 'lib/gitlab/usage/metrics/instrumentations'
EE_DIR = 'ee/lib/ee/gitlab/usage/metrics/instrumentations'
SPEC_CE_DIR = 'spec/lib/gitlab/usage/metrics/instrumentations'
SPEC_EE_DIR = 'ee/spec/lib/ee/gitlab/usage/metrics/instrumentations'
ALLOWED_SUPERCLASSES = {
generic: 'Generic',
database: 'Database',
redis_hll: 'RedisHLL'
}.freeze
source_root File.expand_path('templates', __dir__)
class_option :ee, type: :boolean, optional: true, default: false, desc: 'Indicates if instrumentation is for EE'
class_option :type, type: :string, desc: "Metric type, must be one of: #{ALLOWED_SUPERCLASSES.keys.join(', ')}"
argument :class_name, type: :string, desc: 'Instrumentation class name, e.g.: CountIssues'
def create_class_files
validate!
template "instrumentation_class.rb.template", file_path
template "instrumentation_class_spec.rb.template", spec_file_path
end
private
def validate!
raise ArgumentError, "Type is required, valid options are #{ALLOWED_SUPERCLASSES.keys.join(', ')}" unless type.present?
raise ArgumentError, "Unknown type '#{type}', valid options are #{ALLOWED_SUPERCLASSES.keys.join(', ')}" if metric_superclass.nil?
end
def ee?
options[:ee]
end
def type
options[:type]
end
def file_path
dir = ee? ? EE_DIR : CE_DIR
File.join(dir, file_name)
end
def spec_file_path
dir = ee? ? SPEC_EE_DIR : SPEC_CE_DIR
File.join(dir, spec_file_name)
end
def file_name
"#{class_name.underscore}_metric.rb"
end
def spec_file_name
"#{class_name.underscore}_metric_spec.rb"
end
def metric_superclass
ALLOWED_SUPERCLASSES[type.to_sym]
end
end
end
# frozen_string_literal: true
module Gitlab
module Usage
module Metrics
module Instrumentations
class CountFooMetric < RedisHLLMetric
def value
end
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountFooMetric do
it_behaves_like 'a correct instrumented metric value', {}, 1
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::UsageMetricGenerator, :silence_stdout do
let(:ce_temp_dir) { Dir.mktmpdir }
let(:ee_temp_dir) { Dir.mktmpdir }
let(:spec_ce_temp_dir) { Dir.mktmpdir }
let(:spec_ee_temp_dir) { Dir.mktmpdir }
let(:args) { ['CountFoo'] }
let(:options) { { 'type' => 'redis_hll' } }
before do
stub_const("#{described_class}::CE_DIR", ce_temp_dir)
stub_const("#{described_class}::EE_DIR", ee_temp_dir)
stub_const("#{described_class}::SPEC_CE_DIR", spec_ce_temp_dir)
stub_const("#{described_class}::SPEC_EE_DIR", spec_ee_temp_dir)
end
after do
FileUtils.rm_rf([ce_temp_dir, ee_temp_dir, spec_ce_temp_dir, spec_ee_temp_dir])
end
def expect_generated_file(directory, file_name, content)
file_path = File.join(directory, file_name)
file = File.read(file_path)
expect(file).to eq(content)
end
describe 'Creating metric instrumentation files' do
let(:sample_metric_dir) { 'lib/generators/gitlab/usage_metric_generator' }
let(:sample_metric) { fixture_file(File.join(sample_metric_dir, 'sample_metric.rb')) }
let(:sample_spec) { fixture_file(File.join(sample_metric_dir, 'sample_metric_test.rb')) }
it 'creates CE metric instrumentation files using the template' do
described_class.new(args, options).invoke_all
expect_generated_file(ce_temp_dir, 'count_foo_metric.rb', sample_metric)
expect_generated_file(spec_ce_temp_dir, 'count_foo_metric_spec.rb', sample_spec)
end
context 'with EE flag true' do
let(:options) { { 'type' => 'redis_hll', 'ee' => true } }
it 'creates EE metric instrumentation files using the template' do
described_class.new(args, options).invoke_all
expect_generated_file(ee_temp_dir, 'count_foo_metric.rb', sample_metric)
expect_generated_file(spec_ee_temp_dir, 'count_foo_metric_spec.rb', sample_spec)
end
end
context 'with type option missing' do
let(:options) { {} }
it 'raises an ArgumentError' do
expect { described_class.new(args, options).invoke_all }.to raise_error(ArgumentError, /Type is required/)
end
end
context 'with type option value not included in approved superclasses' do
let(:options) { { 'type' => 'some_other_type' } }
it 'raises an ArgumentError' do
expect { described_class.new(args, options).invoke_all }.to raise_error(ArgumentError, /Unknown type 'some_other_type'/)
end
end
end
end
......@@ -17,8 +17,10 @@ module Tooling
'app/assets/javascripts/tracking.js',
'spec/frontend/tracking_spec.js',
'generator_templates/usage_metric_definition/metric_definition.yml',
'lib/generators/gitlab/usage_metric/usage_metric_generator.rb',
'lib/generators/gitlab/usage_metric_definition_generator.rb',
'lib/generators/gitlab/usage_metric_definition/redis_hll_generator.rb',
'spec/lib/generators/gitlab/usage_metric_generator_spec.rb',
'spec/lib/generators/gitlab/usage_metric_definition_generator_spec.rb',
'spec/lib/generators/gitlab/usage_metric_definition/redis_hll_generator_spec.rb',
'config/metrics/schema.json'
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment