Коммит b0e09f06 создал по автору Pavel Shutsin's avatar Pavel Shutsin
Просмотр файлов

Extend DORA API to support multiple metrics

Graphql API now supports query
for multiple metrics

Changelog: added
владелец d66a7e4b
......@@ -12927,8 +12927,9 @@ Returns [`[DoraMetric!]`](#dorametric).
| <a id="dorametricsenddate"></a>`endDate` | [`Date`](#date) | Date range to end at. Default is the current date. |
| <a id="dorametricsenvironmenttier"></a>`environmentTier` | [`DeploymentTier`](#deploymenttier) | Deployment tier of the environments to return. Deprecated, please update to `environment_tiers` param. |
| <a id="dorametricsenvironmenttiers"></a>`environmentTiers` | [`[DeploymentTier!]`](#deploymenttier) | Deployment tiers of the environments to return. Defaults to `[PRODUCTION]`. |
| <a id="dorametricsinterval"></a>`interval` | [`DoraMetricBucketingInterval`](#dorametricbucketinginterval) | How the metric should be aggregrated. Defaults to `DAILY`. In the case of `ALL`, the `date` field in the response will be `null`. |
| <a id="dorametricsmetric"></a>`metric` | [`DoraMetricType!`](#dorametrictype) | Type of metric to return. |
| <a id="dorametricsinterval"></a>`interval` | [`DoraMetricBucketingInterval`](#dorametricbucketinginterval) | How the metrics should be aggregated. Defaults to `DAILY`. In the case of `ALL`, the `date` fields in the response will be `null`. |
| <a id="dorametricsmetric"></a>`metric` | [`DoraMetricType`](#dorametrictype) | Type of metric to return. Deprecated, please update to `metrics` param. |
| <a id="dorametricsmetrics"></a>`metrics` | [`[DoraMetricType!]`](#dorametrictype) | Types of metrics to return. |
| <a id="dorametricsstartdate"></a>`startDate` | [`Date`](#date) | Date range to start from. Default is 3 months ago. |
 
### `DoraMetric`
......@@ -12938,6 +12939,7 @@ Returns [`[DoraMetric!]`](#dorametric).
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="dorametricdate"></a>`date` | [`String`](#string) | Date of the data point. |
| <a id="dorametricmetrickey"></a>`metricKey` | [`DoraMetricType!`](#dorametrictype) | Metric type. |
| <a id="dorametricvalue"></a>`value` | [`Float`](#float) | Value of the data point. |
 
### `EgressNode`
......@@ -10,8 +10,12 @@ class DoraMetricsResolver < BaseResolver
alias_method :container, :object
argument :metric, Types::DoraMetricTypeEnum,
required: true,
description: 'Type of metric to return.'
required: false,
description: 'Type of metric to return. Deprecated, please update to `metrics` param.'
argument :metrics, [Types::DoraMetricTypeEnum],
required: false,
description: 'Types of metrics to return.'
argument :start_date, Types::DateType,
required: false,
......@@ -23,7 +27,7 @@ class DoraMetricsResolver < BaseResolver
argument :interval, Types::DoraMetricBucketingIntervalEnum,
required: false,
description: 'How the metric should be aggregrated. Defaults to `DAILY`. In the case of `ALL`, the `date` field in the response will be `null`.'
description: 'How the metrics should be aggregated. Defaults to `DAILY`. In the case of `ALL`, the `date` fields in the response will be `null`.'
argument :environment_tier, Types::DeploymentTierEnum,
required: false,
......@@ -40,27 +44,19 @@ def resolve(params)
params[:environment_tiers] |= [params[:environment_tier]]
end
# Backwards compatibility until %16.0
if params[:metric]
params[:metrics] ||= []
params[:metrics] |= [params[:metric]]
end
result = ::Dora::AggregateMetricsService
.new(container: container, current_user: current_user, params: params)
.execute
raise Gitlab::Graphql::Errors::ArgumentError, result[:message] unless result[:status] == :success
data = result[:data]
if data.is_a? Numeric
# When interval=ALL, the service above returns a single number (float or integer)
# instead of an array of hashes, like it does otherwise.
# To keep the return value of this resolver consistent, we wrap
# it in the structure we expect.
#
# This can be removed if/when we update the service to always
# return a consistent shape:
# https://gitlab.com/gitlab-org/gitlab/-/issues/334821
[{ 'date' => nil, 'value' => data }]
else
data
end
result[:data]
end
end
end
......@@ -8,6 +8,8 @@ class DoraMetricType < BaseObject
field :date, GraphQL::Types::String, null: true, description: 'Date of the data point.'
field :value, GraphQL::Types::Float, null: true, description: 'Value of the data point.'
field :metric_key, Types::DoraMetricTypeEnum, null: false, description: 'Metric type.'
end
# rubocop: enable Graphql/AuthorizeTypes
end
......@@ -33,7 +33,8 @@ def aggregate_for!(metrics, interval)
case interval
when INTERVAL_ALL
row = select(select_query_part).take
transform_aggregated_row(row.attributes.merge('interval_date' => nil), metrics)
[transform_aggregated_row(row.attributes.merge('interval_date' => nil), metrics)]
when INTERVAL_MONTHLY
select("DATE_TRUNC('month', date)::date AS interval_date, #{select_query_part}")
.group("DATE_TRUNC('month', date)")
......
......@@ -31,9 +31,7 @@ def execute_without_authorization
.in_range_of(start_date, end_date)
.aggregate_for!(metrics, interval)
data = backwards_compatibility_convert(data)
success(data: data)
success(data: unfold_aggregated_rows(data))
end
private
......@@ -131,19 +129,23 @@ def interval
end
def metrics
Array(params[:metric])
params[:metrics] || []
end
def group_project_ids
Array(params[:group_project_ids])
end
def backwards_compatibility_convert(new_data)
metric = metrics.first
def unfold_aggregated_rows(data)
result = []
return new_data[metric] if interval == ::Dora::DailyMetrics::INTERVAL_ALL
data.each do |row|
metrics.each do |metric|
result << { 'date' => row['date'], 'value' => row[metric], 'metric_key' => metric }
end
end
new_data.map { |row| { 'date' => row['date'], 'value' => row[metric] } }
result
end
end
end
......@@ -43,14 +43,20 @@ def fetch!(container)
params[:environment_tiers] |= [params[:environment_tier]]
end
params[:metrics] = [params[:metric]]
result = ::Dora::AggregateMetricsService
.new(container: container, current_user: current_user, params: params)
.execute
if result[:status] == :success
present result[:data]
return render_api_error!(result[:message], result[:http_status]) unless result[:status] == :success
# Legacy API design requires us to return simple number if the request interval is "ALL"
# @see https://gitlab.com/gitlab-org/gitlab/-/issues/334821
if params[:interval] == 'all'
present result[:data].first['value']
else
render_api_error!(result[:message], result[:http_status])
present result[:data]
end
end
end
......@@ -69,14 +75,9 @@ def fetch!(container)
message: 'successful operation',
examples: {
successfull_response: [
{ "date" => "2021-03-01", "value" => 3 },
{ "date" => "2021-03-02", "value" => 6 },
{ "date" => "2021-03-03", "value" => 0 },
{ "date" => "2021-03-04", "value" => 0 },
{ "date" => "2021-03-05", "value" => 0 },
{ "date" => "2021-03-06", "value" => 0 },
{ "date" => "2021-03-07", "value" => 0 },
{ "date" => "2021-03-08", "value" => 4 }
{ "date" => "2021-03-01", "value" => 3, 'metric_key' => 'deployment_frequency' },
{ "date" => "2021-03-02", "value" => 0, 'metric_key' => 'deployment_frequency' },
{ "date" => "2021-03-03", "value" => nil, 'metric_key' => 'deployment_frequency' }
]
}
}
......@@ -109,14 +110,9 @@ def fetch!(container)
message: 'successful operation',
examples: {
successfull_response: [
{ "date" => "2021-03-01", "value" => 3 },
{ "date" => "2021-03-02", "value" => 6 },
{ "date" => "2021-03-03", "value" => 0 },
{ "date" => "2021-03-04", "value" => 0 },
{ "date" => "2021-03-05", "value" => 0 },
{ "date" => "2021-03-06", "value" => 0 },
{ "date" => "2021-03-07", "value" => 0 },
{ "date" => "2021-03-08", "value" => 4 }
{ "date" => "2021-03-01", "value" => 3, 'metric_key' => 'deployment_frequency' },
{ "date" => "2021-03-02", "value" => 0, 'metric_key' => 'deployment_frequency' },
{ "date" => "2021-03-03", "value" => nil, 'metric_key' => 'deployment_frequency' }
]
}
}
......
......@@ -25,7 +25,9 @@ def deployment_count_via_dora_api
params: dora_aggregate_metrics_params
).execute_without_authorization
result[:status] == :success ? (result[:data] || 0) : 0
return 0 unless result[:status] == :success
result[:data].first['value'] || 0
end
def dora_aggregate_metrics_params
......@@ -34,7 +36,7 @@ def dora_aggregate_metrics_params
end_date: (options[:to] || Date.today).to_date,
interval: 'all',
environment_tiers: %w[production],
metric: 'deployment_frequency'
metrics: ['deployment_frequency']
}
end
end
......
......@@ -20,7 +20,7 @@ def value
metric = dora_metric
# nil signals the summary class to not even try to serialize the result
metric[:status] == :success ? convert_to_days(metric[:data]) : nil
metric[:status] == :success ? convert_to_days(metric[:data].first['value']) : nil
end
end
......@@ -42,7 +42,7 @@ def dora_metric
end_date: to,
interval: 'all',
environment_tiers: %w[production],
metric: metric_key
metrics: [metric_key]
}
params[:group_project_ids] = options[:projects] if options[:projects].present?
......
......@@ -18,7 +18,8 @@ def value
metric = dora_metric
if metric[:status] == :success
metric[:data] ? (metric[:data] * 100).round(2) : 0
value = metric[:data].first['value']
value ? (value * 100).round(2) : 0
else
nil # nil signals the summary class to not even try to serialize the result
end
......
......@@ -33,7 +33,9 @@ def deployment_count_via_dora_api
params: dora_aggregate_metrics_params
).execute_without_authorization
result[:status] == :success ? (result[:data] || 0) : 0
return 0 unless result[:status] == :success
result[:data].first['value'] || 0
end
def dora_aggregate_metrics_params
......@@ -42,7 +44,7 @@ def dora_aggregate_metrics_params
end_date: (options[:to] || Date.today).to_date,
interval: 'all',
environment_tiers: %w[production],
metric: 'deployment_frequency'
metrics: ['deployment_frequency']
}
params[:group_project_ids] = options[:projects] if options[:projects].present?
......
......@@ -65,7 +65,7 @@ def dora_api_params
interval: dora_interval,
environment_tiers: environment_tiers,
start_date: start_date,
metric: metric
metrics: [metric]
}
# AggregateMetricsService filters out projects outside of the group
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Resolvers::DoraMetricsResolver, time_travel_to: '2021-05-01' do
RSpec.describe Resolvers::DoraMetricsResolver, feature_category: :value_stream_management, time_travel_to: '2021-05-01' do
include GraphqlHelpers
let_it_be(:guest) { create(:user) }
......@@ -13,7 +13,7 @@
let_it_be(:staging) { create(:environment, :staging, project: project) }
let(:current_user) { reporter }
let(:args) { { metric: 'deployment_frequency' } }
let(:args) { { metrics: ['deployment_frequency'] } }
before_all do
group.add_guest(guest)
......@@ -59,109 +59,109 @@
end
end
context 'with metric: "deployment_frequency"' do
let(:args) { { metric: 'deployment_frequency' } }
context 'with metrics: "deployment_frequency"' do
let(:args) { { metrics: ['deployment_frequency'] } }
it 'returns metrics from production for the last 3 months from the production environment, grouped by day' do
expect(resolve_metrics).to eq(
[
{ 'date' => '2021-03-01', 'value' => 18 },
{ 'date' => '2021-04-01', 'value' => 17 },
{ 'date' => '2021-04-02', 'value' => 16 },
{ 'date' => '2021-04-03', 'value' => 15 },
{ 'date' => '2021-04-04', 'value' => 14 },
{ 'date' => '2021-04-05', 'value' => 13 },
{ 'date' => '2021-04-06', 'value' => 12 },
{ 'date' => '2021-04-07', 'value' => nil }
{ 'date' => '2021-03-01', 'value' => 18, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-01', 'value' => 17, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-02', 'value' => 16, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-03', 'value' => 15, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-04', 'value' => 14, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-05', 'value' => 13, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-06', 'value' => 12, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-07', 'value' => nil, 'metric_key' => 'deployment_frequency' }
])
end
end
context 'with interval: "daily"' do
let(:args) { { metric: 'deployment_frequency', interval: 'daily' } }
let(:args) { { metrics: ['deployment_frequency'], interval: 'daily' } }
it 'returns the metrics grouped by day (the default)' do
expect(resolve_metrics).to eq(
[
{ 'date' => '2021-03-01', 'value' => 18 },
{ 'date' => '2021-04-01', 'value' => 17 },
{ 'date' => '2021-04-02', 'value' => 16 },
{ 'date' => '2021-04-03', 'value' => 15 },
{ 'date' => '2021-04-04', 'value' => 14 },
{ 'date' => '2021-04-05', 'value' => 13 },
{ 'date' => '2021-04-06', 'value' => 12 },
{ 'date' => '2021-04-07', 'value' => nil }
{ 'date' => '2021-03-01', 'value' => 18, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-01', 'value' => 17, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-02', 'value' => 16, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-03', 'value' => 15, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-04', 'value' => 14, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-05', 'value' => 13, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-06', 'value' => 12, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-07', 'value' => nil, 'metric_key' => 'deployment_frequency' }
])
end
end
context 'with interval: "monthly"' do
let(:args) { { metric: 'deployment_frequency', interval: 'monthly' } }
let(:args) { { metrics: ['deployment_frequency'], interval: 'monthly' } }
it 'returns the metrics grouped by month' do
expect(resolve_metrics).to eq(
[
{ 'date' => '2021-03-01', 'value' => 18 },
{ 'date' => '2021-04-01', 'value' => 87 }
{ 'date' => '2021-03-01', 'value' => 18, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-01', 'value' => 87, 'metric_key' => 'deployment_frequency' }
])
end
end
context 'with interval: "all"' do
let(:args) { { metric: 'deployment_frequency', interval: 'all' } }
let(:args) { { metrics: ['deployment_frequency'], interval: 'all' } }
it 'returns the metrics grouped into a single bucket with a nil date' do
expect(resolve_metrics).to eq(
[
{ 'date' => nil, 'value' => 105 }
{ 'date' => nil, 'value' => 105, 'metric_key' => 'deployment_frequency' }
])
end
end
context 'with a start_date' do
let(:args) { { metric: 'deployment_frequency', start_date: '2021-04-03'.to_datetime } }
let(:args) { { metrics: ['deployment_frequency'], start_date: '2021-04-03'.to_datetime } }
it 'returns metrics for data on or after the provided date' do
expect(resolve_metrics).to eq(
[
{ 'date' => '2021-04-03', 'value' => 15 },
{ 'date' => '2021-04-04', 'value' => 14 },
{ 'date' => '2021-04-05', 'value' => 13 },
{ 'date' => '2021-04-06', 'value' => 12 },
{ 'date' => '2021-04-07', 'value' => nil }
{ 'date' => '2021-04-03', 'value' => 15, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-04', 'value' => 14, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-05', 'value' => 13, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-06', 'value' => 12, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-07', 'value' => nil, 'metric_key' => 'deployment_frequency' }
])
end
end
context 'with an end_date' do
let(:args) { { metric: 'deployment_frequency', end_date: '2021-04-03'.to_datetime } }
let(:args) { { metrics: ['deployment_frequency'], end_date: '2021-04-03'.to_datetime } }
it 'returns metrics for data on or before the provided date' do
expect(resolve_metrics).to eq(
[
{ 'date' => '2021-03-01', 'value' => 18 },
{ 'date' => '2021-04-01', 'value' => 17 },
{ 'date' => '2021-04-02', 'value' => 16 },
{ 'date' => '2021-04-03', 'value' => 15 }
{ 'date' => '2021-03-01', 'value' => 18, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-01', 'value' => 17, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-02', 'value' => 16, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-03', 'value' => 15, 'metric_key' => 'deployment_frequency' }
])
end
end
context 'with both a start_date and an end_date' do
let(:args) { { metric: 'deployment_frequency', start_date: '2021-04-01'.to_datetime, end_date: '2021-04-03'.to_datetime } }
let(:args) { { metrics: ['deployment_frequency'], start_date: '2021-04-01'.to_datetime, end_date: '2021-04-03'.to_datetime } }
it 'returns metrics between the provided dates (inclusive)' do
expect(resolve_metrics).to eq(
[
{ 'date' => '2021-04-01', 'value' => 17 },
{ 'date' => '2021-04-02', 'value' => 16 },
{ 'date' => '2021-04-03', 'value' => 15 }
{ 'date' => '2021-04-01', 'value' => 17, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-02', 'value' => 16, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-03', 'value' => 15, 'metric_key' => 'deployment_frequency' }
])
end
end
context 'when the requested date range is too large' do
let(:args) { { metric: 'deployment_frequency', start_date: '2020-01-01'.to_datetime, end_date: '2021-05-01'.to_datetime } }
let(:args) { { metrics: ['deployment_frequency'], start_date: '2020-01-01'.to_datetime, end_date: '2021-05-01'.to_datetime } }
it 'generates an error' do
expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, 'Date range must be shorter than 180 days.') do
......@@ -171,7 +171,7 @@
end
context 'when the start date equal to or later than the end date' do
let(:args) { { metric: 'deployment_frequency', start_date: '2021-04-01'.to_datetime, end_date: '2021-03-01'.to_datetime } }
let(:args) { { metrics: ['deployment_frequency'], start_date: '2021-04-01'.to_datetime, end_date: '2021-03-01'.to_datetime } }
it 'generates an error' do
expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, 'The start date must be earlier than the end date.') do
......@@ -188,63 +188,89 @@
end
end
context 'with metric: "lead_time_for_changes"' do
let(:args) { { metric: 'lead_time_for_changes' } }
context 'with metrics: "lead_time_for_changes"' do
let(:args) { { metrics: ['lead_time_for_changes'] } }
it 'returns lead time metrics' do
expect(resolve_metrics).to eq(
[
{ 'date' => '2021-03-01', 'value' => nil },
{ 'date' => '2021-04-01', 'value' => 99.0 },
{ 'date' => '2021-04-02', 'value' => 98.0 },
{ 'date' => '2021-04-03', 'value' => 97.0 },
{ 'date' => '2021-04-04', 'value' => nil },
{ 'date' => '2021-04-05', 'value' => nil },
{ 'date' => '2021-04-06', 'value' => nil },
{ 'date' => '2021-04-07', 'value' => nil }
{ 'date' => '2021-03-01', 'value' => nil, 'metric_key' => 'lead_time_for_changes' },
{ 'date' => '2021-04-01', 'value' => 99.0, 'metric_key' => 'lead_time_for_changes' },
{ 'date' => '2021-04-02', 'value' => 98.0, 'metric_key' => 'lead_time_for_changes' },
{ 'date' => '2021-04-03', 'value' => 97.0, 'metric_key' => 'lead_time_for_changes' },
{ 'date' => '2021-04-04', 'value' => nil, 'metric_key' => 'lead_time_for_changes' },
{ 'date' => '2021-04-05', 'value' => nil, 'metric_key' => 'lead_time_for_changes' },
{ 'date' => '2021-04-06', 'value' => nil, 'metric_key' => 'lead_time_for_changes' },
{ 'date' => '2021-04-07', 'value' => nil, 'metric_key' => 'lead_time_for_changes' }
])
end
# Testing this combination of arguments explicitly since it previously
# caused a bug: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/65653
context 'with interval: "all"' do
let(:args) { { metric: 'lead_time_for_changes', interval: 'all' } }
let(:args) { { metrics: ['lead_time_for_changes'], interval: 'all' } }
it 'returns the metrics grouped into a single bucket with a nil date' do
expect(resolve_metrics).to eq(
[
{ 'date' => nil, 'value' => 98.0 }
{ 'date' => nil, 'value' => 98.0, 'metric_key' => 'lead_time_for_changes' }
])
end
end
end
context 'with multiple environment_tiers' do
let(:args) { { metric: 'deployment_frequency', environment_tiers: %w[production staging] } }
let(:args) { { metrics: ['deployment_frequency'], environment_tiers: %w[production staging] } }
it 'returns metrics for all environments combined' do
expect(resolve_metrics).to eq(
[
{ 'date' => '2021-03-01', 'value' => 18 },
{ 'date' => '2021-04-01', 'value' => 27 },
{ 'date' => '2021-04-02', 'value' => 16 },
{ 'date' => '2021-04-03', 'value' => 15 },
{ 'date' => '2021-04-04', 'value' => 14 },
{ 'date' => '2021-04-05', 'value' => 13 },
{ 'date' => '2021-04-06', 'value' => 12 },
{ 'date' => '2021-04-07', 'value' => nil }
{ 'date' => '2021-03-01', 'value' => 18, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-01', 'value' => 27, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-02', 'value' => 16, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-03', 'value' => 15, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-04', 'value' => 14, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-05', 'value' => 13, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-06', 'value' => 12, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-07', 'value' => nil, 'metric_key' => 'deployment_frequency' }
])
end
end
context 'backwards compatibility for environment_tier' do
let(:args) { { metric: 'deployment_frequency', environment_tier: 'staging' } }
let(:args) { { metrics: ['deployment_frequency'], environment_tier: 'staging' } }
it 'returns metrics for the staging environment' do
expect(resolve_metrics).to eq(
[
{ 'date' => '2021-04-01', 'value' => 10 },
{ 'date' => '2021-04-02', 'value' => nil }
{ 'date' => '2021-04-01', 'value' => 10, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-02', 'value' => nil, 'metric_key' => 'deployment_frequency' }
])
end
end
context 'with multiple metrics' do
let(:args) { { metrics: %w[deployment_frequency lead_time_for_changes], interval: 'monthly' } }
it 'returns metrics for all metric keys' do
expect(resolve_metrics).to eq(
[
{ 'date' => '2021-03-01', 'value' => 18, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-03-01', 'value' => nil, 'metric_key' => 'lead_time_for_changes' },
{ 'date' => '2021-04-01', 'value' => 87, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-01', 'value' => 98, 'metric_key' => 'lead_time_for_changes' }
])
end
end
context 'backwards compatibility for metric attribute' do
let(:args) { { metric: 'deployment_frequency', environment_tiers: ['staging'] } }
it 'returns metrics for the staging environment' do
expect(resolve_metrics).to eq(
[
{ 'date' => '2021-04-01', 'value' => 10, 'metric_key' => 'deployment_frequency' },
{ 'date' => '2021-04-02', 'value' => nil, 'metric_key' => 'deployment_frequency' }
])
end
end
......
......@@ -2,9 +2,9 @@
require 'spec_helper'
RSpec.describe Types::DoraMetricType do
RSpec.describe Types::DoraMetricType, feature_category: :value_stream_management do
it 'has the expected fields' do
expect(described_class).to have_graphql_fields(:date, :value)
expect(described_class).to have_graphql_fields(:date, :value, :metric_key)
end
describe 'date field' do
......@@ -18,4 +18,10 @@
it { is_expected.to have_graphql_type(GraphQL::Types::Float) }
end
describe 'metricKey field' do
subject { described_class.fields['metricKey'] }
it { is_expected.to have_non_null_graphql_type(Types::DoraMetricTypeEnum) }
end
end
......@@ -2,7 +2,8 @@
require 'spec_helper'
RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::BaseDoraSummary do
RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::BaseDoraSummary,
feature_category: :value_stream_management do
describe '#metric_key' do
it 'is required to be overloaded' do
expect do
......
......@@ -2,7 +2,8 @@
require 'spec_helper'
RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::ChangeFailureRate do
RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::ChangeFailureRate,
feature_category: :value_stream_management do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:stage) { build(:cycle_analytics_stage, project: project) }
......@@ -30,7 +31,7 @@
context 'when the DORA service returns 0 as value' do
it 'returns "0" value' do
expect_next_instance_of(Dora::AggregateMetricsService) do |service|
expect(service).to receive(:execute).and_return({ status: :success, data: 0 })
expect(service).to receive(:execute).and_return({ status: :success, data: [{ 'value' => 0 }] })
end
expect(result.to_s).to eq('0')
......@@ -40,7 +41,7 @@
context 'when the DORA service returns the value' do
it 'returns the value in days' do
expect_next_instance_of(Dora::AggregateMetricsService) do |service|
expect(service).to receive(:execute).and_return({ status: :success, data: 0.85 })
expect(service).to receive(:execute).and_return({ status: :success, data: [{ 'value' => 0.85 }] })
end
expect(result.to_s).to eq('85.0')
......
......@@ -2,7 +2,8 @@
require 'spec_helper'
RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::LeadTimeForChanges do
RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::LeadTimeForChanges,
feature_category: :value_stream_management do
let(:stage) { build(:cycle_analytics_stage) }
let(:user) { build(:user) }
......@@ -45,7 +46,7 @@
context 'when the DORA service returns 0 as the lead time for changes' do
it 'returns "none" value' do
expect_next_instance_of(Dora::AggregateMetricsService) do |service|
expect(service).to receive(:execute).and_return({ status: :success, data: 0 })
expect(service).to receive(:execute).and_return({ status: :success, data: [{ 'value' => 0 }] })
end
expect(result.to_s).to eq('-')
......@@ -55,7 +56,7 @@
context 'when the DORA service returns the lead time for changes as seconds' do
it 'returns the value in days' do
expect_next_instance_of(Dora::AggregateMetricsService) do |service|
expect(service).to receive(:execute).and_return({ status: :success, data: 5.days.to_i })
expect(service).to receive(:execute).and_return({ status: :success, data: [{ 'value' => 5.days.to_i }] })
end
expect(result.to_s).to eq('5.0')
......
......@@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::LeadTime do
RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::LeadTime, feature_category: :value_stream_management do
let(:stage) { build(:cycle_analytics_stage) }
let(:user) { build(:user) }
......
......@@ -2,7 +2,8 @@
require 'spec_helper'
RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::TimeToRestoreService do
RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::TimeToRestoreService,
feature_category: :value_stream_management do
let(:stage) { build(:cycle_analytics_stage) }
let(:user) { build(:user) }
......@@ -28,7 +29,7 @@
context 'when the DORA service returns 0 as value' do
it 'returns "none" value' do
expect_next_instance_of(Dora::AggregateMetricsService) do |service|
expect(service).to receive(:execute).and_return({ status: :success, data: 0 })
expect(service).to receive(:execute).and_return({ status: :success, data: [{ 'value' => 0 }] })
end
expect(result.to_s).to eq('-')
......@@ -38,7 +39,7 @@
context 'when the DORA service returns the value' do
it 'returns the value in days' do
expect_next_instance_of(Dora::AggregateMetricsService) do |service|
expect(service).to receive(:execute).and_return({ status: :success, data: 5.days.to_i })
expect(service).to receive(:execute).and_return({ status: :success, data: [{ 'value' => 5.days.to_i }] })
end
expect(result.to_s).to eq('5.0')
......
......@@ -140,7 +140,7 @@
let(:interval) { described_class::INTERVAL_ALL }
it 'aggregates the rows' do
is_expected.to eq({ 'date' => nil, metric => 6 })
is_expected.to eq([{ 'date' => nil, metric => 6 }])
end
end
......@@ -186,7 +186,7 @@
let(:interval) { described_class::INTERVAL_ALL }
it 'aggregates the rows' do
is_expected.to eq({ 'date' => nil, metric => 0.5 })
is_expected.to eq([{ 'date' => nil, metric => 0.5 }])
end
end
......@@ -235,7 +235,7 @@
let(:interval) { described_class::INTERVAL_ALL }
it 'calculates the median' do
is_expected.to eq({ 'date' => nil, metric => 70 })
is_expected.to eq([{ 'date' => nil, metric => 70 }])
end
end
......@@ -295,7 +295,7 @@
let(:interval) { described_class::INTERVAL_ALL }
it 'aggregates the rows' do
is_expected.to eq({ 'date' => nil, 'deployment_frequency' => 6, 'change_failure_rate' => 4.0 / 6 })
is_expected.to eq([{ 'date' => nil, 'deployment_frequency' => 6, 'change_failure_rate' => 4.0 / 6 }])
end
end
......
......@@ -50,10 +50,10 @@
end
where(:metric, :value1, :value2) do
:deployment_frequency | 1 | 2
:lead_time_for_changes | 3 | 4
:time_to_restore_service | 5 | 6
:change_failure_rate | 7 | 4
'deployment_frequency' | 1 | 2
'lead_time_for_changes' | 3 | 4
'time_to_restore_service' | 5 | 6
'change_failure_rate' | 7 | 4
end
with_them do
......@@ -63,31 +63,44 @@
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to match_array([{ 'date' => '2021-01-01', 'value' => value1 },
{ 'date' => '2021-01-02', 'value' => value2 }])
expect(json_response).to match_array([{ 'date' => '2021-01-01', 'value' => value1, 'metric_key' => metric },
{ 'date' => '2021-01-02', 'value' => value2, 'metric_key' => metric }])
end
end
context 'with multiple metrics' do
let(:params) { { metric: 'deployment_frequency', environment_tiers: %w[production staging] } }
context 'with interval all' do
let(:params) { { metric: 'deployment_frequency', interval: 'all' } }
it 'returns simple number' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq(3)
end
end
context 'with multiple environments' do
let(:metric) { 'deployment_frequency' }
let(:params) { { metric: metric, environment_tiers: %w[production staging] } }
it 'returns combined data' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to match_array([{ 'date' => '2021-01-01', 'value' => 1 },
{ 'date' => '2021-01-02', 'value' => 102 }])
expect(json_response).to match_array([{ 'date' => '2021-01-01', 'value' => 1, 'metric_key' => metric },
{ 'date' => '2021-01-02', 'value' => 102, 'metric_key' => metric }])
end
end
context 'backwards compatibility for environment_tier' do
let(:params) { { metric: 'deployment_frequency', environment_tier: 'staging' } }
let(:metric) { 'deployment_frequency' }
let(:params) { { metric: metric, environment_tier: 'staging' } }
it 'returns combined data' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to match_array([{ 'date' => '2021-01-02', 'value' => 100 }])
expect(json_response).to match_array([{ 'date' => '2021-01-02', 'value' => 100, 'metric_key' => metric }])
end
end
......
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать