Commit 426f1c20 authored by charlieablett's avatar charlieablett

Remove N+1 query for project and group boards

- Add test for N+1 queries
- Add destroyable lists scope to Board and List
- Preload lists for both project and group boards
parent 4633df7b
...@@ -5,10 +5,13 @@ class Board < ApplicationRecord ...@@ -5,10 +5,13 @@ class Board < ApplicationRecord
belongs_to :project belongs_to :project
has_many :lists, -> { order(:list_type, :position) }, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent has_many :lists, -> { order(:list_type, :position) }, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_many :destroyable_lists, -> { destroyable }, class_name: "List"
validates :project, presence: true, if: :project_needed? validates :project, presence: true, if: :project_needed?
validates :group, presence: true, unless: :project validates :group, presence: true, unless: :project
scope :with_associations, -> { preload(:destroyable_lists) }
def project_needed? def project_needed?
!group !group
end end
......
...@@ -2,4 +2,12 @@ ...@@ -2,4 +2,12 @@
class BoardSerializer < BaseSerializer class BoardSerializer < BaseSerializer
entity BoardSimpleEntity entity BoardSimpleEntity
def represent(resource, opts = {})
if resource.respond_to?(:with_associations)
resource = resource.with_associations
end
super
end
end end
...@@ -27,7 +27,7 @@ module API ...@@ -27,7 +27,7 @@ module API
end end
get '/' do get '/' do
authorize!(:read_board, user_project) authorize!(:read_board, user_project)
present paginate(board_parent.boards), with: Entities::Board present paginate(board_parent.boards.with_associations), with: Entities::Board
end end
desc 'Find a project board' do desc 'Find a project board' do
......
...@@ -11,7 +11,7 @@ module API ...@@ -11,7 +11,7 @@ module API
end end
def board_lists def board_lists
board.lists.destroyable board.destroyable_lists
end end
def create_list def create_list
......
...@@ -1101,7 +1101,7 @@ module API ...@@ -1101,7 +1101,7 @@ module API
expose :project, using: Entities::BasicProjectDetails expose :project, using: Entities::BasicProjectDetails
expose :lists, using: Entities::List do |board| expose :lists, using: Entities::List do |board|
board.lists.destroyable board.destroyable_lists
end end
end end
......
...@@ -37,7 +37,7 @@ module API ...@@ -37,7 +37,7 @@ module API
use :pagination use :pagination
end end
get '/' do get '/' do
present paginate(board_parent.boards), with: Entities::Board present paginate(board_parent.boards.with_associations), with: Entities::Board
end end
end end
......
...@@ -14,6 +14,16 @@ shared_examples_for 'group and project boards' do |route_definition, ee = false| ...@@ -14,6 +14,16 @@ shared_examples_for 'group and project boards' do |route_definition, ee = false|
end end
end end
it 'avoids N+1 queries' do
pat = create(:personal_access_token, user: user)
control = ActiveRecord::QueryRecorder.new { get api(root_url, personal_access_token: pat) }
create(:milestone, "#{board_parent.class.name.underscore}": board_parent)
create(:board, "#{board_parent.class.name.underscore}": board_parent)
expect { get api(root_url, personal_access_token: pat) }.not_to exceed_query_limit(control)
end
describe "GET #{route_definition}" do describe "GET #{route_definition}" do
context "when unauthenticated" do context "when unauthenticated" do
it "returns authentication error" do it "returns authentication error" 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