Skip to content
Snippets Groups Projects
Commit c0a0751e authored by Pavel Shutsin's avatar Pavel Shutsin :two:
Browse files

Add multiple tiers aggregaion to DORA API

New `deployment_tiers` param allows to aggregate
data for multiple tiers data into one number.

Old `deployment_tier` param is deprecated and will be
removed in milestone 16.0

Changelog: added
EE: true
parent 5b7900e5
No related branches found
No related tags found
1 merge request!90711Add multiple tiers aggregaion to DORA API
Showing with 112 additions and 41 deletions
......@@ -21,14 +21,15 @@ Get project-level DORA metrics.
GET /projects/:id/dora/metrics
```
| Attribute | Type | Required | Description |
|-------------- |-------- |----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../index.md#namespaced-path-encoding) can be accessed by the authenticated user. |
| `metric` | string | yes | The metric name: `deployment_frequency`, `lead_time_for_changes` or `time_to_restore_service`.|
| `start_date` | string | no | Date range to start from. ISO 8601 Date format, for example `2021-03-01`. Default is 3 months ago. |
| `end_date` | string | no | Date range to end at. ISO 8601 Date format, for example `2021-03-01`. Default is the current date. |
| `interval` | string | no | The bucketing interval. One of `all`, `monthly` or `daily`. Default is `daily`. |
| `environment_tier` | string | no | The [tier of the environment](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. |
| Attribute | Type | Required | Description |
|----------------------|------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../index.md#namespaced-path-encoding) can be accessed by the authenticated user. |
| `metric` | string | yes | The metric name: `deployment_frequency`, `lead_time_for_changes` or `time_to_restore_service`. |
| `start_date` | string | no | Date range to start from. ISO 8601 Date format, for example `2021-03-01`. Default is 3 months ago. |
| `end_date` | string | no | Date range to end at. ISO 8601 Date format, for example `2021-03-01`. Default is the current date. |
| `interval` | string | no | The bucketing interval. One of `all`, `monthly` or `daily`. Default is `daily`. |
| `environment_tier` | string | no | The [tier of the environment](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. Planned for deprecation, please use `environment_tiers`. |
| `environment_tiers` | array of strings | no | The [tiers of the environments](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. |
Example request:
......@@ -61,14 +62,15 @@ Get group-level DORA metrics.
GET /groups/:id/dora/metrics
```
| Attribute | Type | Required | Description |
|-------------- |-------- |----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../index.md#namespaced-path-encoding) can be accessed by the authenticated user. |
| `metric` | string | yes | One of `deployment_frequency`, `lead_time_for_changes`, `time_to_restore_service` or `change_failure_rate`. |
| `start_date` | string | no | Date range to start from. ISO 8601 Date format, for example `2021-03-01`. Default is 3 months ago. |
| `end_date` | string | no | Date range to end at. ISO 8601 Date format, for example `2021-03-01`. Default is the current date. |
| `interval` | string | no | The bucketing interval. One of `all`, `monthly` or `daily`. Default is `daily`. |
| `environment_tier` | string | no | The [tier of the environment](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. |
| Attribute | Type | Required | Description |
|---------------------|------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../index.md#namespaced-path-encoding) can be accessed by the authenticated user. |
| `metric` | string | yes | One of `deployment_frequency`, `lead_time_for_changes`, `time_to_restore_service` or `change_failure_rate`. |
| `start_date` | string | no | Date range to start from. ISO 8601 Date format, for example `2021-03-01`. Default is 3 months ago. |
| `end_date` | string | no | Date range to end at. ISO 8601 Date format, for example `2021-03-01`. Default is the current date. |
| `interval` | string | no | The bucketing interval. One of `all`, `monthly` or `daily`. Default is `daily`. |
| `environment_tier` | string | no | The [tier of the environment](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. Planned for deprecation, please use `environment_tiers`. |
| `environment_tiers` | array of strings | no | The [tiers of the environments](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. |
Example request:
......
......@@ -10969,7 +10969,8 @@ Returns [`[DoraMetric!]`](#dorametric).
| Name | Type | Description |
| ---- | ---- | ----------- |
| <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. Defaults to `PRODUCTION`. |
| <a id="dorametricsenvironmenttier"></a>`environmentTier` | [`DeploymentTier`](#deploymenttier) | Deployment tier of the environments to return. Planned for deprecation: 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="dorametricsstartdate"></a>`startDate` | [`Date`](#date) | Date range to start from. Default is 3 months ago. |
......@@ -27,9 +27,19 @@ class DoraMetricsResolver < BaseResolver
argument :environment_tier, Types::DeploymentTierEnum,
required: false,
description: 'Deployment tier of the environments to return. Defaults to `PRODUCTION`.'
description: 'Deployment tier of the environments to return. Planned for deprecation: please update to `environment_tiers` param.'
argument :environment_tiers, [Types::DeploymentTierEnum],
required: false,
description: 'Deployment tiers of the environments to return. Defaults to [`PRODUCTION`].'
def resolve(params)
# Backwards compatibility until %16.0
if params[:environment_tier]
params[:environment_tiers] ||= []
params[:environment_tiers] |= [params[:environment_tier]]
end
result = ::Dora::AggregateMetricsService
.new(container: container, current_user: current_user, params: params)
.execute
......
......@@ -4,7 +4,7 @@ module Dora
class AggregateMetricsService < ::BaseContainerService
MAX_RANGE = Gitlab::Analytics::CycleAnalytics::RequestParams::MAX_RANGE_DAYS # same range as Value Stream Analytics
DEFAULT_ENVIRONMENT_TIER = 'production'
DEFAULT_ENVIRONMENT_TIERS = %w[production].freeze
DEFAULT_INTERVAL = Dora::DailyMetrics::INTERVAL_DAILY
def execute
......@@ -76,8 +76,8 @@ def validate
:bad_request)
end
unless Environment.tiers[environment_tier]
return error(_("The environment tier must be one of %{environment_tiers}.") % { environment_tiers: Environment.tiers.keys.join(',') },
unless environment_tiers.all? { |tier| Environment.tiers[tier] }
return error(_("The environment tiers must be from %{environment_tiers}.") % { environment_tiers: Environment.tiers.keys.join(', ') },
:bad_request)
end
......@@ -85,7 +85,7 @@ def validate
end
def environments
Environment.for_project(target_projects).for_tier(environment_tier)
Environment.for_project(target_projects).for_tier(environment_tiers)
end
def target_projects
......@@ -121,8 +121,8 @@ def end_date
params[:end_date] || Time.current.to_date
end
def environment_tier
params[:environment_tier] || DEFAULT_ENVIRONMENT_TIER
def environment_tiers
params[:environment_tiers] || DEFAULT_ENVIRONMENT_TIERS
end
def interval
......
......@@ -12,12 +12,22 @@ class Metrics < ::API::Base
optional :start_date, type: Date, desc: 'Date range to start from.'
optional :end_date, type: Date, desc: 'Date range to end at.'
optional :interval, type: String, desc: "The bucketing interval."
optional :environment_tier, type: String, desc: "The tier of the environment."
optional :environment_tier,
type: String,
desc: "The tier of the environment. Planned for deprecation: please use `environment_tiers` param."
optional :environment_tiers, type: Array[String], desc: "Filter by environment tiers."
end
def fetch!(container)
# Backwards compatibility until %16.0
params = declared_params(include_missing: false)
if params[:environment_tier]
params[:environment_tiers] ||= []
params[:environment_tiers] |= [params[:environment_tier]]
end
result = ::Dora::AggregateMetricsService
.new(container: container, current_user: current_user, params: declared_params(include_missing: false))
.new(container: container, current_user: current_user, params: params)
.execute
if result[:status] == :success
......
......@@ -33,7 +33,7 @@ def dora_aggregate_metrics_params
start_date: options[:from].to_date,
end_date: (options[:to] || Date.today).to_date,
interval: 'all',
environment_tier: 'production',
environment_tiers: %w[production],
metric: 'deployment_frequency'
}
end
......
......@@ -41,7 +41,7 @@ def dora_metric
start_date: from,
end_date: to,
interval: 'all',
environment_tier: 'production',
environment_tiers: %w[production],
metric: metric_key
}
......
......@@ -41,7 +41,7 @@ def dora_aggregate_metrics_params
start_date: options[:from].to_date,
end_date: (options[:to] || Date.today).to_date,
interval: 'all',
environment_tier: 'production',
environment_tiers: %w[production],
metric: 'deployment_frequency'
}
......
......@@ -210,14 +210,31 @@
end
end
context 'with environment_tier: "staging"' do
context 'with multiple environment_tiers' do
let(:args) { { metric: '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 }
])
end
end
context 'backwards compatibility for environment_tier' do
let(:args) { { metric: '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 },
{ 'date' => '2021-04-02', 'value' => nil }
])
end
end
end
......
......@@ -6,6 +6,7 @@
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:production) { create(:environment, :production, project: project) }
let_it_be(:staging) { create(:environment, :staging, project: project) }
let_it_be(:maintainer) { create(:user) }
let_it_be(:guest) { create(:user) }
......@@ -35,6 +36,13 @@
incidents_count: 8,
environment: production,
date: '2021-01-02')
create(:dora_daily_metrics,
deployment_frequency: 100,
lead_time_for_changes_in_seconds: 200,
time_to_restore_service_in_seconds: 300,
incidents_count: 400,
environment: staging,
date: '2021-01-02')
end
before do
......@@ -60,6 +68,29 @@
end
end
context 'with multiple metrics' do
let(:params) { { metric: 'deployment_frequency', 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 }])
end
end
context 'backwards compatibility for environment_tier' do
let(:params) { { metric: 'deployment_frequency', 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 }])
end
end
context 'when user is guest' do
let(:user) { guest }
let(:params) { { metric: :deployment_frequency } }
......
......@@ -68,11 +68,11 @@
end
end
context 'when environment tier is invalid' do
let(:extra_params) { { environment_tier: 'unknown' } }
context 'when environment tiers are invalid' do
let(:extra_params) { { environment_tiers: ['unknown'] } }
it_behaves_like 'request failure' do
let(:message) { "The environment tier must be one of #{Environment.tiers.keys.join(',')}." }
let(:message) { "The environment tiers must be from #{Environment.tiers.keys.join(', ')}." }
let(:http_status) { :bad_request }
end
end
......@@ -136,8 +136,8 @@
end
end
context 'when environment tier is changed' do
let(:extra_params) { { environment_tier: 'staging' } }
context 'when environment tiers are changed' do
let(:extra_params) { { environment_tiers: ['staging'] } }
it 'returns the aggregated data' do
expect(subject[:status]).to eq(:success)
......@@ -232,7 +232,7 @@
context 'runs the service without authorization' do
subject { service.execute_without_authorization }
context 'when passign a non-ultimate group' do
context 'when passing a non-ultimate group' do
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:production) { create(:environment, :production, project: project) }
......@@ -240,7 +240,7 @@
let(:container) { group }
let(:user) { maintainer }
let(:params) { { environment_tier: 'production', interval: 'all', metric: 'deployment_frequency' } }
let(:params) { { environment_tiers: ['production'], interval: 'all', metric: 'deployment_frequency' } }
before do
group.add_maintainer(maintainer)
......
......@@ -38266,7 +38266,7 @@ msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
 
msgid "The environment tier must be one of %{environment_tiers}."
msgid "The environment tiers must be from %{environment_tiers}."
msgstr ""
 
msgid "The errors we encountered were:"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment