Commit ea526797 authored by Thong Kuah's avatar Thong Kuah

Merge branch '321005-use-new-deployment-frequency-api' into 'master'

Use aggregated DORA API in VSA [RUN ALL RSPEC] [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!60367
parents 1a993332 58f27851
......@@ -35,6 +35,10 @@ module Dora
return error(_('Container must be a project or a group.'), :bad_request)
end
if group_project_ids.present? && !group?
return error(_('The group_project_ids parameter is only allowed for a group'), :bad_request)
end
unless ::Dora::DailyMetrics::AVAILABLE_INTERVALS.include?(interval)
return error(_("The interval must be one of %{intervals}.") % { intervals: ::Dora::DailyMetrics::AVAILABLE_INTERVALS.join(',') },
:bad_request)
......@@ -72,7 +76,9 @@ module Dora
# - In the subsequent projects, the assigned role at the group-level
# can't be lowered. For example, if the user is reporter at group-level,
# the user can be developer in subsequent projects, but can't be guest.
container.all_projects
projects = container.all_projects
projects = projects.id_in(group_project_ids) if group_project_ids.any?
projects
end
end
......@@ -103,5 +109,9 @@ module Dora
def metric
params[:metric]
end
def group_project_ids
Array(params[:group_project_ids])
end
end
end
---
name: dora_deployment_frequency_in_vsa
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60367
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/329178
milestone: '13.12'
type: development
group: group::optimize
default_enabled: false
......@@ -18,18 +18,48 @@ module Gitlab
private
# rubocop: disable CodeReuse/ActiveRecord
def deployments_count
@deployments_count ||= begin
deployments = DeploymentsFinder
.new(group: group, finished_after: options[:from], finished_before: options[:to], status: :success)
.execute
deployments = deployments.where(project_id: options[:projects]) if options[:projects].present?
deployments.count
end
@deployments_count ||= if Feature.enabled?(:dora_deployment_frequency_in_vsa)
deployment_count_via_dora_api
else
deployment_count_via_finder
end
end
# rubocop: disable CodeReuse/ActiveRecord
def deployment_count_via_finder
deployments = DeploymentsFinder
.new(group: group, finished_after: options[:from], finished_before: options[:to], status: :success)
.execute
deployments = deployments.where(project_id: options[:projects]) if options[:projects].present?
deployments.count
end
# rubocop: enable CodeReuse/ActiveRecord
def deployment_count_via_dora_api
result = Dora::AggregateMetricsService.new(
container: group,
current_user: options[:current_user],
params: dora_aggregate_metrics_params
).execute
result[:status] == :success ? (result[:data] || 0) : 0
end
def dora_aggregate_metrics_params
params = {
start_date: options[:from].to_date,
end_date: (options[:to] || Date.today).to_date,
interval: 'all',
environment_tier: 'production',
metric: 'deployment_frequency'
}
params[:group_project_ids] = options[:projects] if options[:projects].present?
params
end
end
end
end
......
......@@ -127,116 +127,164 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::Summary::Group::StageSummary d
end
end
describe "#deploys" do
context 'with from date' do
before do
travel_to(5.days.ago) { create(:deployment, :success, project: project, finished_at: Time.zone.now) }
travel_to(5.days.from_now) { create(:deployment, :success, project: project, finished_at: Time.zone.now) }
travel_to(5.days.ago) { create(:deployment, :success, project: project_2, finished_at: Time.zone.now) }
travel_to(5.days.from_now) { create(:deployment, :success, project: project_2, finished_at: Time.zone.now) }
end
def create_deployment(args)
project = args[:project]
environment = project.environments.production.first || create(:environment, :production, project: project)
create(:deployment, :success, args.merge(environment: environment))
it "finds the number of deploys made created after it" do
expect(subject.second[:value]).to eq('2')
end
# this is needed for the dora_deployment_frequency_in_vsa feature flag so we have aggregated data
::Dora::DailyMetrics::RefreshWorker.new.perform(environment.id, Time.current.to_date.to_s)
end
it 'returns the localized title' do
Gitlab::I18n.with_locale(:ru) do
expect(subject.second[:title]).to eq(n_('Deploy', 'Deploys', 2))
end
end
shared_examples 'VSA deployment related metrics' do
describe "#deploys" do
let(:current_time) { Time.current }
let(:one_day_ago) { current_time - 1.day }
let(:two_days_ago) { current_time - 2.days }
let(:five_days_ago) { current_time - 5.days }
let(:ten_days_ago) { current_time - 10.days }
context 'with from date' do
subject { described_class.new(group, options: { from: two_days_ago, current_user: user }).data }
context 'with subgroups' do
before do
travel_to(5.days.from_now) do
create(:deployment, :success, finished_at: Time.zone.now, project: create(:project, :repository, namespace: create(:group, parent: group)))
stub_licensed_features(dora4_analytics: true)
travel_to(five_days_ago) do
create_deployment(project: project, finished_at: Time.current)
create_deployment(project: project_2, finished_at: Time.current)
end
travel_to(current_time) do
create_deployment(project: project, finished_at: Time.current)
create_deployment(project: project_2, finished_at: Time.current)
end
end
it "finds deploys from them" do
expect(subject.second[:value]).to eq('3')
it "finds the number of deploys made created after it" do
expect(subject.second[:value]).to eq('2')
end
end
context 'with projects specified in options' do
before do
travel_to(5.days.from_now) do
create(:deployment, :success, finished_at: Time.zone.now, project: create(:project, :repository, namespace: group, name: 'not_applicable'))
it 'returns the localized title' do
Gitlab::I18n.with_locale(:ru) do
expect(subject.second[:title]).to eq(n_('Deploy', 'Deploys', 2))
end
end
subject { described_class.new(group, options: { from: Time.now, current_user: user, projects: [project.id, project_2.id] }).data }
context 'with subgroups' do
before do
travel_to(current_time) do
create_deployment(project: project, finished_at: Time.current)
end
end
it 'shows deploys from those projects' do
expect(subject.second[:value]).to eq('2')
it "finds deploys from them" do
expect(subject.second[:value]).to eq('3')
end
end
end
context 'when `from` and `to` parameters are provided' do
subject { described_class.new(group, options: { from: 10.days.ago, to: Time.now, current_user: user }).data }
context 'with projects specified in options' do
before do
travel_to(Date.today) do
create_deployment(finished_at: current_time, project: create(:project, :repository, namespace: group, name: 'not_applicable'))
end
end
it 'finds deployments from 5 days ago' do
expect(subject.second[:value]).to eq('2')
end
end
end
subject { described_class.new(group, options: { from: one_day_ago, current_user: user, projects: [project.id, project_2.id] }).data }
context 'with other projects' do
before do
travel_to(5.days.from_now) do
create(:deployment, :success, finished_at: Time.zone.now, project: create(:project, :repository, namespace: create(:group)))
it 'shows deploys from those projects' do
expect(subject.second[:value]).to eq('2')
end
end
end
it "doesn't find deploys from them" do
expect(subject.second[:value]).to eq('-')
end
end
describe '#deployment_frequency' do
let(:from) { 6.days.ago }
let(:to) { nil }
context 'when `from` and `to` parameters are provided' do
subject { described_class.new(group, options: { from: ten_days_ago, to: one_day_ago, current_user: user }).data }
subject do
described_class.new(group, options: {
from: from,
to: to,
current_user: user
}).data.third
it 'finds deployments from 5 days ago' do
expect(subject.second[:value]).to eq('2')
end
end
end
it 'includes the unit: `per day`' do
expect(subject[:unit]).to eq(_('per day'))
end
context 'with other projects' do
before do
travel_to(one_day_ago) do
create_deployment(finished_at: Time.current, project: create(:project, :repository, namespace: create(:group)))
end
end
before do
travel_to(5.days.ago) do
create(:deployment, :success, finished_at: Time.zone.now, project: project)
it "doesn't find deploys from them" do
expect(subject.second[:value]).to eq('-')
end
end
context 'when `to` is nil' do
it 'includes range until now' do
# 1 deployment over 7 days
expect(subject[:value]).to eq('0.1')
describe '#deployment_frequency' do
let(:from) { ten_days_ago }
let(:to) { nil }
subject do
described_class.new(group, options: {
from: from,
to: to,
current_user: user
}).data.third
end
end
context 'when `to` is given' do
let(:from) { 10.days.ago }
let(:to) { 10.days.from_now }
it 'includes the unit: `per day`' do
expect(subject[:unit]).to eq(_('per day'))
end
before do
travel_to(5.days.from_now) do
create(:deployment, :success, finished_at: Time.zone.now, project: project)
stub_licensed_features(dora4_analytics: true)
travel_to(five_days_ago) do
create_deployment(finished_at: Time.current, project: project)
end
end
context 'when `to` is nil' do
it 'includes range until now' do
# 1 deployment over 7 days
expect(subject[:value]).to eq('0.1')
end
end
it 'returns deployment frequency within `from` and `to` range' do
# 2 deployments over 20 days
expect(subject[:value]).to eq('0.1')
context 'when `to` is given' do
let(:from) { ten_days_ago }
let(:to) { 10.days.from_now }
before do
travel_to(Date.yesterday) do
create_deployment(finished_at: Time.current, project: project)
end
end
it 'returns deployment frequency within `from` and `to` range' do
# 2 deployments over 20 days
expect(subject[:value]).to eq('0.1')
end
end
end
end
end
context 'when dora_deployment_frequency_in_vsa feature flag is enabled' do
before do
stub_feature_flags(dora_deployment_frequency_in_vsa: true)
expect(Dora::AggregateMetricsService).to receive(:new).and_call_original
end
it_behaves_like 'VSA deployment related metrics'
end
context 'when dora_deployment_frequency_in_vsa feature flag is disabled' do
before do
stub_feature_flags(dora_deployment_frequency_in_vsa: false)
expect(Dora::AggregateMetricsService).not_to receive(:new)
end
it_behaves_like 'VSA deployment related metrics'
end
end
......@@ -27,8 +27,13 @@ RSpec.describe Analytics::CycleAnalytics::GroupLevel do
describe '#summary' do
before do
stub_licensed_features(dora4_analytics: true)
create_cycle(user, project, issue, mr, milestone, pipeline)
deploy_master(user, project)
environment = project.environments.production.first
::Dora::DailyMetrics::RefreshWorker.new.perform(environment.id, pipeline.created_at.to_date.to_s)
end
it 'returns medians for each stage for a specific group' do
......
......@@ -144,6 +144,15 @@ RSpec.describe Dora::AggregateMetricsService do
expect(subject[:data]).to eq([{ Time.current.to_date.to_s => 1, 'date' => Time.current.to_date.to_s, 'value' => 1 }])
end
end
context 'when group_project_ids parameter is given' do
let(:extra_params) { { interval: Dora::DailyMetrics::INTERVAL_ALL, group_project_ids: [1] } }
it_behaves_like 'request failure' do
let(:message) { 'The group_project_ids parameter is only allowed for a group' }
let(:http_status) { :bad_request }
end
end
end
context 'when container is a group' do
......@@ -196,6 +205,15 @@ RSpec.describe Dora::AggregateMetricsService do
expect(subject[:data]).to eq(3)
end
end
context 'when group_project_ids parameter is given' do
let(:extra_params) { { interval: Dora::DailyMetrics::INTERVAL_ALL, group_project_ids: [project_2.id] } }
it 'returns the aggregated data' do
expect(subject[:status]).to eq(:success)
expect(subject[:data]).to eq(1)
end
end
end
context 'when container is nil' do
......
......@@ -31868,6 +31868,9 @@ msgstr ""
msgid "The group will be placed in 'pending removal' state"
msgstr ""
msgid "The group_project_ids parameter is only allowed for a group"
msgstr ""
msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
......
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