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
d6f55cb6
Commit
d6f55cb6
authored
Jan 03, 2018
by
Jarka Kadlecová
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add api for epic_issue associations
parent
627a8882
Changes
11
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
811 additions
and
92 deletions
+811
-92
changelogs/unreleased-ee/4250-api-epic-issues.yml
changelogs/unreleased-ee/4250-api-epic-issues.yml
+5
-0
doc/api/README.md
doc/api/README.md
+1
-0
doc/api/epic_issues.md
doc/api/epic_issues.md
+314
-0
ee/lib/api/epic_issues.rb
ee/lib/api/epic_issues.rb
+93
-0
lib/api/api.rb
lib/api/api.rb
+1
-0
lib/api/entities.rb
lib/api/entities.rb
+20
-0
spec/ee/fixtures/api/schemas/public_api/v4/epic_issue_link.json
...e/fixtures/api/schemas/public_api/v4/epic_issue_link.json
+37
-0
spec/ee/fixtures/api/schemas/public_api/v4/epic_issues.json
spec/ee/fixtures/api/schemas/public_api/v4/epic_issues.json
+11
-0
spec/ee/spec/requests/api/epic_issues_spec.rb
spec/ee/spec/requests/api/epic_issues_spec.rb
+231
-0
spec/fixtures/api/schemas/public_api/v4/issue.json
spec/fixtures/api/schemas/public_api/v4/issue.json
+96
-0
spec/fixtures/api/schemas/public_api/v4/issues.json
spec/fixtures/api/schemas/public_api/v4/issues.json
+2
-92
No files found.
changelogs/unreleased-ee/4250-api-epic-issues.yml
0 → 100644
View file @
d6f55cb6
---
title
:
Add api for epic_issue associations
merge_request
:
author
:
type
:
added
doc/api/README.md
View file @
d6f55cb6
...
...
@@ -18,6 +18,7 @@ following locations:
-
[
Deployments
](
deployments.md
)
-
[
Deploy Keys
](
deploy_keys.md
)
-
[
Environments
](
environments.md
)
-
[
Epic Issues
](
epic_issues.md
)
-
[
Events
](
events.md
)
-
[
Feature flags
](
features.md
)
-
[
Geo Nodes
](
geo_nodes.md
)
...
...
doc/api/epic_issues.md
0 → 100644
View file @
d6f55cb6
This diff is collapsed.
Click to expand it.
ee/lib/api/epic_issues.rb
0 → 100644
View file @
d6f55cb6
module
API
class
EpicIssues
<
Grape
::
API
before
do
authenticate!
authorize_epics_feature!
end
helpers
do
def
authorize_epics_feature!
forbidden!
unless
user_group
.
feature_available?
(
:epics
)
end
def
authorize_can_read!
authorize!
(
:read_epic
,
epic
)
end
def
authorize_can_admin!
authorize!
(
:admin_epic
,
epic
)
end
def
epic
@epic
||=
user_group
.
epics
.
find_by
(
iid:
params
[
:epic_iid
])
end
def
link
@link
||=
epic
.
epic_issues
.
find
(
params
[
:epic_issue_id
])
end
end
params
do
requires
:id
,
type:
String
,
desc:
'The ID of a group'
end
resource
:groups
,
requirements:
API
::
PROJECT_ENDPOINT_REQUIREMENTS
do
desc
'Get issues assigned to the epic'
do
success
Entities
::
EpicIssue
end
params
do
requires
:epic_iid
,
type:
Integer
,
desc:
'The iid of the epic'
end
get
':id/-/epics/:epic_iid/issues'
do
authorize_can_read!
present
epic
.
issues
(
current_user
),
with:
Entities
::
EpicIssue
,
current_user:
current_user
end
desc
'Assign an issue to the epic'
do
success
Entities
::
EpicIssueLink
end
params
do
requires
:epic_iid
,
type:
Integer
,
desc:
'The iid of the epic'
end
post
':id/-/epics/:epic_iid/issues/:issue_id'
do
authorize_can_admin!
issue
=
Issue
.
find
(
params
[
:issue_id
])
create_params
=
{
target_issue:
issue
}
result
=
::
EpicIssues
::
CreateService
.
new
(
epic
,
current_user
,
create_params
).
execute
if
result
[
:status
]
==
:success
epic_issue_link
=
EpicIssue
.
find_by!
(
epic:
epic
,
issue:
issue
)
present
epic_issue_link
,
with:
Entities
::
EpicIssueLink
else
render_api_error!
(
result
[
:message
],
result
[
:http_status
])
end
end
desc
'Remove an issue from the epic'
do
success
Entities
::
EpicIssueLink
end
params
do
requires
:epic_iid
,
type:
Integer
,
desc:
'The iid of the epic'
requires
:epic_issue_id
,
type:
Integer
,
desc:
'The id of the association'
end
delete
':id/-/epics/:epic_iid/issues/:epic_issue_id'
do
authorize_can_admin!
result
=
::
EpicIssues
::
DestroyService
.
new
(
link
,
current_user
).
execute
if
result
[
:status
]
==
:success
present
link
,
with:
Entities
::
EpicIssueLink
else
render_api_error!
(
result
[
:message
],
result
[
:http_status
])
end
end
end
end
end
lib/api/api.rb
View file @
d6f55cb6
...
...
@@ -124,6 +124,7 @@ module API
mount
::
API
::
DeployKeys
mount
::
API
::
Deployments
mount
::
API
::
Environments
mount
::
API
::
EpicIssues
mount
::
API
::
Events
mount
::
API
::
Features
mount
::
API
::
Files
...
...
lib/api/entities.rb
View file @
d6f55cb6
...
...
@@ -491,6 +491,26 @@ module API
expose
:issue_link_id
end
class
Epic
<
Grape
::
Entity
expose
:id
expose
:iid
expose
:title
expose
:description
expose
:author
,
using:
Entities
::
UserBasic
expose
:start_date
expose
:end_date
end
class
EpicIssue
<
Issue
expose
:epic_issue_id
end
class
EpicIssueLink
<
Grape
::
Entity
expose
:id
expose
:epic
,
using:
Entities
::
Epic
expose
:issue
,
using:
Entities
::
IssueBasic
end
class
IssueLink
<
Grape
::
Entity
expose
:source
,
as: :source_issue
,
using:
Entities
::
IssueBasic
expose
:target
,
as: :target_issue
,
using:
Entities
::
IssueBasic
...
...
spec/ee/fixtures/api/schemas/public_api/v4/epic_issue_link.json
0 → 100644
View file @
d6f55cb6
{
"type"
:
"object"
,
"properties"
:
{
"id"
:
{
"type"
:
"integer"
},
"issue"
:
{
"type"
:
"object"
},
"epic"
:
{
"type"
:
"object"
,
"required"
:
[
"id"
,
"iid"
,
"title"
],
"properties"
:
{
"id"
:
{
"type"
:
"integer"
},
"iid"
:
{
"type"
:
"integer"
},
"title"
:
{
"type"
:
"string"
},
"description"
:
{
"type"
:
[
"string"
,
"null"
]
},
"author"
:
{
"type"
:
[
"object"
,
"null"
]
},
"start_date"
:
{
"type"
:
[
"string"
,
"null"
]
},
"end_date"
:
{
"type"
:
[
"string"
,
"null"
]
}
},
"additionalProperties"
:
false
},
"issue"
:
{
"allOf"
:
[
{
"$ref"
:
"../../../../../../fixtures/api/schemas/public_api/v4/issue.json"
},
{
"properties"
:
{
"weight"
:
{
"type"
:
[
"integer"
,
"null"
]
}
}
}
]
}
},
"required"
:
[
"id"
,
"epic"
,
"issue"
],
"additionalProperties"
:
false
}
spec/ee/fixtures/api/schemas/public_api/v4/epic_issues.json
0 → 100644
View file @
d6f55cb6
{
"allOf"
:
[
{
"$ref"
:
"./issues.json"
},
{
"properties"
:
{
"issue_link_id"
:
{
"type"
:
[
"integer"
,
"null"
]
},
"position"
:
{
"type"
:
[
"integer"
,
"null"
]
}
}
}
]
}
spec/ee/spec/requests/api/epic_issues_spec.rb
0 → 100644
View file @
d6f55cb6
require
'spec_helper'
describe
API
::
EpicIssues
do
let
(
:user
)
{
create
(
:user
)
}
let
(
:group
)
{
create
(
:group
)
}
let
(
:project
)
{
create
(
:project
,
:public
,
group:
group
)
}
let
(
:epic
)
{
create
(
:epic
,
group:
group
)
}
describe
'GET /groups/:id/-/epics/:epic_iid/issues'
do
let
(
:url
)
{
"/groups/
#{
group
.
path
}
/-/epics/
#{
epic
.
iid
}
/issues"
}
context
'when epics feature is disabled'
do
it
'returns 403 forbidden error'
do
group
.
add_developer
(
user
)
get
api
(
url
,
user
)
expect
(
response
).
to
have_gitlab_http_status
(
403
)
end
end
context
'when epics feature is enabled'
do
before
do
stub_licensed_features
(
epics:
true
)
end
context
'when an error occurs'
do
it
'returns 401 unauthorized error for non authenticated user'
do
get
api
(
url
)
expect
(
response
).
to
have_gitlab_http_status
(
401
)
end
it
'returns 404 not found error for a user without permissions to see the group'
do
project
.
update
(
visibility_level:
Gitlab
::
VisibilityLevel
::
PRIVATE
)
group
.
update
(
visibility_level:
Gitlab
::
VisibilityLevel
::
PRIVATE
)
get
api
(
url
,
user
)
expect
(
response
).
to
have_gitlab_http_status
(
404
)
end
end
context
'when the request is correct'
do
let
(
:issues
)
{
create_list
(
:issue
,
2
,
project:
project
)
}
let!
(
:epic_issue1
)
{
create
(
:epic_issue
,
epic:
epic
,
issue:
issues
[
0
])
}
let!
(
:epic_issue2
)
{
create
(
:epic_issue
,
epic:
epic
,
issue:
issues
[
1
])
}
before
do
get
api
(
url
,
user
)
end
it
'returns 200 status'
do
expect
(
response
).
to
have_gitlab_http_status
(
200
)
end
it
'matches the response schema'
do
expect
(
response
).
to
match_response_schema
(
'public_api/v4/epic_issues'
,
dir:
'ee'
)
end
end
end
end
describe
'POST /groups/:id/-/epics/:epic_iid/issues'
do
let
(
:issue
)
{
create
(
:issue
,
project:
project
)
}
let
(
:url
)
{
"/groups/
#{
group
.
path
}
/-/epics/
#{
epic
.
iid
}
/issues/
#{
issue
.
id
}
"
}
context
'when epics feature is disabled'
do
it
'returns 403 forbidden error'
do
group
.
add_developer
(
user
)
post
api
(
url
,
user
)
expect
(
response
).
to
have_gitlab_http_status
(
403
)
end
end
context
'when epics feature is enabled'
do
before
do
stub_licensed_features
(
epics:
true
)
end
context
'when an error occurs'
do
it
'returns 401 unauthorized error for non authenticated user'
do
post
api
(
url
)
expect
(
response
).
to
have_gitlab_http_status
(
401
)
end
it
'returns 404 not found error 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
api
(
url
,
user
)
expect
(
response
).
to
have_gitlab_http_status
(
404
)
end
it
'returns 403 forbidden error for a user without permissions to admin the epic'
do
post
api
(
url
,
user
)
expect
(
response
).
to
have_gitlab_http_status
(
403
)
end
context
'when issue project is not under the epic group'
do
before
do
other_project
=
create
(
:project
)
issue
.
update_attribute
(
:project
,
other_project
)
group
.
add_developer
(
user
)
other_project
.
add_developer
(
user
)
end
it
'returns an error'
do
post
api
(
url
,
user
)
expect
(
response
).
to
have_gitlab_http_status
(
404
)
expect
(
json_response
).
to
eq
(
'message'
=>
'No Issue found for given params'
)
end
end
end
context
'when the request is correct'
do
before
do
group
.
add_developer
(
user
)
post
api
(
url
,
user
)
end
it
'returns 201 status'
do
expect
(
response
).
to
have_gitlab_http_status
(
201
)
end
it
'matches the response schema'
do
expect
(
response
).
to
match_response_schema
(
'public_api/v4/epic_issue_link'
,
dir:
'ee'
)
end
it
'assigns the issue to the epic'
do
epic_issue
=
EpicIssue
.
last
expect
(
epic_issue
.
issue
).
to
eq
(
issue
)
expect
(
epic_issue
.
epic
).
to
eq
(
epic
)
end
end
end
end
describe
'DELETE /groups/:id/-/epics/:epic_iid/issues/:epic_issue_id"'
do
let
(
:issue
)
{
create
(
:issue
,
project:
project
)
}
let!
(
:epic_issue
)
{
create
(
:epic_issue
,
epic:
epic
,
issue:
issue
)
}
let
(
:url
)
{
"/groups/
#{
group
.
path
}
/-/epics/
#{
epic
.
iid
}
/issues/
#{
epic_issue
.
id
}
"
}
context
'when epics feature is disabled'
do
it
'returns 403 forbidden error'
do
group
.
add_developer
(
user
)
post
api
(
url
,
user
)
expect
(
response
).
to
have_gitlab_http_status
(
403
)
end
end
context
'when epics feature is enabled'
do
before
do
stub_licensed_features
(
epics:
true
)
end
context
'when an error occurs'
do
it
'returns 401 unauthorized error for non authenticated user'
do
delete
api
(
url
)
expect
(
response
).
to
have_gitlab_http_status
(
401
)
end
it
'returns 404 not found error for a user without permissions to see the group'
do
project
.
update
(
visibility_level:
Gitlab
::
VisibilityLevel
::
PRIVATE
)
group
.
update
(
visibility_level:
Gitlab
::
VisibilityLevel
::
PRIVATE
)
delete
api
(
url
,
user
)
expect
(
response
).
to
have_gitlab_http_status
(
404
)
end
it
'returns 403 forbidden error for a user without permissions to admin the epic'
do
delete
api
(
url
,
user
)
expect
(
response
).
to
have_gitlab_http_status
(
403
)
end
context
'when epic_issue association does not include the epic in the url'
do
before
do
other_group
=
create
(
:group
)
other_group_epic
=
create
(
:epic
,
group:
other_group
)
epic_issue
.
update_attribute
(
:epic
,
other_group_epic
)
group
.
add_developer
(
user
)
other_group
.
add_developer
(
user
)
end
it
'returns 404 not found error'
do
delete
api
(
url
,
user
)
expect
(
response
).
to
have_gitlab_http_status
(
404
)
end
end
end
context
'when the request is correct'
do
before
do
group
.
add_developer
(
user
)
end
it
'returns 200 status'
do
delete
api
(
url
,
user
)
expect
(
response
).
to
have_gitlab_http_status
(
200
)
end
it
'matches the response schema'
do
delete
api
(
url
,
user
)
expect
(
response
).
to
match_response_schema
(
'public_api/v4/epic_issue_link'
,
dir:
'ee'
)
end
it
'removes the association'
do
expect
{
delete
api
(
url
,
user
)
}.
to
change
{
EpicIssue
.
count
}.
from
(
1
).
to
(
0
)
end
end
end
end
end
spec/fixtures/api/schemas/public_api/v4/issue.json
0 → 100644
View file @
d6f55cb6
{
"type"
:
"object"
,
"properties"
:
{
"id"
:
{
"type"
:
"integer"
},
"iid"
:
{
"type"
:
"integer"
},
"project_id"
:
{
"type"
:
"integer"
},
"title"
:
{
"type"
:
"string"
},
"description"
:
{
"type"
:
[
"string"
,
"null"
]
},
"state"
:
{
"type"
:
"string"
},
"discussion_locked"
:
{
"type"
:
[
"boolean"
,
"null"
]
},
"closed_at"
:
{
"type"
:
"date"
},
"created_at"
:
{
"type"
:
"date"
},
"updated_at"
:
{
"type"
:
"date"
},
"labels"
:
{
"type"
:
"array"
,
"items"
:
{
"type"
:
"string"
}
},
"milestone"
:
{
"type"
:
[
"object"
,
"null"
],
"properties"
:
{
"id"
:
{
"type"
:
"integer"
},
"iid"
:
{
"type"
:
"integer"
},
"project_id"
:
{
"type"
:
[
"integer"
,
"null"
]
},
"group_id"
:
{
"type"
:
[
"integer"
,
"null"
]
},
"title"
:
{
"type"
:
"string"
},
"description"
:
{
"type"
:
[
"string"
,
"null"
]
},
"state"
:
{
"type"
:
"string"
},
"created_at"
:
{
"type"
:
"date"
},
"updated_at"
:
{
"type"
:
"date"
},
"due_date"
:
{
"type"
:
"date"
},
"start_date"
:
{
"type"
:
"date"
}
},
"additionalProperties"
:
false
},
"assignees"
:
{
"type"
:
"array"
,
"items"
:
{
"type"
:
[
"object"
,
"null"
],
"properties"
:
{
"name"
:
{
"type"
:
"string"
},
"username"
:
{
"type"
:
"string"
},
"id"
:
{
"type"
:
"integer"
},
"state"
:
{
"type"
:
"string"
},
"avatar_url"
:
{
"type"
:
"uri"
},
"web_url"
:
{
"type"
:
"uri"
}
},
"additionalProperties"
:
false
}
},
"assignee"
:
{
"type"
:
[
"object"
,
"null"
],
"properties"
:
{
"name"
:
{
"type"
:
"string"
},
"username"
:
{
"type"
:
"string"
},
"id"
:
{
"type"
:
"integer"
},
"state"
:
{
"type"
:
"string"
},
"avatar_url"
:
{
"type"
:
"uri"
},
"web_url"
:
{
"type"
:
"uri"
}
},
"additionalProperties"
:
false
},
"author"
:
{
"type"
:
"object"
,
"properties"
:
{
"name"
:
{
"type"
:
"string"
},
"username"
:
{
"type"
:
"string"
},
"id"
:
{
"type"
:
"integer"
},
"state"
:
{
"type"
:
"string"
},
"avatar_url"
:
{
"type"
:
"uri"
},
"web_url"
:
{
"type"
:
"uri"
}
},
"additionalProperties"
:
false
},
"user_notes_count"
:
{
"type"
:
"integer"
},
"upvotes"
:
{
"type"
:
"integer"
},
"downvotes"
:
{
"type"
:
"integer"
},
"due_date"
:
{
"type"
:
[
"date"
,
"null"
]
},
"confidential"
:
{
"type"
:
"boolean"
},
"web_url"
:
{
"type"
:
"uri"
},
"time_stats"
:
{
"time_estimate"
:
{
"type"
:
"integer"
},
"total_time_spent"
:
{
"type"
:
"integer"
},
"human_time_estimate"
:
{
"type"
:
[
"string"
,
"null"
]
},
"human_total_time_spent"
:
{
"type"
:
[
"string"
,
"null"
]
}
}
},
"required"
:
[
"id"
,
"iid"
,
"project_id"
,
"title"
,
"description"
,
"state"
,
"created_at"
,
"updated_at"
,
"labels"
,
"milestone"
,
"assignees"
,
"author"
,
"user_notes_count"
,
"upvotes"
,
"downvotes"
,
"due_date"
,
"confidential"
,
"web_url"
]
}
spec/fixtures/api/schemas/public_api/v4/issues.json
View file @
d6f55cb6
...
...
@@ -3,97 +3,7 @@
"items"
:
{
"type"
:
"object"
,
"properties"
:
{
"id"
:
{
"type"
:
"integer"
},
"iid"
:
{
"type"
:
"integer"
},
"project_id"
:
{
"type"
:
"integer"
},
"title"
:
{
"type"
:
"string"
},
"description"
:
{
"type"
:
[
"string"
,
"null"
]
},
"state"
:
{
"type"
:
"string"
},
"discussion_locked"
:
{
"type"
:
[
"boolean"
,
"null"
]
},
"closed_at"
:
{
"type"
:
"date"
},
"created_at"
:
{
"type"
:
"date"
},
"updated_at"
:
{
"type"
:
"date"
},
"labels"
:
{
"type"
:
"array"
,
"items"
:
{
"type"
:
"string"
}
},
"milestone"
:
{
"type"
:
"object"
,
"properties"
:
{
"id"
:
{
"type"
:
"integer"
},
"iid"
:
{
"type"
:
"integer"
},
"project_id"
:
{
"type"
:
[
"integer"
,
"null"
]
},
"group_id"
:
{
"type"
:
[
"integer"
,
"null"
]
},
"title"
:
{
"type"
:
"string"
},
"description"
:
{
"type"
:
[
"string"
,
"null"
]
},
"state"
:
{
"type"
:
"string"
},
"created_at"
:
{
"type"
:
"date"
},
"updated_at"
:
{
"type"
:
"date"
},
"due_date"
:
{
"type"
:
"date"
},
"start_date"
:
{
"type"
:
"date"
}
},
"additionalProperties"
:
false
},
"assignees"
:
{
"type"
:
"array"
,
"items"
:
{
"type"
:
[
"object"
,
"null"
],
"properties"
:
{
"name"
:
{
"type"
:
"string"
},
"username"
:
{
"type"
:
"string"
},
"id"
:
{
"type"
:
"integer"
},
"state"
:
{
"type"
:
"string"
},
"avatar_url"
:
{
"type"
:
"uri"
},
"web_url"
:
{
"type"
:
"uri"
}
},
"additionalProperties"
:
false
}
},
"assignee"
:
{
"type"
:
[
"object"
,
"null"
],
"properties"
:
{
"name"
:
{
"type"
:
"string"
},
"username"
:
{
"type"
:
"string"
},
"id"
:
{
"type"
:
"integer"
},
"state"
:
{
"type"
:
"string"
},
"avatar_url"
:
{
"type"
:
"uri"
},
"web_url"
:
{
"type"
:
"uri"
}
},
"additionalProperties"
:
false
},
"author"
:
{
"type"
:
"object"
,
"properties"
:
{
"name"
:
{
"type"
:
"string"
},
"username"
:
{
"type"
:
"string"
},
"id"
:
{
"type"
:
"integer"
},
"state"
:
{
"type"
:
"string"
},
"avatar_url"
:
{
"type"
:
"uri"
},
"web_url"
:
{
"type"
:
"uri"
}
},
"additionalProperties"
:
false
},
"user_notes_count"
:
{
"type"
:
"integer"
},
"upvotes"
:
{
"type"
:
"integer"
},
"downvotes"
:
{
"type"
:
"integer"
},
"due_date"
:
{
"type"
:
[
"date"
,
"null"
]
},
"confidential"
:
{
"type"
:
"boolean"
},
"web_url"
:
{
"type"
:
"uri"
},
"time_stats"
:
{
"time_estimate"
:
{
"type"
:
"integer"
},
"total_time_spent"
:
{
"type"
:
"integer"
},
"human_time_estimate"
:
{
"type"
:
[
"string"
,
"null"
]
},
"human_total_time_spent"
:
{
"type"
:
[
"string"
,
"null"
]
}
}
},
"required"
:
[
"id"
,
"iid"
,
"project_id"
,
"title"
,
"description"
,
"state"
,
"created_at"
,
"updated_at"
,
"labels"
,
"milestone"
,
"assignees"
,
"author"
,
"user_notes_count"
,
"upvotes"
,
"downvotes"
,
"due_date"
,
"confidential"
,
"web_url"
]
"$ref"
:
"./issue.json"
}
}
}
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