Commit c4399be0 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Merge branch 'improve-preload-of-vuln-api-ee' into 'master'

Fix pagination and preloading of resources

Closes #7970

See merge request gitlab-org/gitlab-ee!7945
parents 9d50560f 98821eb1
...@@ -6,17 +6,13 @@ class Groups::Security::VulnerabilitiesController < Groups::ApplicationControlle ...@@ -6,17 +6,13 @@ class Groups::Security::VulnerabilitiesController < Groups::ApplicationControlle
def index def index
@vulnerabilities = group.all_vulnerabilities.ordered @vulnerabilities = group.all_vulnerabilities.ordered
.page(params[:page]) .page(params[:page])
.per(10)
.to_a
::Gitlab::Vulnerabilities::OccurrencesPreloader.new.preload(@vulnerabilities) # rubocop:disable CodeReuse/ActiveRecord
respond_to do |format| respond_to do |format|
format.json do format.json do
render json: Vulnerabilities::OccurrenceSerializer render json: Vulnerabilities::OccurrenceSerializer
.new(current_user: @current_user) .new(current_user: @current_user)
.with_pagination(request, response) .with_pagination(request, response)
.represent(@vulnerabilities) .represent(@vulnerabilities, preload: true)
end end
end end
end end
......
...@@ -7,6 +7,8 @@ module Vulnerabilities ...@@ -7,6 +7,8 @@ module Vulnerabilities
self.table_name = "vulnerability_occurrences" self.table_name = "vulnerability_occurrences"
paginates_per 10
# Used for both severity and confidence # Used for both severity and confidence
LEVELS = { LEVELS = {
undefined: 0, undefined: 0,
...@@ -62,6 +64,10 @@ module Vulnerabilities ...@@ -62,6 +64,10 @@ module Vulnerabilities
scope :ordered, -> { order("severity desc", :id) } scope :ordered, -> { order("severity desc", :id) }
scope :counted_by_report_and_severity, -> { group(:report_type, :severity).count } scope :counted_by_report_and_severity, -> { group(:report_type, :severity).count }
scope :all_preloaded, -> do
preload(:scanner, :identifiers, :project)
end
def feedback(feedback_type:) def feedback(feedback_type:)
params = { params = {
project_id: project_id, project_id: project_id,
...@@ -75,7 +81,7 @@ module Vulnerabilities ...@@ -75,7 +81,7 @@ module Vulnerabilities
categories = items.group_by { |i| i[:category] } categories = items.group_by { |i| i[:category] }
fingerprints = items.group_by { |i| i[:project_fingerprint] } fingerprints = items.group_by { |i| i[:project_fingerprint] }
VulnerabilityFeedback.where( VulnerabilityFeedback.all_preloaded.where(
project_id: project_ids.keys, project_id: project_ids.keys,
category: categories.keys, category: categories.keys,
project_fingerprint: fingerprints.keys).find_each do |feedback| project_fingerprint: fingerprints.keys).find_each do |feedback|
......
...@@ -18,4 +18,8 @@ class VulnerabilityFeedback < ActiveRecord::Base ...@@ -18,4 +18,8 @@ class VulnerabilityFeedback < ActiveRecord::Base
validates :project_fingerprint, presence: true, uniqueness: { scope: [:project_id, :category, :feedback_type] } validates :project_fingerprint, presence: true, uniqueness: { scope: [:project_id, :category, :feedback_type] }
scope :with_associations, -> { includes(:pipeline, :issue, :author) } scope :with_associations, -> { includes(:pipeline, :issue, :author) }
scope :all_preloaded, -> do
preload(:author, :project, :issue, :pipeline)
end
end end
...@@ -2,4 +2,16 @@ class Vulnerabilities::OccurrenceSerializer < BaseSerializer ...@@ -2,4 +2,16 @@ class Vulnerabilities::OccurrenceSerializer < BaseSerializer
include WithPagination include WithPagination
entity Vulnerabilities::OccurrenceEntity entity Vulnerabilities::OccurrenceEntity
def represent(resource, opts = {})
if paginated?
resource = paginator.paginate(resource)
end
if opts.delete(:preload)
resource = Gitlab::Vulnerabilities::OccurrencesPreloader.preload!(resource)
end
super(resource, opts)
end
end end
...@@ -7,9 +7,11 @@ module Gitlab ...@@ -7,9 +7,11 @@ module Gitlab
# vulnerabilities (occurrences). # vulnerabilities (occurrences).
module Vulnerabilities module Vulnerabilities
class OccurrencesPreloader class OccurrencesPreloader
def preload(occurrences) def self.preload!(occurrences)
occurrences.each(&:issue_feedback) occurrences.all_preloaded.tap do |occurrences|
occurrences.each(&:dismissal_feedback) occurrences.each(&:issue_feedback)
occurrences.each(&:dismissal_feedback)
end
end end
end end
end end
......
...@@ -86,24 +86,37 @@ describe Groups::Security::VulnerabilitiesController do ...@@ -86,24 +86,37 @@ describe Groups::Security::VulnerabilitiesController do
end end
context 'with vulnerability feedback' do context 'with vulnerability feedback' do
it "avoids N+1 queries" do
create_vulnerabilities(2, project_dev)
control_count = ActiveRecord::QueryRecorder.new { get_summary }
create_vulnerabilities(2, project_guest)
expect { get_summary }.not_to exceed_all_query_limit(control_count)
end
private
def get_summary def get_summary
get :index, group_id: group, format: :json get :index, group_id: group, format: :json
end end
it "avoids N+1 queries" do def create_vulnerabilities(count, project)
control_count = ActiveRecord::QueryRecorder.new { get_summary } pipeline = create(:ci_empty_pipeline, project: project)
vulnerabilities = create_list(:vulnerabilities_occurrence, count, project: project)
# Create feedback vulnerabilities.each do |occurrence|
project_dev.vulnerabilities.each do |occ|
create(:vulnerability_feedback, :sast, :dismissal, create(:vulnerability_feedback, :sast, :dismissal,
project: project_dev, project_fingerprint: occ.project_fingerprint) pipeline: pipeline,
project: project_dev,
project_fingerprint: occurrence.project_fingerprint)
create(:vulnerability_feedback, :sast, :issue, create(:vulnerability_feedback, :sast, :issue,
issue: create(:issue, project: project), pipeline: pipeline,
project: project_dev, project_fingerprint: occ.project_fingerprint) issue: create(:issue, project: project_dev),
project: project_dev,
project_fingerprint: occurrence.project_fingerprint)
end end
expect { get_summary }.not_to exceed_query_limit(control_count)
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