Commit c5a87715 authored by Dylan Griffith's avatar Dylan Griffith

Merge branch 'remove-legacy-group-level-dora-metrics' into 'master'

Remove Legacy Group-Level DORA metrics API [RUN ALL RSPEC] [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!59858
parents 18d9d2d1 1c9804e3
......@@ -39,10 +39,12 @@ class DeploymentsFinder
private
def init_collection
if params[:project]
if params[:project].present?
params[:project].deployments
elsif params[:group].present?
::Deployment.for_projects(params[:group].all_projects)
else
Deployment.none
::Deployment.none
end
end
......@@ -113,5 +115,3 @@ class DeploymentsFinder
end
# rubocop: enable CodeReuse/ActiveRecord
end
DeploymentsFinder.prepend_if_ee('EE::DeploymentsFinder')
---
title: Remove Legacy Group-Level DORA metrics API
merge_request: 59858
author:
type: removed
---
stage: Release
group: Release
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
type: reference, api
redirect_to: 'dora/metrics.md'
---
# DORA4 Analytics Group API **(ULTIMATE SELF)**
This document was moved to [another location](dora/metrics.md).
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291747) in GitLab 13.9.
> - [Deployed behind a feature flag](../user/feature_flags.md), disabled by default.
> - Disabled on GitLab.com.
> - Not recommended for production use.
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-dora4-analytics-group-api).
WARNING:
These endpoints are deprecated and will be removed in GitLab 14.0. Use the [DORA metrics API](dora/metrics.md) instead.
WARNING:
This feature might not be available to you. Check the **version history** note above for details.
All methods require reporter authorization.
## List group deployment frequencies
Get a list of all group deployment frequencies:
```plaintext
GET /groups/:id/analytics/deployment_frequency?environment=:environment&from=:from&to=:to&interval=:interval
```
Attributes:
| Attribute | Type | Required | Description |
|--------------|--------|----------|-----------------------|
| `id` | string | yes | The ID of the group. |
Parameters:
| Parameter | Type | Required | Description |
|--------------|--------|----------|-----------------------|
| `environment`| string | yes | The name of the environment to filter by. |
| `from` | string | yes | Datetime range to start from. Inclusive, ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`). |
| `to` | string | no | Datetime range to end at. Exclusive, ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`). |
| `interval` | string | no | The bucketing interval (`all`, `monthly`, `daily`). |
Example request:
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/analytics/deployment_frequency?environment=:environment&from=:from&to=:to&interval=:interval"
```
Example response:
```json
[
{
"from": "2017-01-01",
"to": "2017-01-02",
"value": 106
},
{
"from": "2017-01-02",
"to": "2017-01-03",
"value": 55
}
]
```
## Enable or disable DORA4 Analytics Group API **(ULTIMATE SELF)**
DORA4 Analytics Group API is under development and not ready for production use. It is
deployed behind a feature flag that is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../administration/feature_flags.md)
can enable it.
To enable it:
```ruby
Feature.enable(:dora4_group_deployment_frequency_api)
```
To disable it:
```ruby
Feature.disable(:dora4_group_deployment_frequency_api)
```
<!-- This redirect file can be deleted after <2021-07-25>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
......@@ -645,9 +645,6 @@ The group's new subgroups have push rules set for them based on either:
and issues) of group members. **(PREMIUM)**
- [Issue analytics](issues_analytics/index.md): View a bar chart of your group's number of issues per month. **(PREMIUM)**
- Use GitLab as a [dependency proxy](../packages/dependency_proxy/index.md) for upstream Docker images.
- [DORA4 Project Analytics API](../../api/dora4_group_analytics.md): View deployment frequency analytics.
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291747) in GitLab Ultimate 13.9 as a
[Beta feature](https://about.gitlab.com/handbook/product/gitlab-the-product/#beta). **(ULTIMATE SELF)**
- [Epics](epics/index.md): Track groups of issues that share a theme. **(ULTIMATE)**
- [Security Dashboard](../application_security/security_dashboard/index.md): View the vulnerabilities of all
the projects in a group and its subgroups. **(ULTIMATE)**
......
# frozen_string_literal: true
# WARNING: This finder does not check permissions!
#
# Arguments:
# params:
# group: Group model - Find deployments within a group (including subgroups)
#
# Note: If project and group is given at the same time, the project will have precedence.
# If project or group is missing, the finder will return empty resultset.
module EE
module DeploymentsFinder
private
def init_collection
if params[:project].present?
super
elsif params[:group].present?
::Deployment.for_project(::Project.in_namespace(params[:group].self_and_descendants))
else
::Deployment.none
end
end
end
end
# frozen_string_literal: true
module Analytics
module Deployments
module Frequency
# This class is to aggregate deployments data at project-level or group-level
# for calculating the frequency.
class AggregateService < BaseContainerService
include Gitlab::Utils::StrongMemoize
QUARTER_DAYS = 3.months / 1.day
INTERVAL_ALL = 'all'
INTERVAL_MONTHLY = 'monthly'
INTERVAL_DAILY = 'daily'
VALID_INTERVALS = [
INTERVAL_ALL,
INTERVAL_MONTHLY,
INTERVAL_DAILY
].freeze
def execute
if error = validate
return error
end
frequencies = deployments_grouped.map do |grouped_start_date, grouped_deploys|
{
value: grouped_deploys.count,
from: grouped_start_date,
to: deployments_grouped_end_date(grouped_start_date)
}
end
success(frequencies: frequencies)
end
private
def validate
unless start_date
return error(_("Parameter `from` must be specified"), :bad_request)
end
if start_date > end_date
return error(_("Parameter `to` is before the `from` date"), :bad_request)
end
if days_between > QUARTER_DAYS
return error(_("Date range is greater than %{quarter_days} days") % { quarter_days: QUARTER_DAYS },
:bad_request)
end
unless VALID_INTERVALS.include?(interval)
return error(_("Parameter `interval` must be one of (\"%{valid_intervals}\")") % { valid_intervals: VALID_INTERVALS.join('", "') }, :bad_request)
end
unless can?(current_user, :read_dora4_analytics, container)
error(_("You do not have permission to access deployment frequencies"), :forbidden)
end
end
def interval
params[:interval] || INTERVAL_ALL
end
def start_date
params[:from]
end
def end_date
strong_memoize(:end_date) do
params[:to] || DateTime.current
end
end
def days_between
(end_date - start_date).to_i
end
def deployments_grouped
case interval
when INTERVAL_ALL
{ start_date => deployments }
when INTERVAL_MONTHLY
deployments.group_by { |d| d.finished_at.beginning_of_month }
when INTERVAL_DAILY
deployments.group_by { |d| d.finished_at.to_date }
end
end
def deployments_grouped_end_date(deployments_grouped_start_date)
case interval
when INTERVAL_ALL
end_date
when INTERVAL_MONTHLY
deployments_grouped_start_date + 1.month
when INTERVAL_DAILY
deployments_grouped_start_date + 1.day
end
end
def container_params
if container.is_a?(Project)
{ project: container }
elsif container.is_a?(Group)
{ group: container }
else
{}
end
end
def deployments
::DeploymentsFinder.new(
**container_params,
environment: params[:environment],
status: :success,
finished_before: end_date,
finished_after: start_date,
order_by: :finished_at,
sort: :asc
).execute
end
end
end
end
end
---
name: dora4_group_deployment_frequency_api
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51938
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/300239
milestone: '13.9'
type: development
group: group::release
default_enabled: false
# frozen_string_literal: true
module API
module Analytics
class GroupDeploymentFrequency < ::API::Base
feature_category :continuous_delivery
before do
authenticate!
not_found! unless ::Feature.enabled?(:dora4_group_deployment_frequency_api, user_group)
end
params do
requires :id, type: String, desc: 'The ID of the group'
end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
namespace ':id/analytics' do
desc 'List deployment frequencies for the group'
params do
requires :environment, type: String, desc: 'The name of the environment to filter by'
requires :from, type: DateTime, desc: 'Datetime range to start from. Inclusive, ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`)'
optional :to, type: DateTime, desc: 'Datetime range to end at. Exclusive, ISO 8601 format (`YYYY-MM-DDTHH:MM:SSZ`)'
optional :interval, type: String, desc: 'The bucketing interval (`all`, `monthly`, `daily`)'
end
get 'deployment_frequency' do
result = ::Analytics::Deployments::Frequency::AggregateService
.new(container: user_group,
current_user: current_user,
params: declared_params(include_missing: false))
.execute
unless result[:status] == :success
render_api_error!(result[:message], result[:http_status])
end
present result[:frequencies], with: EE::API::Entities::Analytics::DeploymentFrequency
end
end
end
end
end
end
......@@ -45,7 +45,6 @@ module EE
mount ::API::VisualReviewDiscussions
mount ::API::Analytics::CodeReviewAnalytics
mount ::API::Analytics::GroupActivityAnalytics
mount ::API::Analytics::GroupDeploymentFrequency
mount ::API::Analytics::ProjectDeploymentFrequency
mount ::API::ProtectedEnvironments
mount ::API::ResourceWeightEvents
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe DeploymentsFinder do
subject { described_class.new(params).execute }
context 'at group scope' do
let_it_be(:group) { create(:group) }
let_it_be(:subgroup) { create(:group, parent: group) }
let(:group_project_1) { create(:project, :public, :test_repo, group: group) }
let(:group_project_2) { create(:project, :public, :test_repo, group: group) }
let(:subgroup_project_1) { create(:project, :public, :test_repo, group: subgroup) }
let(:base_params) { { group: group } }
describe 'ordering' do
using RSpec::Parameterized::TableSyntax
let(:params) { { **base_params, order_by: order_by, sort: sort } }
let!(:group_project_1_deployment) { create(:deployment, :success, project: group_project_1, iid: 11, ref: 'master', created_at: 2.days.ago, updated_at: Time.now, finished_at: Time.now) }
let!(:group_project_2_deployment) { create(:deployment, :success, project: group_project_2, iid: 12, ref: 'feature', created_at: 1.day.ago, updated_at: 2.hours.ago, finished_at: 2.hours.ago) }
let!(:subgroup_project_1_deployment) { create(:deployment, :success, project: subgroup_project_1, iid: 8, ref: 'video', created_at: Time.now, updated_at: 1.hour.ago, finished_at: 1.hour.ago) }
where(:order_by, :sort) do
'created_at' | 'asc'
'created_at' | 'desc'
'id' | 'asc'
'id' | 'desc'
'iid' | 'asc'
'iid' | 'desc'
'ref' | 'asc'
'ref' | 'desc'
'updated_at' | 'asc'
'updated_at' | 'desc'
'finished_at' | 'asc'
'finished_at' | 'desc'
'invalid' | 'asc'
'iid' | 'err'
end
with_them do
it 'returns the deployments unordered' do
expect(subject.to_a).to contain_exactly(group_project_1_deployment,
group_project_2_deployment,
subgroup_project_1_deployment)
end
end
end
it 'avoids N+1 queries' do
execute_queries = -> { described_class.new({ group: group }).execute.first }
control_count = ActiveRecord::QueryRecorder.new { execute_queries }.count
new_project = create(:project, :repository, group: group)
new_env = create(:environment, project: new_project, name: "production")
create_list(:deployment, 2, status: :success, project: new_project, environment: new_env)
group.reload
expect { execute_queries }.not_to exceed_query_limit(control_count)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe API::Analytics::GroupDeploymentFrequency do
let_it_be(:group) { create(:group, :private) }
let_it_be(:project1) { create(:project, :repository, namespace: group) }
let_it_be(:project2) { create(:project, :repository, namespace: group) }
let_it_be(:prod_env_name) { "prod" }
let_it_be(:prod1) { create(:environment, project: project1, name: prod_env_name) }
let_it_be(:dev1) { create(:environment, project: project1, name: "dev") }
let_it_be(:prod2) { create(:environment, project: project2, name: prod_env_name) }
let_it_be(:dev2) { create(:environment, project: project2, name: "dev") }
let_it_be(:anonymous_user) { create(:user) }
let_it_be(:reporter) { create(:user).tap { |u| group.add_reporter(u) } }
def make_deployment(finished_at, env, proj)
create(:deployment,
status: :success,
project: proj,
environment: env,
finished_at: finished_at)
end
let_it_be(:deployment_2020_01_01) { make_deployment(DateTime.new(2020, 1, 1), prod1, project1) }
let_it_be(:deployment_2020_01_02) { make_deployment(DateTime.new(2020, 1, 2), prod2, project2) }
let_it_be(:deployment_2020_01_03) { make_deployment(DateTime.new(2020, 1, 3), dev1, project1) }
let_it_be(:deployment_2020_01_04) { make_deployment(DateTime.new(2020, 1, 4), prod2, project2) }
let_it_be(:deployment_2020_01_05) { make_deployment(DateTime.new(2020, 1, 5), prod1, project1) }
let_it_be(:deployment_2020_02_01) { make_deployment(DateTime.new(2020, 2, 1), prod2, project2) }
let_it_be(:deployment_2020_02_02) { make_deployment(DateTime.new(2020, 2, 2), prod1, project1) }
let_it_be(:deployment_2020_02_03) { make_deployment(DateTime.new(2020, 2, 3), dev2, project2) }
let_it_be(:deployment_2020_02_04) { make_deployment(DateTime.new(2020, 2, 4), prod1, project1) }
let_it_be(:deployment_2020_02_05) { make_deployment(DateTime.new(2020, 2, 5), prod2, project2) }
let_it_be(:deployment_2020_03_01) { make_deployment(DateTime.new(2020, 3, 1), prod1, project1) }
let_it_be(:deployment_2020_03_02) { make_deployment(DateTime.new(2020, 3, 2), prod2, project2) }
let_it_be(:deployment_2020_03_03) { make_deployment(DateTime.new(2020, 3, 3), dev1, project1) }
let_it_be(:deployment_2020_03_04) { make_deployment(DateTime.new(2020, 3, 4), prod2, project2) }
let_it_be(:deployment_2020_03_05) { make_deployment(DateTime.new(2020, 3, 5), prod1, project1) }
let_it_be(:deployment_2020_04_01) { make_deployment(DateTime.new(2020, 4, 1), prod2, project2) }
let_it_be(:deployment_2020_04_02) { make_deployment(DateTime.new(2020, 4, 2), prod1, project1) }
let_it_be(:deployment_2020_04_03) { make_deployment(DateTime.new(2020, 4, 3), dev2, project2) }
let_it_be(:deployment_2020_04_04) { make_deployment(DateTime.new(2020, 4, 4), prod1, project1) }
let_it_be(:deployment_2020_04_05) { make_deployment(DateTime.new(2020, 4, 5), prod2, project2) }
let(:dora4_analytics_enabled) { true }
let(:api_feature_flag_enabled) { true }
let(:current_user) { reporter }
let(:params) { { from: 1.day.ago, to: Time.now, interval: "all", environment: prod_env_name } }
let(:path) { api("/groups/#{group.id}/analytics/deployment_frequency", current_user) }
let(:request) { get path, params: params }
let(:request_time) { nil }
before do
stub_licensed_features(dora4_analytics: dora4_analytics_enabled)
stub_feature_flags(dora4_group_deployment_frequency_api: api_feature_flag_enabled)
if request_time
travel_to(request_time) { request }
else
request
end
end
context 'when user has access to the group' do
it 'returns `ok`' do
expect(response).to have_gitlab_http_status(:ok)
end
end
context 'with params: from 2017 to 2019' do
let(:params) { { environment: prod_env_name, from: DateTime.new(2017), to: DateTime.new(2019) } }
it 'returns `bad_request` with expected message' do
expect(response.parsed_body).to eq({
"message" => "Date range is greater than 91 days"
})
end
end
context 'with params: from 2019 to 2017' do
let(:params) do
{ environment: prod_env_name, from: DateTime.new(2019), to: DateTime.new(2017) }
end
it 'returns `bad_request` with expected message' do
expect(response.parsed_body).to eq({
"message" => "Parameter `to` is before the `from` date"
})
end
end
context 'with params: from 2020/04/02 to request time' do
let(:request_time) { DateTime.new(2020, 4, 4) }
let(:params) { { environment: prod_env_name, from: DateTime.new(2020, 4, 2) } }
it 'returns the expected deployment frequencies' do
expect(response.parsed_body).to eq([{
"from" => "2020-04-02",
"to" => "2020-04-04",
"value" => 1
}])
end
end
context 'with params: from 2020/02/01 to 2020/04/01 by all' do
let(:params) do
{
environment: prod_env_name,
from: DateTime.new(2020, 2, 1),
to: DateTime.new(2020, 4, 1),
interval: "all"
}
end
it 'returns the expected deployment frequencies' do
expect(response.parsed_body).to eq([{
"from" => "2020-02-01",
"to" => "2020-04-01",
"value" => 8
}])
end
end
context 'with params: from 2020/02/01 to 2020/04/01 by month' do
let(:params) do
{
environment: prod_env_name,
from: DateTime.new(2020, 2, 1),
to: DateTime.new(2020, 4, 1),
interval: "monthly"
}
end
it 'returns the expected deployment frequencies' do
expect(response.parsed_body).to eq([
{ "from" => "2020-02-01", "to" => "2020-03-01", "value" => 4 },
{ "from" => "2020-03-01", "to" => "2020-04-01", "value" => 4 }
])
end
end
context 'with params: from 2020/02/01 to 2020/04/01 by day' do
let(:params) do
{
environment: prod_env_name,
from: DateTime.new(2020, 2, 1),
to: DateTime.new(2020, 4, 1),
interval: "daily"
}
end
it 'returns the expected deployment frequencies' do
expect(response.parsed_body).to eq([
{ "from" => "2020-02-01", "to" => "2020-02-02", "value" => 1 },
{ "from" => "2020-02-02", "to" => "2020-02-03", "value" => 1 },
{ "from" => "2020-02-04", "to" => "2020-02-05", "value" => 1 },
{ "from" => "2020-02-05", "to" => "2020-02-06", "value" => 1 },
{ "from" => "2020-03-01", "to" => "2020-03-02", "value" => 1 },
{ "from" => "2020-03-02", "to" => "2020-03-03", "value" => 1 },
{ "from" => "2020-03-04", "to" => "2020-03-05", "value" => 1 },
{ "from" => "2020-03-05", "to" => "2020-03-06", "value" => 1 }
])
end
end
context 'with params: invalid interval' do
let(:params) do
{
environment: prod_env_name,
from: DateTime.new(2020, 1),
to: DateTime.new(2020, 2),
interval: "invalid"
}
end
it 'returns `bad_request`' do
expect(response).to have_gitlab_http_status(:bad_request)
end
end
context 'with params: missing from' do
let(:params) { { environment: prod_env_name, to: DateTime.new(2019), interval: "all" } }
it 'returns `bad_request`' do
expect(response).to have_gitlab_http_status(:bad_request)
end
end
context 'when user does not have access to the group' do
let(:current_user) { anonymous_user }
it 'returns `not_found`' do
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'when feature is not available in plan' do
let(:dora4_analytics_enabled) { false }
context 'when user has access to the group' do
it 'returns `forbidden`' do
expect(response).to have_gitlab_http_status(:forbidden)
end
end
context 'when user does not have access to the group' do
let(:current_user) { anonymous_user }
it 'returns `not_found`' do
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
context 'when feature flag dora4_group_deployment_frequency_api is disabled' do
let(:api_feature_flag_enabled) { false }
context 'when user has access to the group' do
it 'returns `not_found`' do
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Analytics::Deployments::Frequency::AggregateService do
let_it_be(:group) { create(:group) }
let_it_be(:subgroup) { create(:group, parent: group) }
let_it_be(:group_project, refind: true) { create(:project, :repository, group: group) }
let_it_be(:subgroup_project, refind: true) { create(:project, :repository, group: subgroup) }
let_it_be(:developer) { create(:user) }
let_it_be(:guest) { create(:user) }
let(:container) { group_project }
let(:actor) { developer }
let(:service) { described_class.new(container: container, current_user: actor, params: params) }
let(:params) { { from: 4.days.ago.to_datetime } }
before_all do
group.add_developer(developer)
group.add_guest(guest)
end
before do
stub_licensed_features(dora4_analytics: true)
end
around do |example|
freeze_time { example.run }
end
describe '#execute' do
subject { service.execute }
let_it_be(:group_production) { create(:environment, project: group_project, name: 'production') }
let_it_be(:group_staging) { create(:environment, project: group_project, name: 'staging') }
let_it_be(:subgroup_production) { create(:environment, project: subgroup_project, name: 'production') }
let_it_be(:subgroup_staging) { create(:environment, project: subgroup_project, name: 'staging') }
before_all do
create(:deployment, :success, project: group_project, environment: group_production, finished_at: 7.days.ago)
create(:deployment, :failed, project: group_project, environment: group_production, finished_at: 3.days.ago)
create(:deployment, :success, project: group_project, environment: group_production, finished_at: 1.day.ago)
create(:deployment, :success, project: group_project, environment: group_staging, finished_at: 1.day.ago)
create(:deployment, :success, project: subgroup_project, environment: subgroup_production, finished_at: 7.days.ago)
create(:deployment, :failed, project: subgroup_project, environment: subgroup_production, finished_at: 3.days.ago)
create(:deployment, :success, project: subgroup_project, environment: subgroup_production, finished_at: 1.day.ago)
create(:deployment, :success, project: subgroup_project, environment: subgroup_staging, finished_at: 1.day.ago)
end
shared_examples_for 'validation error' do
it 'returns an error with message' do
result = subject
expect(result[:status]).to eq(:error)
expect(result[:message]).to eq(message)
end
end
it 'returns deployment frequencies' do
result = subject
expect(result[:status]).to eq(:success)
expect(result[:frequencies]).to eq(
[
{
from: params[:from],
to: DateTime.current,
value: 2
}
]
)
end
context 'when date range is specified' do
let(:params) { { from: 10.days.ago.to_datetime, to: 5.days.from_now.to_datetime } }
it 'returns deployment frequencies' do
result = subject
expect(result[:status]).to eq(:success)
expect(result[:frequencies]).to eq(
[
{
from: params[:from],
to: params[:to],
value: 3
}
]
)
end
end
context 'when environment name is specified' do
let(:params) { { from: 3.days.ago.to_datetime, environment: 'production' } }
it 'returns frequencies that related to production environment' do
result = subject
expect(result[:status]).to eq(:success)
expect(result[:frequencies]).to eq(
[
{
from: params[:from],
to: DateTime.current,
value: 1
}
]
)
end
end
context 'when the container is group' do
let(:container) { group }
it 'returns frequencies that related to production environment' do
result = subject
expect(result[:status]).to eq(:success)
expect(result[:frequencies]).to eq(
[
{
from: params[:from],
to: DateTime.current,
value: 4
}
]
)
end
end
context 'when parameter is empty' do
let(:params) { {} }
it_behaves_like 'validation error' do
let(:message) { 'Parameter `from` must be specified' }
end
end
context 'when start_date is eariler than end_date' do
let(:params) { { from: 3.days.ago.to_datetime, to: 4.days.ago.to_datetime } }
it_behaves_like 'validation error' do
let(:message) { 'Parameter `to` is before the `from` date' }
end
end
context 'when the date range is too broad' do
let(:params) { { from: 1.year.ago.to_datetime } }
it_behaves_like 'validation error' do
let(:message) { 'Date range is greater than 91 days' }
end
end
context 'when the interval is not supported' do
let(:params) { { from: 3.days.ago.to_datetime, interval: 'unknown' } }
it_behaves_like 'validation error' do
let(:message) { 'Parameter `interval` must be one of ("all", "monthly", "daily")' }
end
end
context 'when the actor does not have permission to read DORA4 metrics' do
let(:actor) { guest }
it_behaves_like 'validation error' do
let(:message) { 'You do not have permission to access deployment frequencies' }
end
end
context 'when license is insufficient' do
before do
stub_licensed_features(dora4_analytics: false)
end
it_behaves_like 'validation error' do
let(:message) { 'You do not have permission to access deployment frequencies' }
end
end
end
end
......@@ -10354,9 +10354,6 @@ msgstr ""
msgid "Date range cannot exceed %{maxDateRange} days."
msgstr ""
msgid "Date range is greater than %{quarter_days} days"
msgstr ""
msgid "Date range must be shorter than %{max_range} days."
msgstr ""
......@@ -23257,15 +23254,6 @@ msgstr ""
msgid "Parameter \"job_id\" cannot exceed length of %{job_id_max_size}"
msgstr ""
msgid "Parameter `from` must be specified"
msgstr ""
msgid "Parameter `interval` must be one of (\"%{valid_intervals}\")"
msgstr ""
msgid "Parameter `to` is before the `from` date"
msgstr ""
msgid "Parent"
msgstr ""
......@@ -36566,9 +36554,6 @@ msgstr ""
msgid "You do not have any subscriptions yet"
msgstr ""
msgid "You do not have permission to access deployment frequencies"
msgstr ""
msgid "You do not have permission to access dora metrics."
msgstr ""
......
......@@ -160,5 +160,62 @@ RSpec.describe DeploymentsFinder do
end
end
end
context 'at group scope' do
let_it_be(:group) { create(:group) }
let_it_be(:subgroup) { create(:group, parent: group) }
let_it_be(:group_project_1) { create(:project, :public, :test_repo, group: group) }
let_it_be(:group_project_2) { create(:project, :public, :test_repo, group: group) }
let_it_be(:subgroup_project_1) { create(:project, :public, :test_repo, group: subgroup) }
let(:base_params) { { group: group } }
describe 'ordering' do
using RSpec::Parameterized::TableSyntax
let(:params) { { **base_params, order_by: order_by, sort: sort } }
let!(:group_project_1_deployment) { create(:deployment, :success, project: group_project_1, iid: 11, ref: 'master', created_at: 2.days.ago, updated_at: Time.now, finished_at: Time.now) }
let!(:group_project_2_deployment) { create(:deployment, :success, project: group_project_2, iid: 12, ref: 'feature', created_at: 1.day.ago, updated_at: 2.hours.ago, finished_at: 2.hours.ago) }
let!(:subgroup_project_1_deployment) { create(:deployment, :success, project: subgroup_project_1, iid: 8, ref: 'video', created_at: Time.now, updated_at: 1.hour.ago, finished_at: 1.hour.ago) }
where(:order_by, :sort) do
'created_at' | 'asc'
'created_at' | 'desc'
'id' | 'asc'
'id' | 'desc'
'iid' | 'asc'
'iid' | 'desc'
'ref' | 'asc'
'ref' | 'desc'
'updated_at' | 'asc'
'updated_at' | 'desc'
'finished_at' | 'asc'
'finished_at' | 'desc'
'invalid' | 'asc'
'iid' | 'err'
end
with_them do
it 'returns the deployments unordered' do
expect(subject.to_a).to contain_exactly(group_project_1_deployment,
group_project_2_deployment,
subgroup_project_1_deployment)
end
end
end
it 'avoids N+1 queries' do
execute_queries = -> { described_class.new({ group: group }).execute.first }
control_count = ActiveRecord::QueryRecorder.new { execute_queries }.count
new_project = create(:project, :repository, group: group)
new_env = create(:environment, project: new_project, name: "production")
create_list(:deployment, 2, status: :success, project: new_project, environment: new_env)
group.reload
expect { execute_queries }.not_to exceed_query_limit(control_count)
end
end
end
end
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