Commit cd5b3e33 authored by Jan Provaznik's avatar Jan Provaznik

Merge branch '324687-expose-limited-record-count-in-vsa' into 'master'

Expose stage counts in VSA

See merge request gitlab-org/gitlab!59642
parents 6bf05c3a a9aafa5f
...@@ -9,7 +9,8 @@ module Groups ...@@ -9,7 +9,8 @@ module Groups
before_action :load_group before_action :load_group
before_action :load_value_stream before_action :load_value_stream
before_action :validate_params, only: %i[median average records duration_chart average_duration_chart] before_action :validate_params, only: %i[median average records duration_chart average_duration_chart count]
before_action :authorize_read_group_stage, only: %i[median average records duration_chart average_duration_chart count]
def index def index
return render_403 unless can?(current_user, :read_group_cycle_analytics, @group) return render_403 unless can?(current_user, :read_group_cycle_analytics, @group)
...@@ -42,20 +43,14 @@ module Groups ...@@ -42,20 +43,14 @@ module Groups
end end
def median def median
return render_403 unless can?(current_user, :read_group_stage, @group)
render json: { value: data_collector.median.seconds } render json: { value: data_collector.median.seconds }
end end
def average def average
return render_403 unless can?(current_user, :read_group_stage, @group)
render json: { value: data_collector.average.seconds } render json: { value: data_collector.average.seconds }
end end
def records def records
return render_403 unless can?(current_user, :read_group_stage, @group)
serialized_records = data_collector.serialized_records do |relation| serialized_records = data_collector.serialized_records do |relation|
add_pagination_headers(relation) add_pagination_headers(relation)
end end
...@@ -64,17 +59,17 @@ module Groups ...@@ -64,17 +59,17 @@ module Groups
end end
def duration_chart def duration_chart
return render_403 unless can?(current_user, :read_group_stage, @group)
render json: ::Analytics::CycleAnalytics::DurationChartItemEntity.represent(data_collector.duration_chart_data) render json: ::Analytics::CycleAnalytics::DurationChartItemEntity.represent(data_collector.duration_chart_data)
end end
def average_duration_chart def average_duration_chart
return render_403 unless can?(current_user, :read_group_stage, @group)
render json: ::Analytics::CycleAnalytics::DurationChartAverageItemEntity.represent(data_collector.duration_chart_average_data) render json: ::Analytics::CycleAnalytics::DurationChartAverageItemEntity.represent(data_collector.duration_chart_average_data)
end end
def count
render json: { count: data_collector.count }
end
private private
def data_collector def data_collector
...@@ -156,6 +151,10 @@ module Groups ...@@ -156,6 +151,10 @@ module Groups
params: permitted_cycle_analytics_params params: permitted_cycle_analytics_params
).execute(exclude_total_headers: true, data_without_counts: true) ).execute(exclude_total_headers: true, data_without_counts: true)
end end
def authorize_read_group_stage
return render_403 unless can?(current_user, :delete_group_stage, @group)
end
end end
end end
end end
......
...@@ -32,6 +32,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do ...@@ -32,6 +32,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
get :median get :median
get :average get :average
get :records get :records
get :count
end end
end end
resources :value_streams, only: [:index, :create, :update, :destroy] do resources :value_streams, only: [:index, :create, :update, :destroy] do
...@@ -42,6 +43,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do ...@@ -42,6 +43,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
get :median get :median
get :average get :average
get :records get :records
get :count
end end
end end
end end
......
...@@ -110,6 +110,12 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::DataCollector do ...@@ -110,6 +110,12 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::DataCollector do
}) })
end end
end end
describe '#count' do
subject(:count) { data_collector.count }
it { is_expected.to eq(3) }
end
end end
shared_examples 'test various start and end event combinations' do shared_examples 'test various start and end event combinations' do
...@@ -692,4 +698,52 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::DataCollector do ...@@ -692,4 +698,52 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::DataCollector do
end end
end end
end end
describe 'limit count' do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, group: group) }
let(:merge_request) { merge_requests.first }
let(:stage) do
Analytics::CycleAnalytics::GroupStage.new(
name: 'My Stage',
group: group,
start_event_identifier: :merge_request_created,
end_event_identifier: :merge_request_merged
)
end
before do
merge_requests = create_list(:merge_request, 3, :unique_branches, target_project: project, source_project: project)
merge_requests.each { |mr| mr.metrics.update!(merged_at: 10.days.from_now) }
project.add_user(user, Gitlab::Access::DEVELOPER)
end
subject(:count) do
described_class.new(stage: stage, params: {
from: 5.months.ago,
to: 5.months.from_now,
current_user: user
}).count
end
context 'when limit is reached' do
before do
stub_const('Gitlab::Analytics::CycleAnalytics::DataCollector::MAX_COUNT', 2)
end
it 'shows the MAX COUNT' do
is_expected.to eq(2)
end
end
context 'when limit is not reached' do
it 'shows the actual count' do
is_expected.to eq(3)
end
end
end
end end
...@@ -280,6 +280,20 @@ RSpec.shared_examples 'Value Stream Analytics Stages controller' do ...@@ -280,6 +280,20 @@ RSpec.shared_examples 'Value Stream Analytics Stages controller' do
include_examples 'Value Stream Analytics data endpoint examples' include_examples 'Value Stream Analytics data endpoint examples'
include_examples 'group permission check on the controller level' include_examples 'group permission check on the controller level'
end end
describe 'GET #count' do
subject { get :count, params: params }
it 'matches the response schema' do
subject
expect(response).to be_successful
expect(json_response['count']).to eq(0)
end
include_examples 'Value Stream Analytics data endpoint examples'
include_examples 'group permission check on the controller level'
end
end end
end end
......
...@@ -12,6 +12,8 @@ module Gitlab ...@@ -12,6 +12,8 @@ module Gitlab
class DataCollector class DataCollector
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
MAX_COUNT = 1001
delegate :serialized_records, to: :records_fetcher delegate :serialized_records, to: :records_fetcher
def initialize(stage:, params: {}) def initialize(stage:, params: {})
...@@ -37,6 +39,12 @@ module Gitlab ...@@ -37,6 +39,12 @@ module Gitlab
end end
end end
def count
strong_memoize(:count) do
limit_count
end
end
private private
attr_reader :stage, :params attr_reader :stage, :params
...@@ -44,6 +52,13 @@ module Gitlab ...@@ -44,6 +52,13 @@ module Gitlab
def query def query
BaseQueryBuilder.new(stage: stage, params: params).build BaseQueryBuilder.new(stage: stage, params: params).build
end end
# Limiting the maximum number of records so the COUNT(*) query stays efficient for large groups.
# COUNT = 1001, show 1000+ on the UI
# COUNT < 1001, show the actual number on the UI
def limit_count
query.limit(MAX_COUNT).count
end
end end
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