Commit 16933d16 authored by Adam Hegyi's avatar Adam Hegyi

Add filter arguments to MR GraphQL API

Allow filtering MRs by the following arguments:

- Author
- Assignee
- Milestone
parent c909c0ec
......@@ -29,11 +29,14 @@ module Resolvers
as: :label_name,
description: 'Array of label names. All resolved merge requests will have all of these labels.'
argument :merged_after, Types::TimeType,
required: false,
description: 'Merge requests merged after this date'
required: false,
description: 'Merge requests merged after this date'
argument :merged_before, Types::TimeType,
required: false,
description: 'Merge requests merged before this date'
required: false,
description: 'Merge requests merged before this date'
argument :milestone_title, GraphQL::STRING_TYPE,
required: false,
description: 'Title of the milestone'
def self.single
::Resolvers::MergeRequestResolver
......
# frozen_string_literal: true
module Resolvers
class ProjectMergeRequestsResolver < MergeRequestsResolver
argument :assignee_username, GraphQL::STRING_TYPE,
required: false,
description: 'Username of the assignee'
argument :author_username, GraphQL::STRING_TYPE,
required: false,
description: 'Username of the author'
end
end
......@@ -134,7 +134,7 @@ module Types
null: true,
description: 'Merge requests of the project',
extras: [:lookahead],
resolver: Resolvers::MergeRequestsResolver
resolver: Resolvers::ProjectMergeRequestsResolver
field :merge_request,
Types::MergeRequestType,
......
---
title: Filter Merge Requests by author, assignee and milestone in GraphQL
merge_request: 40265
author:
type: added
......@@ -11419,6 +11419,16 @@ type Project {
"""
after: String
"""
Username of the assignee
"""
assigneeUsername: String
"""
Username of the author
"""
authorUsername: String
"""
Returns the elements in the list that come before the specified cursor.
"""
......@@ -11454,6 +11464,11 @@ type Project {
"""
mergedBefore: Time
"""
Title of the milestone
"""
milestoneTitle: String
"""
Array of source branch names. All resolved merge requests will have one of these branches as their source.
"""
......@@ -16511,6 +16526,11 @@ type User {
"""
mergedBefore: Time
"""
Title of the milestone
"""
milestoneTitle: String
"""
The global ID of the project the authored merge requests should be in. Incompatible with projectPath.
"""
......@@ -16581,6 +16601,11 @@ type User {
"""
mergedBefore: Time
"""
Title of the milestone
"""
milestoneTitle: String
"""
The global ID of the project the authored merge requests should be in. Incompatible with projectPath.
"""
......
......@@ -33824,6 +33824,36 @@
},
"defaultValue": null
},
{
"name": "milestoneTitle",
"description": "Title of the milestone",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "assigneeUsername",
"description": "Username of the assignee",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "authorUsername",
"description": "Username of the author",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
......@@ -48486,6 +48516,16 @@
},
"defaultValue": null
},
{
"name": "milestoneTitle",
"description": "Title of the milestone",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "projectPath",
"description": "The full-path of the project the authored merge requests should be in. Incompatible with projectId.",
......@@ -48661,6 +48701,16 @@
},
"defaultValue": null
},
{
"name": "milestoneTitle",
"description": "Title of the milestone",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "projectPath",
"description": "The full-path of the project the authored merge requests should be in. Incompatible with projectId.",
......@@ -6,7 +6,9 @@ RSpec.describe Resolvers::MergeRequestsResolver do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
let_it_be(:milestone) { create(:milestone, project: project) }
let_it_be(:current_user) { create(:user) }
let_it_be(:other_user) { create(:user) }
let_it_be(:common_attrs) { { author: current_user, source_project: project, target_project: project } }
let_it_be(:merge_request_1) { create(:merge_request, :simple, **common_attrs) }
let_it_be(:merge_request_2) { create(:merge_request, :rebased, **common_attrs) }
......@@ -14,8 +16,10 @@ RSpec.describe Resolvers::MergeRequestsResolver do
let_it_be(:merge_request_4) { create(:merge_request, :unique_branches, :locked, **common_attrs) }
let_it_be(:merge_request_5) { create(:merge_request, :simple, :locked, **common_attrs) }
let_it_be(:merge_request_6) { create(:labeled_merge_request, :unique_branches, labels: create_list(:label, 2), **common_attrs) }
let_it_be(:merge_request_with_milestone) { create(:merge_request, :unique_branches, **common_attrs, milestone: milestone) }
let_it_be(:other_project) { create(:project, :repository) }
let_it_be(:other_merge_request) { create(:merge_request, source_project: other_project, target_project: other_project) }
let(:iid_1) { merge_request_1.iid }
let(:iid_2) { merge_request_2.iid }
let(:other_iid) { other_merge_request.iid }
......@@ -32,7 +36,7 @@ RSpec.describe Resolvers::MergeRequestsResolver do
it 'returns all merge requests' do
result = resolve_mr(project, {})
expect(result).to contain_exactly(merge_request_1, merge_request_2, merge_request_3, merge_request_4, merge_request_5, merge_request_6)
expect(result).to contain_exactly(merge_request_1, merge_request_2, merge_request_3, merge_request_4, merge_request_5, merge_request_6, merge_request_with_milestone)
end
it 'returns only merge requests that the current user can see' do
......@@ -179,6 +183,20 @@ RSpec.describe Resolvers::MergeRequestsResolver do
end
end
context 'by milestone' do
it 'filters merge requests by milestone title' do
result = resolve_mr(project, milestone_title: milestone.title)
expect(result).to eq([merge_request_with_milestone])
end
it 'does not find anything' do
result = resolve_mr(project, milestone_title: 'unknown-milestone')
expect(result).to be_empty
end
end
describe 'combinations' do
it 'requires all filters' do
create(:merge_request, :closed, source_project: project, target_project: project, source_branch: merge_request_4.source_branch)
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Resolvers::ProjectMergeRequestsResolver do
include GraphqlHelpers
let_it_be(:project) { create(:project, :repository) }
let_it_be(:current_user) { create(:user) }
let_it_be(:other_user) { create(:user) }
let_it_be(:merge_request_with_author_and_assignee) do
create(:merge_request,
:unique_branches,
source_project: project,
target_project: project,
author: other_user,
assignee: other_user)
end
before do
project.add_developer(current_user)
end
context 'by assignee' do
it 'filters merge requests by assignee username' do
result = resolve_mr(project, assignee_username: other_user.username)
expect(result).to eq([merge_request_with_author_and_assignee])
end
it 'does not find anything' do
result = resolve_mr(project, assignee_username: 'unknown-user')
expect(result).to be_empty
end
end
context 'by author' do
it 'filters merge requests by author username' do
result = resolve_mr(project, author_username: other_user.username)
expect(result).to eq([merge_request_with_author_and_assignee])
end
it 'does not find anything' do
result = resolve_mr(project, author_username: 'unknown-user')
expect(result).to be_empty
end
end
def resolve_mr(project, args, resolver: described_class, user: current_user)
resolve(resolver, obj: project, args: args, ctx: { current_user: user })
end
end
......@@ -59,7 +59,7 @@ RSpec.describe GitlabSchema.types['Project'] do
subject { described_class.fields['mergeRequests'] }
it { is_expected.to have_graphql_type(Types::MergeRequestType.connection_type) }
it { is_expected.to have_graphql_resolver(Resolvers::MergeRequestsResolver) }
it { is_expected.to have_graphql_resolver(Resolvers::ProjectMergeRequestsResolver) }
it do
is_expected.to have_graphql_arguments(:iids,
......@@ -72,7 +72,10 @@ RSpec.describe GitlabSchema.types['Project'] do
:first,
:last,
:merged_after,
:merged_before
:merged_before,
:author_username,
:assignee_username,
:milestone_title
)
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