Skip to content
Snippets Groups Projects
Commit 4eca9f6d authored by Niklas's avatar Niklas
Browse files

Add Issue.relatedMergeRequests to GraphQL API

As related MRs are currently parsed every time
from the issue description and its notes, the
computation is expensive.

To prevent performance problems with queries
that return up to 100 issues, the resolver is
limited to 1 execution per query.

Changelog: added
parent ee418168
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -67,6 +67,13 @@ class IssueType < BaseObject
field :merge_requests_count, GraphQL::Types::Int, null: false,
description: 'Number of merge requests that close the issue on merge.',
resolver: Resolvers::MergeRequestsCountResolver
field :related_merge_requests, Types::MergeRequestType.connection_type,
null: true,
description: 'Merge requests related to the issue. This field can only be resolved for one issue in any single request.' do
extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
end
field :relative_position, GraphQL::Types::Int, null: true,
description: 'Relative position of the issue (used for positioning in epic tree and issue boards).'
field :upvotes, GraphQL::Types::Int,
Loading
Loading
@@ -182,6 +189,17 @@ def closed_as_duplicate_of
Gitlab::Graphql::Loaders::BatchModelLoader.new(Issue, object.duplicated_to_id).find
end
 
def related_merge_requests
# rubocop: disable CodeReuse/ActiveRecord
MergeRequest.where(
id: ::Issues::ReferencedMergeRequestsService.new(project: object.project, current_user: current_user)
.execute(object)
.first
.map(&:id)
)
# rubocop: enable CodeReuse/ActiveRecord
end
def discussion_locked
!!object.discussion_locked
end
Loading
Loading
Loading
Loading
@@ -13217,6 +13217,7 @@ Relationship between an epic and an issue.
| <a id="epicissuenotes"></a>`notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) |
| <a id="epicissueparticipants"></a>`participants` | [`UserCoreConnection`](#usercoreconnection) | List of participants in the issue. (see [Connections](#connections)) |
| <a id="epicissueprojectid"></a>`projectId` | [`Int!`](#int) | ID of the issue project. |
| <a id="epicissuerelatedmergerequests"></a>`relatedMergeRequests` | [`MergeRequestConnection`](#mergerequestconnection) | Merge requests related to the issue. This field can only be resolved for one issue in any single request. (see [Connections](#connections)) |
| <a id="epicissuerelatedvulnerabilities"></a>`relatedVulnerabilities` | [`VulnerabilityConnection`](#vulnerabilityconnection) | Related vulnerabilities of the issue. (see [Connections](#connections)) |
| <a id="epicissuerelationpath"></a>`relationPath` | [`String`](#string) | URI path of the epic-issue relation. |
| <a id="epicissuerelativeposition"></a>`relativePosition` | [`Int`](#int) | Relative position of the issue (used for positioning in epic tree and issue boards). |
Loading
Loading
@@ -14963,6 +14964,7 @@ Describes an issuable resource link for incident issues.
| <a id="issuenotes"></a>`notes` | [`NoteConnection!`](#noteconnection) | All notes on this noteable. (see [Connections](#connections)) |
| <a id="issueparticipants"></a>`participants` | [`UserCoreConnection`](#usercoreconnection) | List of participants in the issue. (see [Connections](#connections)) |
| <a id="issueprojectid"></a>`projectId` | [`Int!`](#int) | ID of the issue project. |
| <a id="issuerelatedmergerequests"></a>`relatedMergeRequests` | [`MergeRequestConnection`](#mergerequestconnection) | Merge requests related to the issue. This field can only be resolved for one issue in any single request. (see [Connections](#connections)) |
| <a id="issuerelatedvulnerabilities"></a>`relatedVulnerabilities` | [`VulnerabilityConnection`](#vulnerabilityconnection) | Related vulnerabilities of the issue. (see [Connections](#connections)) |
| <a id="issuerelativeposition"></a>`relativePosition` | [`Int`](#int) | Relative position of the issue (used for positioning in epic tree and issue boards). |
| <a id="issueseverity"></a>`severity` | [`IssuableSeverity`](#issuableseverity) | Severity level of the incident. |
Loading
Loading
@@ -154,6 +154,47 @@ def query(fields)
end
end
 
context 'when selecting `related_merge_requests`' do
let(:issue_fields) { ['relatedMergeRequests { nodes { id } }'] }
let_it_be(:user) { create(:user) }
let_it_be(:mr_project) { project }
let!(:merge_request) do
attributes = {
author: user,
source_project: mr_project,
target_project: mr_project,
source_branch: 'master',
target_branch: 'test',
description: "See #{issue.to_reference}"
}
create(:merge_request, attributes).tap do |merge_request|
create(:note, :system, project: issue.project, noteable: issue,
author: user, note: merge_request.to_reference(full: true))
end
end
before do
project.add_developer(current_user)
post_graphql(query, current_user: current_user)
end
it 'returns the related merge request' do
expect(issue_data['relatedMergeRequests']['nodes']).to include a_hash_including({
'id' => merge_request.to_global_id.to_s
})
end
context 'no permission to related merge request' do
let_it_be(:mr_project) { create(:project, :private) }
it 'does not return the related merge request' do
expect(issue_data['relatedMergeRequests']['nodes']).to be_empty
end
end
end
context 'when there is a confidential issue' do
let!(:confidential_issue) do
create(:issue, :confidential, project: project)
Loading
Loading
Loading
Loading
@@ -16,7 +16,7 @@
let(:fields) do
<<~QUERY
nodes {
#{all_graphql_fields_for('AlertManagementAlert', excluded: ['assignees'])}
#{all_graphql_fields_for('AlertManagementAlert', excluded: %w[assignees relatedMergeRequests])}
}
QUERY
end
Loading
Loading
Loading
Loading
@@ -5,7 +5,7 @@
let(:fields) do
<<~QUERY
nodes {
#{all_graphql_fields_for('issues'.classify)}
#{all_graphql_fields_for('issues'.classify, excluded: ['relatedMergeRequests'])}
}
QUERY
end
Loading
Loading
@@ -683,6 +683,28 @@ def assignees_as_global_ids(issues)
end
end
 
context 'when selecting `related_merge_requests`' do
let(:fields) do
<<~QUERY
nodes {
relatedMergeRequests {
nodes {
id
}
}
}
QUERY
end
it 'limits the field to 1 execution' do
post_query
expect_graphql_errors_to_include(
'"relatedMergeRequests" field can be requested only for 1 Issue(s) at a time.'
)
end
end
it 'includes a web_url' do
post_query
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment