Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
dd43ba1e
Commit
dd43ba1e
authored
Aug 04, 2020
by
Sean Arnold
Committed by
Mayra Cabrera
Aug 04, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add issue_type filter for issues in graphql
- Add type - Add resolver - Add scope to model
parent
98a5301e
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
279 additions
and
1 deletion
+279
-1
app/finders/issues_finder.rb
app/finders/issues_finder.rb
+10
-0
app/graphql/resolvers/issues_resolver.rb
app/graphql/resolvers/issues_resolver.rb
+4
-0
app/graphql/types/issue_type.rb
app/graphql/types/issue_type.rb
+4
-0
app/graphql/types/issue_type_enum.rb
app/graphql/types/issue_type_enum.rb
+12
-0
app/models/issue.rb
app/models/issue.rb
+1
-0
changelogs/unreleased/232405-graphql-filter-issue-type.yml
changelogs/unreleased/232405-graphql-filter-issue-type.yml
+5
-0
doc/api/graphql/reference/gitlab_schema.graphql
doc/api/graphql/reference/gitlab_schema.graphql
+40
-0
doc/api/graphql/reference/gitlab_schema.json
doc/api/graphql/reference/gitlab_schema.json
+105
-0
doc/api/graphql/reference/index.md
doc/api/graphql/reference/index.md
+2
-0
spec/finders/issues_finder_spec.rb
spec/finders/issues_finder_spec.rb
+52
-0
spec/graphql/resolvers/issues_resolver_spec.rb
spec/graphql/resolvers/issues_resolver_spec.rb
+15
-1
spec/graphql/types/issue_type_enum_spec.rb
spec/graphql/types/issue_type_enum_spec.rb
+13
-0
spec/models/issue_spec.rb
spec/models/issue_spec.rb
+16
-0
No files found.
app/finders/issues_finder.rb
View file @
dd43ba1e
...
@@ -25,6 +25,7 @@
...
@@ -25,6 +25,7 @@
# updated_after: datetime
# updated_after: datetime
# updated_before: datetime
# updated_before: datetime
# confidential: boolean
# confidential: boolean
# issue_type: array of strings (one of Issue.issue_types)
#
#
class
IssuesFinder
<
IssuableFinder
class
IssuesFinder
<
IssuableFinder
CONFIDENTIAL_ACCESS_LEVEL
=
Gitlab
::
Access
::
REPORTER
CONFIDENTIAL_ACCESS_LEVEL
=
Gitlab
::
Access
::
REPORTER
...
@@ -73,6 +74,7 @@ class IssuesFinder < IssuableFinder
...
@@ -73,6 +74,7 @@ class IssuesFinder < IssuableFinder
issues
=
super
issues
=
super
issues
=
by_due_date
(
issues
)
issues
=
by_due_date
(
issues
)
issues
=
by_confidential
(
issues
)
issues
=
by_confidential
(
issues
)
issues
=
by_issue_types
(
issues
)
issues
issues
end
end
...
@@ -97,6 +99,14 @@ class IssuesFinder < IssuableFinder
...
@@ -97,6 +99,14 @@ class IssuesFinder < IssuableFinder
items
.
due_between
(
Date
.
today
-
2
.
weeks
,
(
Date
.
today
+
1
.
month
).
end_of_month
)
items
.
due_between
(
Date
.
today
-
2
.
weeks
,
(
Date
.
today
+
1
.
month
).
end_of_month
)
end
end
end
end
def
by_issue_types
(
items
)
issue_type_params
=
Array
(
params
[
:issue_types
]).
map
(
&
:to_s
)
return
items
if
issue_type_params
.
blank?
return
Issue
.
none
unless
(
Issue
.
issue_types
.
keys
&
issue_type_params
).
sort
==
issue_type_params
.
sort
items
.
with_issue_type
(
params
[
:issue_types
])
end
end
end
IssuesFinder
.
prepend_if_ee
(
'EE::IssuesFinder'
)
IssuesFinder
.
prepend_if_ee
(
'EE::IssuesFinder'
)
app/graphql/resolvers/issues_resolver.rb
View file @
dd43ba1e
...
@@ -49,6 +49,10 @@ module Resolvers
...
@@ -49,6 +49,10 @@ module Resolvers
description:
'Sort issues by this criteria'
,
description:
'Sort issues by this criteria'
,
required:
false
,
required:
false
,
default_value:
'created_desc'
default_value:
'created_desc'
argument
:types
,
[
Types
::
IssueTypeEnum
],
as: :issue_types
,
description:
'Filter issues by the given issue types'
,
required:
false
type
Types
::
IssueType
,
null:
true
type
Types
::
IssueType
,
null:
true
...
...
app/graphql/types/issue_type.rb
View file @
dd43ba1e
...
@@ -97,6 +97,10 @@ module Types
...
@@ -97,6 +97,10 @@ module Types
field
:design_collection
,
Types
::
DesignManagement
::
DesignCollectionType
,
null:
true
,
field
:design_collection
,
Types
::
DesignManagement
::
DesignCollectionType
,
null:
true
,
description:
'Collection of design images associated with this issue'
description:
'Collection of design images associated with this issue'
field
:type
,
Types
::
IssueTypeEnum
,
null:
true
,
method: :issue_type
,
description:
'Type of the issue'
end
end
end
end
...
...
app/graphql/types/issue_type_enum.rb
0 → 100644
View file @
dd43ba1e
# frozen_string_literal: true
module
Types
class
IssueTypeEnum
<
BaseEnum
graphql_name
'IssueType'
description
'Issue type'
::
Issue
.
issue_types
.
keys
.
each
do
|
issue_type
|
value
issue_type
.
upcase
,
value:
issue_type
,
description:
"
#{
issue_type
.
titleize
}
issue type"
end
end
end
app/models/issue.rb
View file @
dd43ba1e
...
@@ -106,6 +106,7 @@ class Issue < ApplicationRecord
...
@@ -106,6 +106,7 @@ class Issue < ApplicationRecord
milestone:
{
project:
[
:route
,
{
namespace: :route
}]
},
milestone:
{
project:
[
:route
,
{
namespace: :route
}]
},
project:
[
:route
,
{
namespace: :route
}])
project:
[
:route
,
{
namespace: :route
}])
}
}
scope
:with_issue_type
,
->
(
types
)
{
where
(
issue_type:
types
)
}
scope
:public_only
,
->
{
where
(
confidential:
false
)
}
scope
:public_only
,
->
{
where
(
confidential:
false
)
}
scope
:confidential_only
,
->
{
where
(
confidential:
true
)
}
scope
:confidential_only
,
->
{
where
(
confidential:
true
)
}
...
...
changelogs/unreleased/232405-graphql-filter-issue-type.yml
0 → 100644
View file @
dd43ba1e
---
title
:
Filter Issues in GraphQL by type of Issue
merge_request
:
38017
author
:
type
:
added
doc/api/graphql/reference/gitlab_schema.graphql
View file @
dd43ba1e
...
@@ -4505,6 +4505,11 @@ type EpicIssue implements Noteable {
...
@@ -4505,6 +4505,11 @@ type EpicIssue implements Noteable {
"""
"""
totalTimeSpent
:
Int
!
totalTimeSpent
:
Int
!
"""
Type
of
the
issue
"""
type
:
IssueType
"""
"""
Timestamp
of
when
the
issue
was
last
updated
Timestamp
of
when
the
issue
was
last
updated
"""
"""
...
@@ -5263,6 +5268,11 @@ type Group {
...
@@ -5263,6 +5268,11 @@ type Group {
"""
"""
state
:
IssuableState
state
:
IssuableState
"""
Filter
issues
by
the
given
issue
types
"""
types
:
[
IssueType
!]
"""
"""
Issues
updated
after
this
date
Issues
updated
after
this
date
"""
"""
...
@@ -6138,6 +6148,11 @@ type Issue implements Noteable {
...
@@ -6138,6 +6148,11 @@ type Issue implements Noteable {
"""
"""
totalTimeSpent
:
Int
!
totalTimeSpent
:
Int
!
"""
Type
of
the
issue
"""
type
:
IssueType
"""
"""
Timestamp
of
when
the
issue
was
last
updated
Timestamp
of
when
the
issue
was
last
updated
"""
"""
...
@@ -6669,6 +6684,21 @@ enum IssueState {
...
@@ -6669,6 +6684,21 @@ enum IssueState {
opened
opened
}
}
"""
Issue type
"""
enum
IssueType
{
"""
Incident
issue
type
"""
INCIDENT
"""
Issue
issue
type
"""
ISSUE
}
"""
"""
Represents an iteration object.
Represents an iteration object.
"""
"""
...
@@ -9593,6 +9623,11 @@ type Project {
...
@@ -9593,6 +9623,11 @@ type Project {
"""
"""
state
:
IssuableState
state
:
IssuableState
"""
Filter
issues
by
the
given
issue
types
"""
types
:
[
IssueType
!]
"""
"""
Issues
updated
after
this
date
Issues
updated
after
this
date
"""
"""
...
@@ -9698,6 +9733,11 @@ type Project {
...
@@ -9698,6 +9733,11 @@ type Project {
"""
"""
state
:
IssuableState
state
:
IssuableState
"""
Filter
issues
by
the
given
issue
types
"""
types
:
[
IssueType
!]
"""
"""
Issues
updated
after
this
date
Issues
updated
after
this
date
"""
"""
...
...
doc/api/graphql/reference/gitlab_schema.json
View file @
dd43ba1e
...
@@ -12575,6 +12575,20 @@
...
@@ -12575,6 +12575,20 @@
"isDeprecated": false,
"isDeprecated": false,
"deprecationReason": null
"deprecationReason": null
},
},
{
"name": "type",
"description": "Type of the issue",
"args": [
],
"type": {
"kind": "ENUM",
"name": "IssueType",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
{
"name": "updatedAt",
"name": "updatedAt",
"description": "Timestamp of when the issue was last updated",
"description": "Timestamp of when the issue was last updated",
...
@@ -14575,6 +14589,24 @@
...
@@ -14575,6 +14589,24 @@
},
},
"defaultValue": "created_desc"
"defaultValue": "created_desc"
},
},
{
"name": "types",
"description": "Filter issues by the given issue types",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "IssueType",
"ofType": null
}
}
},
"defaultValue": null
},
{
{
"name": "iterationId",
"name": "iterationId",
"description": "Iterations applied to the issue",
"description": "Iterations applied to the issue",
...
@@ -16931,6 +16963,20 @@
...
@@ -16931,6 +16963,20 @@
"isDeprecated": false,
"isDeprecated": false,
"deprecationReason": null
"deprecationReason": null
},
},
{
"name": "type",
"description": "Type of the issue",
"args": [
],
"type": {
"kind": "ENUM",
"name": "IssueType",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
{
"name": "updatedAt",
"name": "updatedAt",
"description": "Timestamp of when the issue was last updated",
"description": "Timestamp of when the issue was last updated",
...
@@ -18412,6 +18458,29 @@
...
@@ -18412,6 +18458,29 @@
],
],
"possibleTypes": null
"possibleTypes": null
},
},
{
"kind": "ENUM",
"name": "IssueType",
"description": "Issue type",
"fields": null,
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "ISSUE",
"description": "Issue issue type",
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "INCIDENT",
"description": "Incident issue type",
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
},
{
{
"kind": "OBJECT",
"kind": "OBJECT",
"name": "Iteration",
"name": "Iteration",
...
@@ -28690,6 +28759,24 @@
...
@@ -28690,6 +28759,24 @@
},
},
"defaultValue": "created_desc"
"defaultValue": "created_desc"
},
},
{
"name": "types",
"description": "Filter issues by the given issue types",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "IssueType",
"ofType": null
}
}
},
"defaultValue": null
},
{
{
"name": "iterationId",
"name": "iterationId",
"description": "Iterations applied to the issue",
"description": "Iterations applied to the issue",
...
@@ -28883,6 +28970,24 @@
...
@@ -28883,6 +28970,24 @@
},
},
"defaultValue": "created_desc"
"defaultValue": "created_desc"
},
},
{
"name": "types",
"description": "Filter issues by the given issue types",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "IssueType",
"ofType": null
}
}
},
"defaultValue": null
},
{
{
"name": "iterationId",
"name": "iterationId",
"description": "Iterations applied to the issue",
"description": "Iterations applied to the issue",
doc/api/graphql/reference/index.md
View file @
dd43ba1e
...
@@ -756,6 +756,7 @@ Relationship between an epic and an issue
...
@@ -756,6 +756,7 @@ Relationship between an epic and an issue
|
`title`
| String! | Title of the issue |
|
`title`
| String! | Title of the issue |
|
`titleHtml`
| String | The GitLab Flavored Markdown rendering of
`title`
|
|
`titleHtml`
| String | The GitLab Flavored Markdown rendering of
`title`
|
|
`totalTimeSpent`
| Int! | Total time reported as spent on the issue |
|
`totalTimeSpent`
| Int! | Total time reported as spent on the issue |
|
`type`
| IssueType | Type of the issue |
|
`updatedAt`
| Time! | Timestamp of when the issue was last updated |
|
`updatedAt`
| Time! | Timestamp of when the issue was last updated |
|
`upvotes`
| Int! | Number of upvotes the issue has received |
|
`upvotes`
| Int! | Number of upvotes the issue has received |
|
`userNotesCount`
| Int! | Number of user notes of the issue |
|
`userNotesCount`
| Int! | Number of user notes of the issue |
...
@@ -922,6 +923,7 @@ Represents a Group Member
...
@@ -922,6 +923,7 @@ Represents a Group Member
|
`title`
| String! | Title of the issue |
|
`title`
| String! | Title of the issue |
|
`titleHtml`
| String | The GitLab Flavored Markdown rendering of
`title`
|
|
`titleHtml`
| String | The GitLab Flavored Markdown rendering of
`title`
|
|
`totalTimeSpent`
| Int! | Total time reported as spent on the issue |
|
`totalTimeSpent`
| Int! | Total time reported as spent on the issue |
|
`type`
| IssueType | Type of the issue |
|
`updatedAt`
| Time! | Timestamp of when the issue was last updated |
|
`updatedAt`
| Time! | Timestamp of when the issue was last updated |
|
`upvotes`
| Int! | Number of upvotes the issue has received |
|
`upvotes`
| Int! | Number of upvotes the issue has received |
|
`userNotesCount`
| Int! | Number of user notes of the issue |
|
`userNotesCount`
| Int! | Number of user notes of the issue |
...
...
spec/finders/issues_finder_spec.rb
View file @
dd43ba1e
...
@@ -668,6 +668,58 @@ RSpec.describe IssuesFinder do
...
@@ -668,6 +668,58 @@ RSpec.describe IssuesFinder do
end
end
end
end
context
'filtering by issue type'
do
let_it_be
(
:incident_issue
)
{
create
(
:incident
,
project:
project1
)
}
context
'no type given'
do
let
(
:params
)
{
{
issue_types:
[]
}
}
it
'returns all issues'
do
expect
(
issues
).
to
contain_exactly
(
incident_issue
,
issue1
,
issue2
,
issue3
,
issue4
)
end
end
context
'incident type'
do
let
(
:params
)
{
{
issue_types:
[
'incident'
]
}
}
it
'returns incident issues'
do
expect
(
issues
).
to
contain_exactly
(
incident_issue
)
end
end
context
'issue type'
do
let
(
:params
)
{
{
issue_types:
[
'issue'
]
}
}
it
'returns all issues with type issue'
do
expect
(
issues
).
to
contain_exactly
(
issue1
,
issue2
,
issue3
,
issue4
)
end
end
context
'multiple params'
do
let
(
:params
)
{
{
issue_types:
%w(issue incident)
}
}
it
'returns all issues'
do
expect
(
issues
).
to
contain_exactly
(
incident_issue
,
issue1
,
issue2
,
issue3
,
issue4
)
end
end
context
'without array'
do
let
(
:params
)
{
{
issue_types:
'incident'
}
}
it
'returns incident issues'
do
expect
(
issues
).
to
contain_exactly
(
incident_issue
)
end
end
context
'invalid params'
do
let
(
:params
)
{
{
issue_types:
[
'nonsense'
]
}
}
it
'returns no issues'
do
expect
(
issues
).
to
eq
(
Issue
.
none
)
end
end
end
context
'when the user is unauthorized'
do
context
'when the user is unauthorized'
do
let
(
:search_user
)
{
nil
}
let
(
:search_user
)
{
nil
}
...
...
spec/graphql/resolvers/issues_resolver_spec.rb
View file @
dd43ba1e
...
@@ -13,7 +13,7 @@ RSpec.describe Resolvers::IssuesResolver do
...
@@ -13,7 +13,7 @@ RSpec.describe Resolvers::IssuesResolver do
let_it_be
(
:milestone
)
{
create
(
:milestone
,
project:
project
)
}
let_it_be
(
:milestone
)
{
create
(
:milestone
,
project:
project
)
}
let_it_be
(
:assignee
)
{
create
(
:user
)
}
let_it_be
(
:assignee
)
{
create
(
:user
)
}
let_it_be
(
:issue1
)
{
create
(
:i
ssue
,
project:
project
,
state: :opened
,
created_at:
3
.
hours
.
ago
,
updated_at:
3
.
hours
.
ago
,
milestone:
milestone
)
}
let_it_be
(
:issue1
)
{
create
(
:i
ncident
,
project:
project
,
state: :opened
,
created_at:
3
.
hours
.
ago
,
updated_at:
3
.
hours
.
ago
,
milestone:
milestone
)
}
let_it_be
(
:issue2
)
{
create
(
:issue
,
project:
project
,
state: :closed
,
title:
'foo'
,
created_at:
1
.
hour
.
ago
,
updated_at:
1
.
hour
.
ago
,
closed_at:
1
.
hour
.
ago
,
assignees:
[
assignee
])
}
let_it_be
(
:issue2
)
{
create
(
:issue
,
project:
project
,
state: :closed
,
title:
'foo'
,
created_at:
1
.
hour
.
ago
,
updated_at:
1
.
hour
.
ago
,
closed_at:
1
.
hour
.
ago
,
assignees:
[
assignee
])
}
let_it_be
(
:issue3
)
{
create
(
:issue
,
project:
other_project
,
state: :closed
,
title:
'foo'
,
created_at:
1
.
hour
.
ago
,
updated_at:
1
.
hour
.
ago
,
closed_at:
1
.
hour
.
ago
,
assignees:
[
assignee
])
}
let_it_be
(
:issue3
)
{
create
(
:issue
,
project:
other_project
,
state: :closed
,
title:
'foo'
,
created_at:
1
.
hour
.
ago
,
updated_at:
1
.
hour
.
ago
,
closed_at:
1
.
hour
.
ago
,
assignees:
[
assignee
])
}
let_it_be
(
:issue4
)
{
create
(
:issue
)
}
let_it_be
(
:issue4
)
{
create
(
:issue
)
}
...
@@ -95,6 +95,20 @@ RSpec.describe Resolvers::IssuesResolver do
...
@@ -95,6 +95,20 @@ RSpec.describe Resolvers::IssuesResolver do
end
end
end
end
describe
'filters by issue_type'
do
it
'filters by a single type'
do
expect
(
resolve_issues
(
issue_types:
[
'incident'
])).
to
contain_exactly
(
issue1
)
end
it
'filters by more than one type'
do
expect
(
resolve_issues
(
issue_types:
%w(incident issue)
)).
to
contain_exactly
(
issue1
,
issue2
)
end
it
'ignores the filter if none given'
do
expect
(
resolve_issues
(
issue_types:
[])).
to
contain_exactly
(
issue1
,
issue2
)
end
end
context
'when searching issues'
do
context
'when searching issues'
do
it
'returns correct issues'
do
it
'returns correct issues'
do
expect
(
resolve_issues
(
search:
'foo'
)).
to
contain_exactly
(
issue2
)
expect
(
resolve_issues
(
search:
'foo'
)).
to
contain_exactly
(
issue2
)
...
...
spec/graphql/types/issue_type_enum_spec.rb
0 → 100644
View file @
dd43ba1e
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Types
::
IssueTypeEnum
do
specify
{
expect
(
described_class
.
graphql_name
).
to
eq
(
'IssueType'
)
}
it
'exposes all the existing issue type values'
do
expect
(
described_class
.
values
.
keys
).
to
include
(
*
%w[ISSUE INCIDENT]
)
end
end
spec/models/issue_spec.rb
View file @
dd43ba1e
...
@@ -128,6 +128,22 @@ RSpec.describe Issue do
...
@@ -128,6 +128,22 @@ RSpec.describe Issue do
end
end
end
end
describe
'.with_issue_type'
do
let_it_be
(
:project
)
{
create
(
:project
)
}
let_it_be
(
:issue
)
{
create
(
:issue
,
project:
project
)
}
let_it_be
(
:incident
)
{
create
(
:incident
,
project:
project
)
}
it
'gives issues with the given issue type'
do
expect
(
described_class
.
with_issue_type
(
'issue'
))
.
to
contain_exactly
(
issue
)
end
it
'gives issues with the given issue type'
do
expect
(
described_class
.
with_issue_type
(
%w(issue incident)
))
.
to
contain_exactly
(
issue
,
incident
)
end
end
describe
'#order_by_position_and_priority'
do
describe
'#order_by_position_and_priority'
do
let
(
:project
)
{
create
:project
}
let
(
:project
)
{
create
:project
}
let
(
:p1
)
{
create
(
:label
,
title:
'P1'
,
project:
project
,
priority:
1
)
}
let
(
:p1
)
{
create
(
:label
,
title:
'P1'
,
project:
project
,
priority:
1
)
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment