Commit 91f36e73 authored by Adam Hegyi's avatar Adam Hegyi Committed by Mayra Cabrera

Use finders in VSA

This change alters the base query in VSA to use the standard Issuable
finders.
parent 936c99b2
......@@ -10,33 +10,25 @@ module EE::Gitlab::Analytics::CycleAnalytics::BaseQueryBuilder
private
# rubocop: disable CodeReuse/ActiveRecord
def filter_by_project_ids(query)
project_ids = Array(params[:project_ids])
query = query.where(project_id: project_ids) if project_ids.any?
query
override :build_finder_params
def build_finder_params(params)
super.tap do |finder_params|
finder_params[:project_ids] = Array(params[:project_ids])
end
end
# rubocop: enable CodeReuse/ActiveRecord
override :filter_by_parent_model
# rubocop: disable CodeReuse/ActiveRecord
def filter_by_parent_model(query)
override :add_parent_model_params!
def add_parent_model_params!(finder_params)
return super unless parent_class.eql?(Group)
if subject_class.eql?(Issue)
join_groups(query.joins(:project))
elsif subject_class.eql?(MergeRequest)
join_groups(query.joins(:target_project))
else
raise ArgumentError, "unknown subject_class: #{subject_class}"
end
finder_params.merge!(group_id: stage.parent_id, include_subgroups: true)
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def join_groups(query)
query.joins(Arel.sql("INNER JOIN (#{stage.parent.self_and_descendants.to_sql}) namespaces ON namespaces.id=projects.namespace_id"))
def filter_by_project_ids(query)
return query if params[:project_ids].empty?
query.where(project_id: params[:project_ids])
end
# rubocop: enable CodeReuse/ActiveRecord
end
......@@ -2,9 +2,4 @@
module EE::Gitlab::Analytics::CycleAnalytics::RecordsFetcher
extend ::Gitlab::Utils::Override
override :finder_params
def finder_params
super.merge({ ::Group => { group_id: stage.parent_id } })
end
end
......@@ -43,7 +43,8 @@ module Gitlab
params: {
from: @options[:from],
to: @options[:to] || DateTime.now,
project_ids: @options[:projects]
project_ids: @options[:projects],
current_user: @current_user
}
)
end
......
......@@ -8,6 +8,12 @@ describe Gitlab::Analytics::CycleAnalytics::BaseQueryBuilder do
let_it_be(:project_in_group) { create(:project, :repository, group: group) }
let_it_be(:project_in_subgroup) { create(:project, :repository, group: subgroup) }
let_it_be(:project_outside_group) { create(:project, :repository, group: create(:group)) }
let_it_be(:user) { create(:user) }
before do
group.add_maintainer(user)
project_outside_group.add_maintainer(user)
end
context 'when the subject is `Issue`' do
let(:issue_in_project) { create(:issue, project: project_in_group, created_at: 5.days.ago) }
......@@ -27,7 +33,7 @@ describe Gitlab::Analytics::CycleAnalytics::BaseQueryBuilder do
group: group
})
result = described_class.new(stage: stage).build
result = described_class.new(stage: stage, params: { current_user: user }).build
expect(result).to contain_exactly(issue_in_project, issue_in_subgroup_project)
end
......@@ -51,7 +57,7 @@ describe Gitlab::Analytics::CycleAnalytics::BaseQueryBuilder do
group: group
})
result = described_class.new(stage: stage).build
result = described_class.new(stage: stage, params: { current_user: user }).build
expect(result).to contain_exactly(mr_in_project, mr_in_subgroup_project)
end
......
......@@ -8,21 +8,24 @@ module Gitlab
delegate :subject_class, to: :stage
# rubocop: disable CodeReuse/ActiveRecord
FINDER_CLASSES = {
MergeRequest.to_s => MergeRequestsFinder,
Issue.to_s => IssuesFinder
}.freeze
def initialize(stage:, params: {})
@stage = stage
@params = params
@params = build_finder_params(params)
end
# rubocop: disable CodeReuse/ActiveRecord
def build
query = subject_class
query = filter_by_parent_model(query)
query = filter_by_time_range(query)
query = finder.execute
query = stage.start_event.apply_query_customization(query)
query = stage.end_event.apply_query_customization(query)
query.where(duration_condition)
end
# rubocop: enable CodeReuse/ActiveRecord
private
......@@ -32,38 +35,33 @@ module Gitlab
stage.end_event.timestamp_projection.gteq(stage.start_event.timestamp_projection)
end
def filter_by_parent_model(query)
if parent_class.eql?(Project)
if subject_class.eql?(Issue)
query.where(project_id: stage.parent_id)
elsif subject_class.eql?(MergeRequest)
query.where(target_project_id: stage.parent_id)
else
raise ArgumentError, "unknown subject_class: #{subject_class}"
end
else
raise ArgumentError, "unknown parent_class: #{parent_class}"
end
def finder
FINDER_CLASSES.fetch(subject_class.to_s).new(params[:current_user], params)
end
def filter_by_time_range(query)
from = params.fetch(:from, 30.days.ago)
to = params[:to]
query = query.where(subject_table[:created_at].gteq(from))
query = query.where(subject_table[:created_at].lteq(to)) if to
query
def parent_class
stage.parent.class
end
def subject_table
subject_class.arel_table
def build_finder_params(params)
{}.tap do |finder_params|
finder_params[:current_user] = params[:current_user]
add_parent_model_params!(finder_params)
add_time_range_params!(finder_params, params[:from], params[:to])
end
end
def parent_class
stage.parent.class
def add_parent_model_params!(finder_params)
raise(ArgumentError, "unknown parent_class: #{parent_class}") unless parent_class.eql?(Project)
finder_params[:project_id] = stage.parent_id
end
# rubocop: enable CodeReuse/ActiveRecord
def add_time_range_params!(finder_params, from, to)
finder_params[:created_after] = from || 30.days.ago
finder_params[:created_before] = to if to
end
end
end
end
......
......@@ -11,12 +11,14 @@ module Gitlab
@query = query
end
# rubocop: disable CodeReuse/ActiveRecord
def seconds
@query = @query.select(median_duration_in_seconds.as('median'))
@query = @query.select(median_duration_in_seconds.as('median')).reorder(nil)
result = execute_query(@query).first || {}
result['median'] || nil
end
# rubocop: enable CodeReuse/ActiveRecord
def days
seconds ? seconds.fdiv(1.day) : nil
......
......@@ -12,13 +12,11 @@ module Gitlab
MAPPINGS = {
Issue => {
finder_class: IssuesFinder,
serializer_class: AnalyticsIssueSerializer,
includes_for_query: { project: [:namespace], author: [] },
columns_for_select: %I[title iid id created_at author_id project_id]
},
MergeRequest => {
finder_class: MergeRequestsFinder,
serializer_class: AnalyticsMergeRequestSerializer,
includes_for_query: { target_project: [:namespace], author: [] },
columns_for_select: %I[title iid id created_at author_id state_id target_project_id]
......@@ -56,27 +54,12 @@ module Gitlab
attr_reader :stage, :query, :params
def finder_query
MAPPINGS
.fetch(subject_class)
.fetch(:finder_class)
.new(params.fetch(:current_user), finder_params.fetch(stage.parent.class))
.execute
end
def columns
MAPPINGS.fetch(subject_class).fetch(:columns_for_select).map do |column_name|
subject_class.arel_table[column_name]
end
end
# EE will override this to include Group rules
def finder_params
{
Project => { project_id: stage.parent_id }
}
end
def default_test_stage?
stage.matches_with_stage_params?(Gitlab::Analytics::CycleAnalytics::DefaultStages.params_for_test_stage)
end
......@@ -113,8 +96,7 @@ module Gitlab
end
def records
results = finder_query
.merge(ordered_and_limited_query)
results = ordered_and_limited_query
.select(*columns, round_duration_to_seconds.as('total_time'))
# using preloader instead of includes to avoid AR generating a large column list
......
......@@ -6,7 +6,8 @@ describe Gitlab::Analytics::CycleAnalytics::BaseQueryBuilder do
let_it_be(:project) { create(:project, :empty_repo) }
let_it_be(:mr1) { create(:merge_request, target_project: project, source_project: project, allow_broken: true, created_at: 3.months.ago) }
let_it_be(:mr2) { create(:merge_request, target_project: project, source_project: project, allow_broken: true, created_at: 1.month.ago) }
let(:params) { {} }
let_it_be(:user) { create(:user) }
let(:params) { { current_user: user } }
let(:records) do
stage = build(:cycle_analytics_project_stage, {
start_event_identifier: :merge_request_created,
......@@ -17,6 +18,7 @@ describe Gitlab::Analytics::CycleAnalytics::BaseQueryBuilder do
end
before do
project.add_maintainer(user)
mr1.metrics.update!(merged_at: 1.month.ago)
mr2.metrics.update!(merged_at: Time.now)
end
......
......@@ -23,7 +23,7 @@ describe Gitlab::Analytics::CycleAnalytics::RecordsFetcher do
describe '#serialized_records' do
shared_context 'when records are loaded by maintainer' do
before do
project.add_user(user, Gitlab::Access::MAINTAINER)
project.add_user(user, Gitlab::Access::DEVELOPER)
end
it 'returns all records' do
......@@ -103,6 +103,8 @@ describe Gitlab::Analytics::CycleAnalytics::RecordsFetcher do
latest_build_finished_at: 7.days.ago,
pipeline: ci_build2.pipeline
})
project.add_user(user, Gitlab::Access::MAINTAINER)
end
context 'returns build records' do
......
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