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
aefa6568
Commit
aefa6568
authored
Jun 03, 2019
by
Brett Walker
Committed by
Nick Thomas
Jun 03, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add support for querying epics with GraphQL
wrapped into a Group query
parent
1a23e034
Changes
31
Hide whitespace changes
Inline
Side-by-side
Showing
31 changed files
with
743 additions
and
25 deletions
+743
-25
app/graphql/resolvers/base_resolver.rb
app/graphql/resolvers/base_resolver.rb
+1
-1
app/graphql/resolvers/concerns/resolves_pipelines.rb
app/graphql/resolvers/concerns/resolves_pipelines.rb
+1
-1
app/graphql/resolvers/issues_resolver.rb
app/graphql/resolvers/issues_resolver.rb
+1
-1
app/graphql/types/base_field.rb
app/graphql/types/base_field.rb
+1
-1
app/graphql/types/group_type.rb
app/graphql/types/group_type.rb
+2
-0
app/graphql/types/issue_type.rb
app/graphql/types/issue_type.rb
+6
-0
app/presenters/issue_presenter.rb
app/presenters/issue_presenter.rb
+11
-1
ee/app/graphql/ee/types/group_type.rb
ee/app/graphql/ee/types/group_type.rb
+27
-0
ee/app/graphql/ee/types/issue_type.rb
ee/app/graphql/ee/types/issue_type.rb
+4
-0
ee/app/graphql/resolvers/epic_issues_resolver.rb
ee/app/graphql/resolvers/epic_issues_resolver.rb
+13
-0
ee/app/graphql/resolvers/epic_resolver.rb
ee/app/graphql/resolvers/epic_resolver.rb
+68
-0
ee/app/graphql/types/epic_issue_type.rb
ee/app/graphql/types/epic_issue_type.rb
+15
-0
ee/app/graphql/types/epic_state_enum.rb
ee/app/graphql/types/epic_state_enum.rb
+11
-0
ee/app/graphql/types/epic_type.rb
ee/app/graphql/types/epic_type.rb
+63
-0
ee/app/graphql/types/permission_types/epic.rb
ee/app/graphql/types/permission_types/epic.rb
+13
-0
ee/app/presenters/epic_issue_presenter.rb
ee/app/presenters/epic_issue_presenter.rb
+21
-0
ee/app/presenters/epic_presenter.rb
ee/app/presenters/epic_presenter.rb
+28
-1
ee/changelogs/unreleased/10795-add-epic-tree-BE-epic-graphql-support.yml
...nreleased/10795-add-epic-tree-BE-epic-graphql-support.yml
+5
-0
ee/spec/graphql/ee/types/group_type_spec.rb
ee/spec/graphql/ee/types/group_type_spec.rb
+11
-0
ee/spec/graphql/resolvers/epic_resolver_spec.rb
ee/spec/graphql/resolvers/epic_resolver_spec.rb
+99
-0
ee/spec/graphql/types/epic_issue_type_spec.rb
ee/spec/graphql/types/epic_issue_type_spec.rb
+15
-0
ee/spec/graphql/types/epic_state_enum_spec.rb
ee/spec/graphql/types/epic_state_enum_spec.rb
+11
-0
ee/spec/graphql/types/epic_type_spec.rb
ee/spec/graphql/types/epic_type_spec.rb
+24
-0
ee/spec/graphql/types/issue_type_spec.rb
ee/spec/graphql/types/issue_type_spec.rb
+2
-0
ee/spec/graphql/types/permission_types/epic_spec.rb
ee/spec/graphql/types/permission_types/epic_spec.rb
+14
-0
ee/spec/presenters/epic_issue_presenter_spec.rb
ee/spec/presenters/epic_issue_presenter_spec.rb
+33
-0
ee/spec/presenters/epic_presenter_spec.rb
ee/spec/presenters/epic_presenter_spec.rb
+40
-18
ee/spec/requests/api/graphql/group/epics_spec.rb
ee/spec/requests/api/graphql/group/epics_spec.rb
+167
-0
spec/graphql/types/base_field_spec.rb
spec/graphql/types/base_field_spec.rb
+1
-1
spec/graphql/types/issue_type_spec.rb
spec/graphql/types/issue_type_spec.rb
+6
-0
spec/presenters/issue_presenter_spec.rb
spec/presenters/issue_presenter_spec.rb
+29
-0
No files found.
app/graphql/resolvers/base_resolver.rb
View file @
aefa6568
...
...
@@ -10,7 +10,7 @@ module Resolvers
end
end
def
self
.
resolver_complexity
(
args
)
def
self
.
resolver_complexity
(
args
,
child_complexity
:
)
complexity
=
1
complexity
+=
1
if
args
[
:sort
]
complexity
+=
5
if
args
[
:search
]
...
...
app/graphql/resolvers/concerns/resolves_pipelines.rb
View file @
aefa6568
...
...
@@ -20,7 +20,7 @@ module ResolvesPipelines
end
class_methods
do
def
resolver_complexity
(
args
)
def
resolver_complexity
(
args
,
child_complexity
:
)
complexity
=
super
complexity
+=
2
if
args
[
:sha
]
complexity
+=
2
if
args
[
:ref
]
...
...
app/graphql/resolvers/issues_resolver.rb
View file @
aefa6568
...
...
@@ -58,7 +58,7 @@ module Resolvers
IssuesFinder
.
new
(
context
[
:current_user
],
args
).
execute
end
def
self
.
resolver_complexity
(
args
)
def
self
.
resolver_complexity
(
args
,
child_complexity
:
)
complexity
=
super
complexity
+=
2
if
args
[
:labelName
]
...
...
app/graphql/types/base_field.rb
View file @
aefa6568
...
...
@@ -33,7 +33,7 @@ module Types
limit_value
=
[
args
[
:first
],
args
[
:last
],
page_size
].
compact
.
min
# Resolvers may add extra complexity depending on used arguments
complexity
=
child_complexity
+
self
.
resolver
&
.
try
(
:resolver_complexity
,
args
).
to_i
complexity
=
child_complexity
+
self
.
resolver
&
.
try
(
:resolver_complexity
,
args
,
child_complexity:
child_complexity
).
to_i
# Resolvers may add extra complexity depending on number of items being loaded.
multiplier
=
self
.
resolver
&
.
try
(
:complexity_multiplier
,
args
).
to_f
...
...
app/graphql/types/group_type.rb
View file @
aefa6568
...
...
@@ -21,3 +21,5 @@ module Types
end
end
end
Types
::
GroupType
.
prepend
(
EE
::
Types
::
GroupType
)
app/graphql/types/issue_type.rb
View file @
aefa6568
...
...
@@ -15,6 +15,10 @@ module Types
field
:description
,
GraphQL
::
STRING_TYPE
,
null:
true
field
:state
,
IssueStateEnum
,
null:
false
field
:reference
,
GraphQL
::
STRING_TYPE
,
null:
false
,
method: :to_reference
do
argument
:full
,
GraphQL
::
BOOLEAN_TYPE
,
required:
false
,
default_value:
false
end
field
:author
,
Types
::
UserType
,
null:
false
,
resolve:
->
(
obj
,
_args
,
_ctx
)
{
Gitlab
::
Graphql
::
Loaders
::
BatchModelLoader
.
new
(
User
,
obj
.
author_id
).
find
}
...
...
@@ -37,7 +41,9 @@ module Types
field
:upvotes
,
GraphQL
::
INT_TYPE
,
null:
false
field
:downvotes
,
GraphQL
::
INT_TYPE
,
null:
false
field
:user_notes_count
,
GraphQL
::
INT_TYPE
,
null:
false
field
:web_path
,
GraphQL
::
STRING_TYPE
,
null:
false
,
method: :issue_path
field
:web_url
,
GraphQL
::
STRING_TYPE
,
null:
false
field
:relative_position
,
GraphQL
::
INT_TYPE
,
null:
true
field
:closed_at
,
Types
::
TimeType
,
null:
true
...
...
app/presenters/issue_presenter.rb
View file @
aefa6568
...
...
@@ -4,6 +4,16 @@ class IssuePresenter < Gitlab::View::Presenter::Delegated
presents
:issue
def
web_url
Gitlab
::
UrlBuilder
.
build
(
issue
)
url_builder
.
url
end
def
issue_path
url_builder
.
issue_path
(
issue
)
end
private
def
url_builder
@url_builder
||=
Gitlab
::
UrlBuilder
.
new
(
issue
)
end
end
ee/app/graphql/ee/types/group_type.rb
0 → 100644
View file @
aefa6568
# frozen_string_literal: true
module
EE
module
Types
module
GroupType
extend
ActiveSupport
::
Concern
prepended
do
%i[epics]
.
each
do
|
feature
|
field
"
#{
feature
}
_enabled"
,
GraphQL
::
BOOLEAN_TYPE
,
null:
true
,
resolve:
->
(
group
,
args
,
ctx
)
do
group
.
feature_available?
(
feature
)
end
end
field
:epic
,
::
Types
::
EpicType
,
null:
true
,
resolver:
::
Resolvers
::
EpicResolver
.
single
field
:epics
,
::
Types
::
EpicType
.
connection_type
,
null:
true
,
resolver:
::
Resolvers
::
EpicResolver
end
end
end
end
ee/app/graphql/ee/types/issue_type.rb
View file @
aefa6568
...
...
@@ -6,6 +6,10 @@ module EE
extend
ActiveSupport
::
Concern
prepended
do
field
:weight
,
GraphQL
::
INT_TYPE
,
null:
true
,
resolve:
->
(
obj
,
_args
,
_ctx
)
{
obj
.
supports_weight?
?
obj
.
weight
:
nil
}
field
:designs
,
::
Types
::
DesignManagement
::
DesignCollectionType
,
null:
true
,
method: :design_collection
end
...
...
ee/app/graphql/resolvers/epic_issues_resolver.rb
0 → 100644
View file @
aefa6568
# frozen_string_literal: true
module
Resolvers
class
EpicIssuesResolver
<
BaseResolver
type
Types
::
EpicIssueType
,
null:
true
alias_method
:epic
,
:object
def
resolve
(
**
args
)
epic
.
issues_readable_by
(
context
[
:current_user
])
end
end
end
ee/app/graphql/resolvers/epic_resolver.rb
0 → 100644
View file @
aefa6568
# frozen_string_literal: true
module
Resolvers
class
EpicResolver
<
BaseResolver
argument
:iid
,
GraphQL
::
ID_TYPE
,
required:
false
,
description:
'The IID of the epic, e.g., "1"'
argument
:iids
,
[
GraphQL
::
ID_TYPE
],
required:
false
,
description:
'The list of IIDs of epics, e.g., [1, 2]'
type
Types
::
EpicType
,
null:
true
def
resolve
(
**
args
)
return
[]
unless
object
.
present?
return
[]
unless
epic_feature_enabled?
find_epics
(
transform_args
(
args
))
end
private
def
find_epics
(
args
)
EpicsFinder
.
new
(
context
[
:current_user
],
args
).
execute
end
def
epic_feature_enabled?
group
.
feature_available?
(
:epics
)
end
def
transform_args
(
args
)
transformed
=
args
.
dup
transformed
[
:group_id
]
=
group
.
id
transformed
[
:parent_id
]
=
parent
.
id
if
parent
transformed
[
:iids
]
||=
[
args
[
:iid
]].
compact
transformed
end
# `object` refers to the object we're currently querying on, and is usually a `Group`
# when querying an Epic. In the case of field that uses this resolver, for example
# an Epic's `children` field, then `object` is an `EpicPresenter` (rather than an Epic).
# But that's the epic we need in order to scope the find to only children of this epic,
# using the `parent_id`
def
parent
object
if
object
.
is_a?
(
EpicPresenter
)
end
def
group
return
object
if
object
.
is_a?
(
Group
)
parent
.
group
end
# If we're querying for multiple iids and selecting issues, then ideally
# we want to batch the epic and issue queries into one to reduce N+1 and memory.
# https://gitlab.com/gitlab-org/gitlab-ee/issues/11841
# Until we do that, add in child_complexity for each iid requested
# (minus one for the automatically added child_complexity in the BaseField)
def
self
.
resolver_complexity
(
args
,
child_complexity
:)
complexity
=
super
complexity
+=
(
args
[
:iids
].
count
-
1
)
*
child_complexity
if
args
[
:iids
]
complexity
end
end
end
ee/app/graphql/types/epic_issue_type.rb
0 → 100644
View file @
aefa6568
# frozen_string_literal: true
module
Types
class
EpicIssueType
<
IssueType
graphql_name
'EpicIssue'
present_using
EpicIssuePresenter
field
:epic_issue_id
,
GraphQL
::
ID_TYPE
,
null:
false
field
:relation_path
,
GraphQL
::
STRING_TYPE
,
null:
true
,
resolve:
->
(
issue
,
args
,
ctx
)
do
issue
.
group_epic_issue_path
(
ctx
[
:current_user
])
end
end
end
ee/app/graphql/types/epic_state_enum.rb
0 → 100644
View file @
aefa6568
# frozen_string_literal: true
module
Types
class
EpicStateEnum
<
BaseEnum
graphql_name
'EpicState'
description
'State of a GitLab epic'
value
'opened'
value
'closed'
end
end
ee/app/graphql/types/epic_type.rb
0 → 100644
View file @
aefa6568
# frozen_string_literal: true
module
Types
class
EpicType
<
BaseObject
graphql_name
'Epic'
authorize
:read_epic
expose_permissions
Types
::
PermissionTypes
::
Epic
present_using
EpicPresenter
field
:id
,
GraphQL
::
ID_TYPE
,
null:
false
field
:iid
,
GraphQL
::
ID_TYPE
,
null:
false
field
:title
,
GraphQL
::
STRING_TYPE
,
null:
true
field
:description
,
GraphQL
::
STRING_TYPE
,
null:
true
field
:state
,
EpicStateEnum
,
null:
false
field
:group
,
GroupType
,
null:
false
,
resolve:
->
(
obj
,
_args
,
_ctx
)
{
Gitlab
::
Graphql
::
Loaders
::
BatchModelLoader
.
new
(
Group
,
obj
.
group_id
).
find
}
field
:parent
,
EpicType
,
null:
true
,
resolve:
->
(
obj
,
_args
,
_ctx
)
{
Gitlab
::
Graphql
::
Loaders
::
BatchModelLoader
.
new
(
Epic
,
obj
.
parent_id
).
find
}
field
:author
,
Types
::
UserType
,
null:
false
,
resolve:
->
(
obj
,
_args
,
_ctx
)
{
Gitlab
::
Graphql
::
Loaders
::
BatchModelLoader
.
new
(
User
,
obj
.
author_id
).
find
}
field
:start_date
,
Types
::
TimeType
,
null:
true
field
:start_date_is_fixed
,
GraphQL
::
BOOLEAN_TYPE
,
null:
true
,
method: :start_date_is_fixed?
,
authorize: :admin_epic
field
:start_date_fixed
,
Types
::
TimeType
,
null:
true
,
authorize: :admin_epic
field
:start_date_from_milestones
,
Types
::
TimeType
,
null:
true
,
authorize: :admin_epic
field
:due_date
,
Types
::
TimeType
,
null:
true
field
:due_date_is_fixed
,
GraphQL
::
BOOLEAN_TYPE
,
null:
true
,
method: :due_date_is_fixed?
,
authorize: :admin_epic
field
:due_date_fixed
,
Types
::
TimeType
,
null:
true
,
authorize: :admin_epic
field
:due_date_from_milestones
,
Types
::
TimeType
,
null:
true
,
authorize: :admin_epic
field
:closed_at
,
Types
::
TimeType
,
null:
true
field
:created_at
,
Types
::
TimeType
,
null:
true
field
:updated_at
,
Types
::
TimeType
,
null:
true
field
:children
,
::
Types
::
EpicType
.
connection_type
,
null:
true
,
resolver:
::
Resolvers
::
EpicResolver
field
:has_children
,
GraphQL
::
BOOLEAN_TYPE
,
null:
false
,
method: :has_children?
field
:has_issues
,
GraphQL
::
BOOLEAN_TYPE
,
null:
false
,
method: :has_issues?
field
:web_path
,
GraphQL
::
STRING_TYPE
,
null:
false
,
method: :group_epic_path
field
:web_url
,
GraphQL
::
STRING_TYPE
,
null:
false
,
method: :group_epic_url
field
:relation_path
,
GraphQL
::
STRING_TYPE
,
null:
true
,
method: :group_epic_link_path
field
:reference
,
GraphQL
::
STRING_TYPE
,
null:
false
,
method: :epic_reference
do
argument
:full
,
GraphQL
::
BOOLEAN_TYPE
,
required:
false
,
default_value:
false
end
field
:issues
,
Types
::
EpicIssueType
.
connection_type
,
null:
true
,
resolver:
Resolvers
::
EpicIssuesResolver
end
end
ee/app/graphql/types/permission_types/epic.rb
0 → 100644
View file @
aefa6568
# frozen_string_literal: true
module
Types
module
PermissionTypes
class
Epic
<
BasePermissionType
description
'Check permissions for the current user on an epic'
graphql_name
'EpicPermissions'
abilities
:read_epic
,
:read_epic_iid
,
:update_epic
,
:destroy_epic
,
:admin_epic
,
:create_epic
,
:create_note
,
:award_emoji
end
end
end
ee/app/presenters/epic_issue_presenter.rb
0 → 100644
View file @
aefa6568
# frozen_string_literal: true
class
EpicIssuePresenter
<
Gitlab
::
View
::
Presenter
::
Delegated
presents
:issue
def
group_epic_issue_path
(
current_user
)
return
unless
can_admin_issue_link?
(
current_user
)
url_builder
.
group_epic_issue_path
(
issue
.
epic
.
group
,
issue
.
epic
.
iid
,
issue
.
epic_issue_id
)
end
private
def
url_builder
@url_builder
||=
Gitlab
::
UrlBuilder
.
new
(
issue
)
end
def
can_admin_issue_link?
(
current_user
)
Ability
.
allowed?
(
current_user
,
:admin_epic_issue
,
issue
)
&&
Ability
.
allowed?
(
current_user
,
:admin_epic
,
issue
.
epic
)
end
end
ee/app/presenters/epic_presenter.rb
View file @
aefa6568
...
...
@@ -13,6 +13,28 @@ class EpicPresenter < Gitlab::View::Presenter::Delegated
}
end
def
group_epic_path
url_builder
.
group_epic_path
(
epic
.
group
,
epic
)
end
def
group_epic_url
url_builder
.
group_epic_url
(
epic
.
group
,
epic
)
end
def
group_epic_link_path
return
unless
epic
.
parent
url_builder
.
group_epic_link_path
(
epic
.
group
,
epic
.
parent
.
iid
,
epic
.
id
)
end
def
epic_reference
(
full:
false
)
if
full
epic
.
to_reference
(
full:
true
)
else
epic
.
to_reference
(
epic
.
parent
&
.
group
||
epic
.
group
)
end
end
private
def
initial_data
...
...
@@ -111,11 +133,16 @@ class EpicPresenter < Gitlab::View::Presenter::Delegated
{
id:
epic
.
id
,
title:
epic
.
title
,
url:
epic_path
(
epic
)
,
url:
group_epic_path
,
state:
epic
.
state
,
human_readable_end_date:
epic
.
end_date
&
.
to_s
(
:medium
),
human_readable_timestamp:
remaining_days_in_words
(
epic
.
end_date
,
epic
.
start_date
)
}
end
end
# important for using routing helpers in GraphQL
def
url_builder
@url_builder
||=
Gitlab
::
UrlBuilder
.
new
(
epic
)
end
end
ee/changelogs/unreleased/10795-add-epic-tree-BE-epic-graphql-support.yml
0 → 100644
View file @
aefa6568
---
title
:
Add support for querying epics with GraphQL
merge_request
:
13248
author
:
type
:
added
ee/spec/graphql/ee/types/group_type_spec.rb
0 → 100644
View file @
aefa6568
# frozen_string_literal: true
require
'spec_helper'
describe
GitlabSchema
.
types
[
'Group'
]
do
describe
'nested epic request'
do
it
{
expect
(
described_class
).
to
have_graphql_field
(
:epicsEnabled
)
}
it
{
expect
(
described_class
).
to
have_graphql_field
(
:epics
)
}
it
{
expect
(
described_class
).
to
have_graphql_field
(
:epic
)
}
end
end
ee/spec/graphql/resolvers/epic_resolver_spec.rb
0 → 100644
View file @
aefa6568
# frozen_string_literal: true
require
'spec_helper'
describe
Resolvers
::
EpicResolver
do
include
GraphqlHelpers
set
(
:current_user
)
{
create
(
:user
)
}
set
(
:user2
)
{
create
(
:user
)
}
context
"with a group"
do
let
(
:group
)
{
create
(
:group
)
}
let
(
:project
)
{
create
(
:project
,
:public
,
group:
group
)
}
let
(
:epic1
)
{
create
(
:epic
,
group:
group
,
state: :closed
,
created_at:
3
.
days
.
ago
,
updated_at:
2
.
days
.
ago
)
}
let
(
:epic2
)
{
create
(
:epic
,
group:
group
,
author:
user2
,
title:
'foo'
,
description:
'bar'
,
created_at:
2
.
days
.
ago
,
updated_at:
3
.
days
.
ago
)
}
before
do
group
.
add_developer
(
current_user
)
stub_licensed_features
(
epics:
true
)
end
describe
'#resolve'
do
it
'returns nothing when feature disabled'
do
stub_licensed_features
(
epics:
false
)
expect
(
resolve_epics
).
to
be_empty
end
it
'finds all epics'
do
expect
(
resolve_epics
).
to
contain_exactly
(
epic1
,
epic2
)
end
context
'with iid'
do
it
'finds a specific epic with iid'
do
expect
(
resolve_epics
(
iid:
epic1
.
iid
)).
to
contain_exactly
(
epic1
)
end
it
'does not inflate the complexity'
do
field
=
Types
::
BaseField
.
new
(
name:
'test'
,
type:
GraphQL
::
STRING_TYPE
,
resolver_class:
described_class
,
null:
false
,
max_page_size:
100
)
expect
(
field
.
to_graphql
.
complexity
.
call
({},
{
iid:
[
epic1
.
iid
]
},
5
)).
to
eq
6
end
end
context
'with iids'
do
it
'finds a specific epic with iids'
do
expect
(
resolve_epics
(
iids:
epic1
.
iid
)).
to
contain_exactly
(
epic1
)
end
it
'finds multiple epics with iids'
do
expect
(
resolve_epics
(
iids:
[
epic1
.
iid
,
epic2
.
iid
]))
.
to
contain_exactly
(
epic1
,
epic2
)
end
it
'increases the complexity based on child_complexity and number of iids'
do
field
=
Types
::
BaseField
.
new
(
name:
'test'
,
type:
GraphQL
::
STRING_TYPE
,
resolver_class:
described_class
,
null:
false
,
max_page_size:
100
)
expect
(
field
.
to_graphql
.
complexity
.
call
({},
{
iids:
[
epic1
.
iid
]
},
5
)).
to
eq
6
expect
(
field
.
to_graphql
.
complexity
.
call
({},
{
iids:
[
epic1
.
iid
,
epic2
.
iid
]
},
5
)).
to
eq
11
end
end
context
'with subgroups'
do
let
(
:sub_group
)
{
create
(
:group
,
parent:
group
)
}
let
(
:iids
)
{
[
epic1
,
epic2
].
map
(
&
:iid
)
}
let!
(
:epic3
)
{
create
(
:epic
,
group:
sub_group
,
iid:
epic1
.
iid
)
}
let!
(
:epic4
)
{
create
(
:epic
,
group:
sub_group
,
iid:
epic2
.
iid
)
}
before
do
sub_group
.
add_developer
(
current_user
)
end
it
'finds only the epics within the group we are looking at'
do
expect
(
resolve_epics
(
iids:
iids
)).
to
contain_exactly
(
epic1
,
epic2
)
end
it
'return all epics'
do
expect
(
resolve_epics
).
to
contain_exactly
(
epic1
,
epic2
,
epic3
,
epic4
)
end
end
end
end
context
"when passing a non existent, batch loaded group"
do
let
(
:group
)
do
BatchLoader
.
for
(
"non-existent-path"
).
batch
do
|
_fake_paths
,
loader
,
_
|
loader
.
call
(
"non-existent-path"
,
nil
)
end
end
it
"returns nil without breaking"
do
expect
(
resolve_epics
(
iids:
[
"don't"
,
"break"
])).
to
be_empty
end
end
def
resolve_epics
(
args
=
{},
context
=
{
current_user:
current_user
})
resolve
(
described_class
,
obj:
group
,
args:
args
,
ctx:
context
)
end
end
ee/spec/graphql/types/epic_issue_type_spec.rb
0 → 100644
View file @
aefa6568
# frozen_string_literal: true
require
'spec_helper'
describe
GitlabSchema
.
types
[
'EpicIssue'
]
do
it
{
expect
(
described_class
.
graphql_name
).
to
eq
(
'EpicIssue'
)
}
it
{
expect
(
described_class
).
to
require_graphql_authorizations
(
:read_issue
)
}
it
'has specific fields'
do
%i[epic_issue_id relation_path]
.
each
do
|
field_name
|
expect
(
described_class
).
to
have_graphql_field
(
field_name
)
end
end
end
ee/spec/graphql/types/epic_state_enum_spec.rb
0 → 100644
View file @
aefa6568
# frozen_string_literal: true
require
'spec_helper'
describe
GitlabSchema
.
types
[
'EpicState'
]
do
it
{
expect
(
described_class
.
graphql_name
).
to
eq
(
'EpicState'
)
}
it
'exposes all the existing epic states'
do
expect
(
described_class
.
values
.
keys
).
to
include
(
*
%w[opened closed]
)
end
end
ee/spec/graphql/types/epic_type_spec.rb
0 → 100644
View file @
aefa6568
# frozen_string_literal: true
require
'spec_helper'
describe
GitlabSchema
.
types
[
'Epic'
]
do
let
(
:fields
)
do
%i[
id iid title description state group parent author
start_date start_date_is_fixed start_date_fixed start_date_from_milestones
due_date due_date_is_fixed due_date_fixed due_date_from_milestones
closed_at created_at updated_at children has_children has_issues
web_path web_url relation_path reference issues
user_permissions
]
end
it
{
expect
(
described_class
).
to
expose_permissions_using
(
Types
::
PermissionTypes
::
Epic
)
}
it
{
expect
(
described_class
.
graphql_name
).
to
eq
(
'Epic'
)
}
it
{
expect
(
described_class
).
to
require_graphql_authorizations
(
:read_epic
)
}
it
{
expect
(
described_class
).
to
have_graphql_fields
(
fields
)
}
end
ee/spec/graphql/types/issue_type_spec.rb
View file @
aefa6568
...
...
@@ -3,5 +3,7 @@
require
'spec_helper'
describe
GitlabSchema
.
types
[
'Issue'
]
do
it
{
expect
(
described_class
).
to
have_graphql_field
(
:weight
)
}
it
{
expect
(
described_class
).
to
have_graphql_field
(
:designs
)
}
end
ee/spec/graphql/types/permission_types/epic_spec.rb
0 → 100644
View file @
aefa6568
# frozen_string_literal: true
require
'spec_helper'
describe
Types
::
PermissionTypes
::
Epic
do
it
do
expected_permissions
=
[
:read_epic
,
:read_epic_iid
,
:update_epic
,
:destroy_epic
,
:admin_epic
,
:create_epic
,
:create_note
,
:award_emoji
]
expected_permissions
.
each
do
|
permission
|
expect
(
described_class
).
to
have_graphql_field
(
permission
)
end
end
end
ee/spec/presenters/epic_issue_presenter_spec.rb
0 → 100644
View file @
aefa6568
# frozen_string_literal: true
require
'spec_helper'
describe
EpicIssuePresenter
do
include
Gitlab
::
Routing
.
url_helpers
let
(
:user
)
{
create
(
:user
)
}
let
(
:group
)
{
create
(
:group
)
}
let
(
:project
)
{
create
(
:project
,
group:
group
)
}
let
(
:epic
)
{
create
(
:epic
,
group:
group
)}
let
(
:issue
)
{
create
(
:issue
,
project:
project
)
}
let!
(
:epic_issue
)
{
create
(
:epic_issue
,
issue:
issue
,
epic:
epic
)
}
let
(
:target_issue
)
{
epic
.
issues_readable_by
(
user
).
first
}
let
(
:presenter
)
{
described_class
.
new
(
target_issue
,
current_user:
user
)
}
before
do
stub_licensed_features
(
epics:
true
)
group
.
add_developer
(
user
)
end
describe
'#group_epic_issue_path'
do
it
'returns correct path'
do
expect
(
presenter
.
group_epic_issue_path
(
user
)).
to
eq
"/groups/
#{
group
.
name
}
/-/epics/
#{
epic
.
iid
}
/issues/
#{
target_issue
.
epic_issue_id
}
"
end
it
'returns nil without proper permission'
do
unauth_user
=
create
(
:user
)
expect
(
presenter
.
group_epic_issue_path
(
unauth_user
)).
to
be_nil
end
end
end
ee/spec/presenters/epic_presenter_spec.rb
View file @
aefa6568
...
...
@@ -4,30 +4,24 @@ require 'spec_helper'
describe
EpicPresenter
do
include
UsersHelper
include
Gitlab
::
Routing
.
url_helpers
let
(
:user
)
{
create
(
:user
)
}
let
(
:group
)
{
create
(
:group
)
}
let
(
:parent_epic
)
{
create
(
:epic
,
group:
group
,
start_date:
Date
.
new
(
2000
,
1
,
10
),
due_date:
Date
.
new
(
2000
,
1
,
20
))
}
let
(
:epic
)
{
create
(
:epic
,
group:
group
,
author:
user
,
parent:
parent_epic
)
}
let
(
:presenter
)
{
described_class
.
new
(
epic
,
current_user:
user
)
}
describe
'#show_data'
do
let
(
:user
)
{
create
(
:user
)
}
let
(
:group
)
{
create
(
:group
)
}
let
(
:milestone1
)
{
create
(
:milestone
,
title:
'make me a sandwich'
,
start_date:
'2010-01-01'
,
due_date:
'2019-12-31'
)
}
let
(
:milestone2
)
{
create
(
:milestone
,
title:
'make me a pizza'
,
start_date:
'2020-01-01'
,
due_date:
'2029-12-31'
)
}
let
(
:parent_epic
)
{
create
(
:epic
,
group:
group
,
start_date:
Date
.
new
(
2000
,
1
,
10
),
due_date:
Date
.
new
(
2000
,
1
,
20
))
}
let
(
:epic
)
do
create
(
:epic
,
group:
group
,
author:
user
,
start_date_sourcing_milestone:
milestone1
,
start_date:
Date
.
new
(
2000
,
1
,
1
),
due_date_sourcing_milestone:
milestone2
,
due_date:
Date
.
new
(
2000
,
1
,
2
),
parent:
parent_epic
)
end
let
(
:presenter
)
{
described_class
.
new
(
epic
,
current_user:
user
)
}
before
do
epic
.
update
(
start_date_sourcing_milestone:
milestone1
,
start_date:
Date
.
new
(
2000
,
1
,
1
),
due_date_sourcing_milestone:
milestone2
,
due_date:
Date
.
new
(
2000
,
1
,
2
)
)
stub_licensed_features
(
epics:
true
)
end
...
...
@@ -59,4 +53,32 @@ describe EpicPresenter do
expect
{
presenter
.
show_data
}.
not_to
exceed_query_limit
(
control_count
)
end
end
describe
'#group_epic_path'
do
it
'returns correct path'
do
expect
(
presenter
.
group_epic_path
).
to
eq
group_epic_path
(
epic
.
group
,
epic
)
end
end
describe
'#group_epic_link_path'
do
it
'returns correct path'
do
expect
(
presenter
.
group_epic_link_path
).
to
eq
group_epic_link_path
(
epic
.
group
,
epic
.
parent
.
iid
,
epic
.
id
)
end
it
'returns nothing with nil parent'
do
epic
.
parent
=
nil
expect
(
presenter
.
group_epic_link_path
).
to
be_nil
end
end
describe
'#epic_reference'
do
it
'returns a reference'
do
expect
(
presenter
.
epic_reference
).
to
eq
"&
#{
epic
.
iid
}
"
end
it
'returns a full reference'
do
expect
(
presenter
.
epic_reference
(
full:
true
)).
to
eq
"
#{
epic
.
parent
.
group
.
name
}
&
#{
epic
.
iid
}
"
end
end
end
ee/spec/requests/api/graphql/group/epics_spec.rb
0 → 100644
View file @
aefa6568
# frozen_string_literal: true
require
'spec_helper'
# Based on ee/spec/requests/api/epics_spec.rb
# Should follow closely in order to ensure all situations are covered
describe
'Epics through GroupQuery'
do
include
GraphqlHelpers
let
(
:user
)
{
create
(
:user
)
}
let
(
:group
)
{
create
(
:group
)
}
let
(
:project
)
{
create
(
:project
,
:public
,
group:
group
)
}
let
(
:label
)
{
create
(
:label
)
}
let
(
:epic
)
{
create
(
:labeled_epic
,
group:
group
,
labels:
[
label
])
}
let
(
:epics_data
)
{
graphql_data
[
'group'
][
'epics'
][
'edges'
]
}
let
(
:epic_data
)
{
graphql_data
[
'group'
][
'epic'
]
}
# similar to GET /groups/:id/epics
describe
'Get list of epics from a group'
do
let
(
:query
)
do
epic_node
=
<<~
NODE
edges {
node {
id
iid
title
userPermissions {
adminEpic
}
}
}
NODE
graphql_query_for
(
"group"
,
{
"fullPath"
=>
group
.
full_path
},
[
'epicsEnabled'
,
query_graphql_field
(
"epics"
,
{},
epic_node
)]
)
end
context
'when the request is correct'
do
before
do
stub_licensed_features
(
epics:
true
)
epic
&&
group
.
reload
post_graphql
(
query
,
current_user:
user
)
end
it_behaves_like
'a working graphql query'
it
'returns epics successfully'
do
expect
(
response
).
to
have_gitlab_http_status
(
200
)
expect
(
graphql_errors
).
to
be_nil
expect
(
node_array
(
'id'
).
first
).
to
eq
epic
.
id
.
to_s
expect
(
graphql_data
[
'group'
][
'epicsEnabled'
]).
to
be_truthy
end
end
context
'with multiple epics'
do
let
(
:user2
)
{
create
(
:user
)
}
let!
(
:epic
)
{
create
(
:epic
,
group:
group
,
state: :closed
,
created_at:
3
.
days
.
ago
,
updated_at:
2
.
days
.
ago
)
}
let!
(
:epic2
)
{
create
(
:epic
,
author:
user2
,
group:
group
,
title:
'foo'
,
description:
'bar'
,
created_at:
2
.
days
.
ago
,
updated_at:
3
.
days
.
ago
)
}
before
do
stub_licensed_features
(
epics:
true
)
end
it
'sorts by created_at descending by default'
do
post_graphql
(
query
,
current_user:
user
)
expect_array_response
([
epic2
.
id
,
epic
.
id
])
end
describe
'can admin epics'
do
context
'when permission is absent'
do
it
'returns false for adminEpic'
do
post_graphql
(
query
,
current_user:
user
)
expect
(
node_array
(
'userPermissions'
)).
to
all
(
include
(
'adminEpic'
=>
false
))
end
end
context
'when permission is present'
do
before
do
group
.
add_maintainer
(
user
)
end
it
'returns true for adminEpic'
do
post_graphql
(
query
,
current_user:
user
)
expect
(
node_array
(
'userPermissions'
)).
to
all
(
include
(
'adminEpic'
=>
true
))
end
end
end
end
context
'when error requests'
do
context
'when epics feature is disabled'
do
it
'returns empty'
do
group
.
add_developer
(
user
)
post_graphql
(
query
,
current_user:
user
)
expect
(
response
).
to
have_gitlab_http_status
(
:success
)
expect
(
graphql_errors
).
to
be_nil
expect
(
epics_data
).
to
be_empty
expect
(
graphql_data
[
'group'
][
'epicsEnabled'
]).
to
be_falsey
end
context
'when epics feature is enabled'
do
before
do
stub_licensed_features
(
epics:
true
)
end
it
'returns a nil group for a user without permissions to see the group'
do
project
.
update
(
visibility_level:
Gitlab
::
VisibilityLevel
::
PRIVATE
)
group
.
update
(
visibility_level:
Gitlab
::
VisibilityLevel
::
PRIVATE
)
post_graphql
(
query
,
current_user:
user
)
expect
(
response
).
to
have_gitlab_http_status
(
:success
)
expect
(
graphql_errors
).
to
be_nil
expect
(
graphql_data
[
'group'
]).
to
be_nil
end
end
end
end
end
# similar to 'GET /groups/:id/epics/:epic_iid'
describe
'Get epic from a group'
do
let
(
:query
)
do
graphql_query_for
(
'group'
,
{
'fullPath'
=>
group
.
full_path
},
[
'epicsEnabled'
,
query_graphql_field
(
'epic'
,
{
iid:
epic
.
iid
})]
)
end
context
'when the request is correct'
do
before
do
stub_licensed_features
(
epics:
true
)
post_graphql
(
query
)
end
it_behaves_like
'a working graphql query'
it
'returns an epic successfully'
do
expect
(
response
).
to
have_gitlab_http_status
(
:success
)
expect
(
graphql_errors
).
to
be_nil
expect
(
epic_data
[
'id'
]).
to
eq
epic
.
id
.
to_s
expect
(
graphql_data
[
'group'
][
'epicsEnabled'
]).
to
be_truthy
end
end
end
def
expect_array_response
(
items
)
expect
(
response
).
to
have_gitlab_http_status
(
:success
)
expect
(
epics_data
).
to
be_an
Array
expect
(
node_array
(
'id'
).
map
(
&
:to_i
)).
to
eq
(
Array
(
items
))
end
def
node_array
(
extract_attribute
=
nil
)
epics_data
.
map
do
|
item
|
extract_attribute
?
item
[
'node'
][
extract_attribute
]
:
item
[
'node'
]
end
end
end
spec/graphql/types/base_field_spec.rb
View file @
aefa6568
...
...
@@ -6,7 +6,7 @@ describe Types::BaseField do
context
'when considering complexity'
do
let
(
:resolver
)
do
Class
.
new
(
described_class
)
do
def
self
.
resolver_complexity
(
args
)
def
self
.
resolver_complexity
(
args
,
child_complexity
:
)
2
if
args
[
:foo
]
end
...
...
spec/graphql/types/issue_type_spec.rb
View file @
aefa6568
...
...
@@ -6,4 +6,10 @@ describe GitlabSchema.types['Issue'] do
it
{
expect
(
described_class
.
graphql_name
).
to
eq
(
'Issue'
)
}
it
{
expect
(
described_class
).
to
require_graphql_authorizations
(
:read_issue
)
}
it
'has specific fields'
do
%i[relative_position web_path web_url reference]
.
each
do
|
field_name
|
expect
(
described_class
).
to
have_graphql_field
(
field_name
)
end
end
end
spec/presenters/issue_presenter_spec.rb
0 → 100644
View file @
aefa6568
# frozen_string_literal: true
require
'spec_helper'
describe
IssuePresenter
do
include
Gitlab
::
Routing
.
url_helpers
let
(
:user
)
{
create
(
:user
)
}
let
(
:group
)
{
create
(
:group
)
}
let
(
:project
)
{
create
(
:project
,
group:
group
)
}
let
(
:issue
)
{
create
(
:issue
,
project:
project
)
}
let
(
:presenter
)
{
described_class
.
new
(
issue
,
current_user:
user
)
}
before
do
group
.
add_developer
(
user
)
end
describe
'#web_url'
do
it
'returns correct path'
do
expect
(
presenter
.
web_url
).
to
eq
"http://localhost/
#{
group
.
name
}
/
#{
project
.
name
}
/issues/
#{
issue
.
iid
}
"
end
end
describe
'#issue_path'
do
it
'returns correct path'
do
expect
(
presenter
.
issue_path
).
to
eq
"/
#{
group
.
name
}
/
#{
project
.
name
}
/issues/
#{
issue
.
iid
}
"
end
end
end
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