Commit a9aafa5f authored by Adam Hegyi's avatar Adam Hegyi

Add limited count to VSA

This exposes the count of issues or MRs per value stream stage. To
avoid counting large amount of DB rows, we use a limit count.
parent 2259e0cb
......@@ -9,7 +9,8 @@ module Groups
before_action :load_group
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
return render_403 unless can?(current_user, :read_group_cycle_analytics, @group)
......@@ -42,20 +43,14 @@ module Groups
end
def median
return render_403 unless can?(current_user, :read_group_stage, @group)
render json: { value: data_collector.median.seconds }
end
def average
return render_403 unless can?(current_user, :read_group_stage, @group)
render json: { value: data_collector.average.seconds }
end
def records
return render_403 unless can?(current_user, :read_group_stage, @group)
serialized_records = data_collector.serialized_records do |relation|
add_pagination_headers(relation)
end
......@@ -64,17 +59,17 @@ module Groups
end
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)
end
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)
end
def count
render json: { count: data_collector.count }
end
private
def data_collector
......@@ -156,6 +151,10 @@ module Groups
params: permitted_cycle_analytics_params
).execute(exclude_total_headers: true, data_without_counts: true)
end
def authorize_read_group_stage
return render_403 unless can?(current_user, :delete_group_stage, @group)
end
end
end
end
......
......@@ -32,6 +32,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
get :median
get :average
get :records
get :count
end
end
resources :value_streams, only: [:index, :create, :update, :destroy] do
......@@ -42,6 +43,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
get :median
get :average
get :records
get :count
end
end
end
......
......@@ -110,6 +110,12 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::DataCollector do
})
end
end
describe '#count' do
subject(:count) { data_collector.count }
it { is_expected.to eq(3) }
end
end
shared_examples 'test various start and end event combinations' do
......@@ -692,4 +698,52 @@ RSpec.describe Gitlab::Analytics::CycleAnalytics::DataCollector do
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
......@@ -280,6 +280,20 @@ RSpec.shared_examples 'Value Stream Analytics Stages controller' do
include_examples 'Value Stream Analytics data endpoint examples'
include_examples 'group permission check on the controller level'
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
......
......@@ -12,6 +12,8 @@ module Gitlab
class DataCollector
include Gitlab::Utils::StrongMemoize
MAX_COUNT = 1001
delegate :serialized_records, to: :records_fetcher
def initialize(stage:, params: {})
......@@ -37,6 +39,12 @@ module Gitlab
end
end
def count
strong_memoize(:count) do
limit_count
end
end
private
attr_reader :stage, :params
......@@ -44,6 +52,13 @@ module Gitlab
def query
BaseQueryBuilder.new(stage: stage, params: params).build
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
......
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