Commit 8c185e66 authored by Eulyeon Ko's avatar Eulyeon Ko Committed by Jan Provaznik

Batch load merge requests count to avoid N+1

Create a resolver to batch load merge requests count for issues.
parent 67ebd3ee
# frozen_string_literal: true
module Resolvers
class MergeRequestsCountResolver < BaseResolver
include Gitlab::Graphql::Authorize::AuthorizeResource
type GraphQL::Types::Int, null: true
def resolve
authorize!(object)
BatchLoader::GraphQL.for(object.id).batch do |ids, loader, args|
counts = MergeRequestsClosingIssues.count_for_collection(ids, context[:current_user]).to_h
ids.each do |id|
loader.call(id, counts[id] || 0)
end
end
end
def authorized_resource?(object)
ability = "read_#{object.class.name.underscore}".to_sym
context[:current_user].present? && Ability.allowed?(context[:current_user], ability, object)
end
end
end
...@@ -60,8 +60,9 @@ module Types ...@@ -60,8 +60,9 @@ module Types
description: 'Number of upvotes the issue has received.' description: 'Number of upvotes the issue has received.'
field :downvotes, GraphQL::Types::Int, null: false, field :downvotes, GraphQL::Types::Int, null: false,
description: 'Number of downvotes the issue has received.' description: 'Number of downvotes the issue has received.'
field :merge_requests_count, GraphQL::INT_TYPE, null: false, field :merge_requests_count, GraphQL::Types::Int, null: false,
description: 'Number of merge requests that close the issue on merge.' description: 'Number of merge requests that close the issue on merge.',
resolver: Resolvers::MergeRequestsCountResolver
field :user_notes_count, GraphQL::Types::Int, null: false, field :user_notes_count, GraphQL::Types::Int, null: false,
description: 'Number of user notes of the issue.', description: 'Number of user notes of the issue.',
resolver: Resolvers::UserNotesCountResolver resolver: Resolvers::UserNotesCountResolver
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Resolvers::MergeRequestsCountResolver do
include GraphqlHelpers
describe '#resolve' do
let_it_be(:user) { create(:user) }
specify do
expect(described_class).to have_nullable_graphql_type(GraphQL::Types::Int)
end
context 'when counting closing merge requests from a public issue' do
let_it_be(:project) { create(:project, :repository, :public) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:merge_request) { create(:merge_requests_closing_issues, issue: issue) }
subject { batch_sync { resolve_merge_requests_count(issue) } }
it 'returns the count of the merge requests closing the issue' do
expect(subject).to eq(1)
end
end
context 'when attempting to view a private issue' do
let_it_be(:private_project) { create(:project, :repository, :private) }
let_it_be(:issue) { create(:issue, project: private_project) }
before_all do
create(:merge_requests_closing_issues, issue: issue)
create(:merge_requests_closing_issues, issue: issue)
end
context 'when a user has permission to view the issue' do
before do
private_project.add_developer(user)
end
subject { batch_sync { resolve_merge_requests_count(issue) } }
it 'returns the count of the merge requests closing the issue' do
expect(subject).to eq(2)
end
end
context 'when a user does not have permission to view the issue' do
subject { batch_sync { resolve_merge_requests_count(issue) } }
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
end
end
def resolve_merge_requests_count(obj)
resolve(described_class, obj: obj, ctx: { current_user: user })
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