Commit b8a83a15 authored by Jan Provaznik's avatar Jan Provaznik

Merge branch 'epic_links' into 'master'

Add _links to epics API

Closes #230586

See merge request gitlab-org/gitlab!37395
parents 0489d9c0 298aa612
...@@ -92,7 +92,7 @@ Example response: ...@@ -92,7 +92,7 @@ Example response:
"description": "Molestias dolorem eos vitae expedita impedit necessitatibus quo voluptatum.", "description": "Molestias dolorem eos vitae expedita impedit necessitatibus quo voluptatum.",
"state": "opened", "state": "opened",
"confidential": "false", "confidential": "false",
"web_url": "http://localhost:3001/groups/test/-/epics/4", "web_url": "http://gitlab.example.com/groups/test/-/epics/4",
"reference": "&4", "reference": "&4",
"references": { "references": {
"short": "&4", "short": "&4",
...@@ -105,7 +105,7 @@ Example response: ...@@ -105,7 +105,7 @@ Example response:
"username": "kam", "username": "kam",
"state": "active", "state": "active",
"avatar_url": "http://www.gravatar.com/avatar/018729e129a6f31c80a6327a30196823?s=80&d=identicon", "avatar_url": "http://www.gravatar.com/avatar/018729e129a6f31c80a6327a30196823?s=80&d=identicon",
"web_url": "http://localhost:3001/kam" "web_url": "http://gitlab.example.com/kam"
}, },
"start_date": null, "start_date": null,
"start_date_is_fixed": false, "start_date_is_fixed": false,
...@@ -123,7 +123,12 @@ Example response: ...@@ -123,7 +123,12 @@ Example response:
"closed_at": "2018-08-18T12:22:05.239Z", "closed_at": "2018-08-18T12:22:05.239Z",
"labels": [], "labels": [],
"upvotes": 4, "upvotes": 4,
"downvotes": 0 "downvotes": 0,
"_links":{
"self": "http://gitlab.example.com/api/v4/groups/7/epics/4",
"epic_issues": "http://gitlab.example.com/api/v4/groups/7/epics/4/issues",
"group":"http://gitlab.example.com/api/v4/groups/7"
}
}, },
{ {
"id": 50, "id": 50,
...@@ -133,7 +138,7 @@ Example response: ...@@ -133,7 +138,7 @@ Example response:
"title": "Accusamus iste et ullam ratione voluptatem omnis debitis dolor est.", "title": "Accusamus iste et ullam ratione voluptatem omnis debitis dolor est.",
"description": "Molestias dolorem eos vitae expedita impedit necessitatibus quo voluptatum.", "description": "Molestias dolorem eos vitae expedita impedit necessitatibus quo voluptatum.",
"state": "opened", "state": "opened",
"web_url": "http://localhost:3001/groups/test/sample/-/epics/4", "web_url": "http://gitlab.example.com/groups/test/sample/-/epics/35",
"reference": "&4", "reference": "&4",
"references": { "references": {
"short": "&4", "short": "&4",
...@@ -146,7 +151,7 @@ Example response: ...@@ -146,7 +151,7 @@ Example response:
"username": "kam", "username": "kam",
"state": "active", "state": "active",
"avatar_url": "http://www.gravatar.com/avatar/018729e129a6f31c80a6327a30196823?s=80&d=identicon", "avatar_url": "http://www.gravatar.com/avatar/018729e129a6f31c80a6327a30196823?s=80&d=identicon",
"web_url": "http://localhost:3001/kam" "web_url": "http://gitlab.example.com/kam"
}, },
"start_date": null, "start_date": null,
"start_date_is_fixed": false, "start_date_is_fixed": false,
...@@ -164,7 +169,12 @@ Example response: ...@@ -164,7 +169,12 @@ Example response:
"closed_at": "2018-08-18T12:22:05.239Z", "closed_at": "2018-08-18T12:22:05.239Z",
"labels": [], "labels": [],
"upvotes": 4, "upvotes": 4,
"downvotes": 0 "downvotes": 0,
"_links":{
"self": "http://gitlab.example.com/api/v4/groups/17/epics/35",
"epic_issues": "http://gitlab.example.com/api/v4/groups/17/epics/35/issues",
"group":"http://gitlab.example.com/api/v4/groups/17"
}
} }
] ]
``` ```
...@@ -196,7 +206,7 @@ Example response: ...@@ -196,7 +206,7 @@ Example response:
"title": "Ea cupiditate dolores ut vero consequatur quasi veniam voluptatem et non.", "title": "Ea cupiditate dolores ut vero consequatur quasi veniam voluptatem et non.",
"description": "Molestias dolorem eos vitae expedita impedit necessitatibus quo voluptatum.", "description": "Molestias dolorem eos vitae expedita impedit necessitatibus quo voluptatum.",
"state": "opened", "state": "opened",
"web_url": "http://localhost:3001/groups/test/-/epics/5", "web_url": "http://gitlab.example.com/groups/test/-/epics/5",
"reference": "&5", "reference": "&5",
"references": { "references": {
"short": "&5", "short": "&5",
...@@ -209,7 +219,7 @@ Example response: ...@@ -209,7 +219,7 @@ Example response:
"username": "arnita", "username": "arnita",
"state": "active", "state": "active",
"avatar_url": "http://www.gravatar.com/avatar/a2f5c6fcef64c9c69cb8779cb292be1b?s=80&d=identicon", "avatar_url": "http://www.gravatar.com/avatar/a2f5c6fcef64c9c69cb8779cb292be1b?s=80&d=identicon",
"web_url": "http://localhost:3001/arnita" "web_url": "http://gitlab.example.com/arnita"
}, },
"start_date": null, "start_date": null,
"start_date_is_fixed": false, "start_date_is_fixed": false,
...@@ -228,7 +238,12 @@ Example response: ...@@ -228,7 +238,12 @@ Example response:
"labels": [], "labels": [],
"upvotes": 4, "upvotes": 4,
"downvotes": 0, "downvotes": 0,
"subscribed": true "subscribed": true,
"_links":{
"self": "http://gitlab.example.com/api/v4/groups/7/epics/5",
"epic_issues": "http://gitlab.example.com/api/v4/groups/7/epics/5/issues",
"group":"http://gitlab.example.com/api/v4/groups/7"
}
} }
``` ```
...@@ -273,7 +288,7 @@ Example response: ...@@ -273,7 +288,7 @@ Example response:
"description": "Epic description", "description": "Epic description",
"state": "opened", "state": "opened",
"confidential": "false", "confidential": "false",
"web_url": "http://localhost:3001/groups/test/-/epics/6", "web_url": "http://gitlab.example.com/groups/test/-/epics/6",
"reference": "&6", "reference": "&6",
"references": { "references": {
"short": "&6", "short": "&6",
...@@ -304,7 +319,12 @@ Example response: ...@@ -304,7 +319,12 @@ Example response:
"closed_at": "2018-08-18T12:22:05.239Z", "closed_at": "2018-08-18T12:22:05.239Z",
"labels": [], "labels": [],
"upvotes": 4, "upvotes": 4,
"downvotes": 0 "downvotes": 0,
"_links":{
"self": "http://gitlab.example.com/api/v4/groups/7/epics/6",
"epic_issues": "http://gitlab.example.com/api/v4/groups/7/epics/6/issues",
"group":"http://gitlab.example.com/api/v4/groups/7"
}
} }
``` ```
...@@ -350,7 +370,7 @@ Example response: ...@@ -350,7 +370,7 @@ Example response:
"description": "Epic description", "description": "Epic description",
"state": "opened", "state": "opened",
"confidential": "false", "confidential": "false",
"web_url": "http://localhost:3001/groups/test/-/epics/6", "web_url": "http://gitlab.example.com/groups/test/-/epics/6",
"reference": "&6", "reference": "&6",
"references": { "references": {
"short": "&6", "short": "&6",
...@@ -456,9 +476,9 @@ Example response: ...@@ -456,9 +476,9 @@ Example response:
"username": "arnita", "username": "arnita",
"state": "active", "state": "active",
"avatar_url": "http://www.gravatar.com/avatar/a2f5c6fcef64c9c69cb8779cb292be1b?s=80&d=identicon", "avatar_url": "http://www.gravatar.com/avatar/a2f5c6fcef64c9c69cb8779cb292be1b?s=80&d=identicon",
"web_url": "http://localhost:3001/arnita" "web_url": "http://gitlab.example.com/arnita"
}, },
"web_url": "http://localhost:3001/groups/test/-/epics/5", "web_url": "http://gitlab.example.com/groups/test/-/epics/5",
"reference": "&5", "reference": "&5",
"references": { "references": {
"short": "&5", "short": "&5",
......
...@@ -134,7 +134,7 @@ Example response: ...@@ -134,7 +134,7 @@ Example response:
"merge_requests_count": 0, "merge_requests_count": 0,
"user_notes_count": 1, "user_notes_count": 1,
"due_date": "2016-07-22", "due_date": "2016-07-22",
"web_url": "http://example.com/my-group/my-project/issues/6", "web_url": "http://gitlab.example.com/my-group/my-project/issues/6",
"references": { "references": {
"short": "#6", "short": "#6",
"relative": "my-group/my-project#6", "relative": "my-group/my-project#6",
...@@ -151,10 +151,10 @@ Example response: ...@@ -151,10 +151,10 @@ Example response:
"confidential": false, "confidential": false,
"discussion_locked": false, "discussion_locked": false,
"_links":{ "_links":{
"self":"http://example.com/api/v4/projects/1/issues/76", "self":"http://gitlab.example.com/api/v4/projects/1/issues/76",
"notes":"`http://example.com/`api/v4/projects/1/issues/76/notes", "notes":"http://gitlab.example.com/api/v4/projects/1/issues/76/notes",
"award_emoji":"http://example.com/api/v4/projects/1/issues/76/award_emoji", "award_emoji":"http://gitlab.example.com/api/v4/projects/1/issues/76/award_emoji",
"project":"http://example.com/api/v4/projects/1" "project":"http://gitlab.example.com/api/v4/projects/1"
}, },
"task_completion_status":{ "task_completion_status":{
"count":0, "count":0,
...@@ -292,7 +292,7 @@ Example response: ...@@ -292,7 +292,7 @@ Example response:
"closed_by" : null, "closed_by" : null,
"user_notes_count": 1, "user_notes_count": 1,
"due_date": null, "due_date": null,
"web_url": "http://example.com/my-group/my-project/issues/1", "web_url": "http://gitlab.example.com/my-group/my-project/issues/1",
"references": { "references": {
"short": "#1", "short": "#1",
"relative": "my-project#1", "relative": "my-project#1",
...@@ -309,10 +309,10 @@ Example response: ...@@ -309,10 +309,10 @@ Example response:
"confidential": false, "confidential": false,
"discussion_locked": false, "discussion_locked": false,
"_links":{ "_links":{
"self":"http://example.com/api/v4/projects/4/issues/41", "self":"http://gitlab.example.com/api/v4/projects/4/issues/41",
"notes":"`http://example.com/`api/v4/projects/4/issues/41/notes", "notes":"http://gitlab.example.com/api/v4/projects/4/issues/41/notes",
"award_emoji":"http://example.com/api/v4/projects/4/issues/41/award_emoji", "award_emoji":"http://gitlab.example.com/api/v4/projects/4/issues/41/award_emoji",
"project":"http://example.com/api/v4/projects/4" "project":"http://gitlab.example.com/api/v4/projects/4"
}, },
"task_completion_status":{ "task_completion_status":{
"count":0, "count":0,
...@@ -456,7 +456,7 @@ Example response: ...@@ -456,7 +456,7 @@ Example response:
}, },
"user_notes_count": 1, "user_notes_count": 1,
"due_date": "2016-07-22", "due_date": "2016-07-22",
"web_url": "http://example.com/my-group/my-project/issues/1", "web_url": "http://gitlab.example.com/my-group/my-project/issues/1",
"references": { "references": {
"short": "#1", "short": "#1",
"relative": "#1", "relative": "#1",
...@@ -473,10 +473,10 @@ Example response: ...@@ -473,10 +473,10 @@ Example response:
"confidential": false, "confidential": false,
"discussion_locked": false, "discussion_locked": false,
"_links":{ "_links":{
"self":"http://example.com/api/v4/projects/4/issues/41", "self":"http://gitlab.example.com/api/v4/projects/4/issues/41",
"notes":"`http://example.com/`api/v4/projects/4/issues/41/notes", "notes":"http://gitlab.example.com/api/v4/projects/4/issues/41/notes",
"award_emoji":"http://example.com/api/v4/projects/4/issues/41/award_emoji", "award_emoji":"http://gitlab.example.com/api/v4/projects/4/issues/41/award_emoji",
"project":"http://example.com/api/v4/projects/4" "project":"http://gitlab.example.com/api/v4/projects/4"
}, },
"task_completion_status":{ "task_completion_status":{
"count":0, "count":0,
...@@ -581,7 +581,7 @@ Example response: ...@@ -581,7 +581,7 @@ Example response:
"subscribed": false, "subscribed": false,
"user_notes_count": 1, "user_notes_count": 1,
"due_date": null, "due_date": null,
"web_url": "http://example.com/my-group/my-project/issues/1", "web_url": "http://gitlab.example.com/my-group/my-project/issues/1",
"references": { "references": {
"short": "#1", "short": "#1",
"relative": "#1", "relative": "#1",
...@@ -596,10 +596,10 @@ Example response: ...@@ -596,10 +596,10 @@ Example response:
"confidential": false, "confidential": false,
"discussion_locked": false, "discussion_locked": false,
"_links": { "_links": {
"self": "http://example.com/api/v4/projects/1/issues/2", "self": "http://gitlab.example.com/api/v4/projects/1/issues/2",
"notes": "http://example.com/api/v4/projects/1/issues/2/notes", "notes": "http://gitlab.example.com/api/v4/projects/1/issues/2/notes",
"award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", "award_emoji": "http://gitlab.example.com/api/v4/projects/1/issues/2/award_emoji",
"project": "http://example.com/api/v4/projects/1" "project": "http://gitlab.example.com/api/v4/projects/1"
}, },
"task_completion_status":{ "task_completion_status":{
"count":0, "count":0,
...@@ -710,7 +710,7 @@ Example response: ...@@ -710,7 +710,7 @@ Example response:
"subscribed" : true, "subscribed" : true,
"user_notes_count": 0, "user_notes_count": 0,
"due_date": null, "due_date": null,
"web_url": "http://example.com/my-group/my-project/issues/14", "web_url": "http://gitlab.example.com/my-group/my-project/issues/14",
"references": { "references": {
"short": "#14", "short": "#14",
"relative": "#14", "relative": "#14",
...@@ -725,10 +725,10 @@ Example response: ...@@ -725,10 +725,10 @@ Example response:
"confidential": false, "confidential": false,
"discussion_locked": false, "discussion_locked": false,
"_links": { "_links": {
"self": "http://example.com/api/v4/projects/1/issues/2", "self": "http://gitlab.example.com/api/v4/projects/1/issues/2",
"notes": "http://example.com/api/v4/projects/1/issues/2/notes", "notes": "http://gitlab.example.com/api/v4/projects/1/issues/2/notes",
"award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", "award_emoji": "http://gitlab.example.com/api/v4/projects/1/issues/2/award_emoji",
"project": "http://example.com/api/v4/projects/1" "project": "http://gitlab.example.com/api/v4/projects/1"
}, },
"task_completion_status":{ "task_completion_status":{
"count":0, "count":0,
...@@ -832,7 +832,7 @@ Example response: ...@@ -832,7 +832,7 @@ Example response:
"subscribed" : true, "subscribed" : true,
"user_notes_count": 0, "user_notes_count": 0,
"due_date": "2016-07-22", "due_date": "2016-07-22",
"web_url": "http://example.com/my-group/my-project/issues/15", "web_url": "http://gitlab.example.com/my-group/my-project/issues/15",
"references": { "references": {
"short": "#15", "short": "#15",
"relative": "#15", "relative": "#15",
...@@ -847,10 +847,10 @@ Example response: ...@@ -847,10 +847,10 @@ Example response:
"confidential": false, "confidential": false,
"discussion_locked": false, "discussion_locked": false,
"_links": { "_links": {
"self": "http://example.com/api/v4/projects/1/issues/2", "self": "http://gitlab.example.com/api/v4/projects/1/issues/2",
"notes": "http://example.com/api/v4/projects/1/issues/2/notes", "notes": "http://gitlab.example.com/api/v4/projects/1/issues/2/notes",
"award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", "award_emoji": "http://gitlab.example.com/api/v4/projects/1/issues/2/award_emoji",
"project": "http://example.com/api/v4/projects/1" "project": "http://gitlab.example.com/api/v4/projects/1"
}, },
"task_completion_status":{ "task_completion_status":{
"count":0, "count":0,
...@@ -985,7 +985,7 @@ Example response: ...@@ -985,7 +985,7 @@ Example response:
"web_url": "https://gitlab.example.com/solon.cremin" "web_url": "https://gitlab.example.com/solon.cremin"
}, },
"due_date": null, "due_date": null,
"web_url": "http://example.com/my-group/my-project/issues/11", "web_url": "http://gitlab.example.com/my-group/my-project/issues/11",
"references": { "references": {
"short": "#11", "short": "#11",
"relative": "#11", "relative": "#11",
...@@ -1000,10 +1000,10 @@ Example response: ...@@ -1000,10 +1000,10 @@ Example response:
"confidential": false, "confidential": false,
"discussion_locked": false, "discussion_locked": false,
"_links": { "_links": {
"self": "http://example.com/api/v4/projects/1/issues/2", "self": "http://gitlab.example.com/api/v4/projects/1/issues/2",
"notes": "http://example.com/api/v4/projects/1/issues/2/notes", "notes": "http://gitlab.example.com/api/v4/projects/1/issues/2/notes",
"award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", "award_emoji": "http://gitlab.example.com/api/v4/projects/1/issues/2/award_emoji",
"project": "http://example.com/api/v4/projects/1" "project": "http://gitlab.example.com/api/v4/projects/1"
}, },
"task_completion_status":{ "task_completion_status":{
"count":0, "count":0,
...@@ -1091,7 +1091,7 @@ Example response: ...@@ -1091,7 +1091,7 @@ Example response:
"web_url": "https://gitlab.example.com/solon.cremin" "web_url": "https://gitlab.example.com/solon.cremin"
}, },
"due_date": null, "due_date": null,
"web_url": "http://example.com/my-group/my-project/issues/11", "web_url": "http://gitlab.example.com/my-group/my-project/issues/11",
"references": { "references": {
"short": "#11", "short": "#11",
"relative": "#11", "relative": "#11",
...@@ -1106,10 +1106,10 @@ Example response: ...@@ -1106,10 +1106,10 @@ Example response:
"confidential": false, "confidential": false,
"discussion_locked": false, "discussion_locked": false,
"_links": { "_links": {
"self": "http://example.com/api/v4/projects/1/issues/2", "self": "http://gitlab.example.com/api/v4/projects/1/issues/2",
"notes": "http://example.com/api/v4/projects/1/issues/2/notes", "notes": "http://gitlab.example.com/api/v4/projects/1/issues/2/notes",
"award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", "award_emoji": "http://gitlab.example.com/api/v4/projects/1/issues/2/award_emoji",
"project": "http://example.com/api/v4/projects/1" "project": "http://gitlab.example.com/api/v4/projects/1"
}, },
"task_completion_status":{ "task_completion_status":{
"count":0, "count":0,
...@@ -1190,7 +1190,7 @@ Example response: ...@@ -1190,7 +1190,7 @@ Example response:
}, },
"subscribed": false, "subscribed": false,
"due_date": null, "due_date": null,
"web_url": "http://example.com/my-group/my-project/issues/12", "web_url": "http://gitlab.example.com/my-group/my-project/issues/12",
"references": { "references": {
"short": "#12", "short": "#12",
"relative": "#12", "relative": "#12",
...@@ -1297,7 +1297,7 @@ Example response: ...@@ -1297,7 +1297,7 @@ Example response:
"downvotes": 0, "downvotes": 0,
"merge_requests_count": 0, "merge_requests_count": 0,
"due_date": null, "due_date": null,
"web_url": "http://example.com/my-group/my-project/issues/10", "web_url": "http://gitlab.example.com/my-group/my-project/issues/10",
"references": { "references": {
"short": "#10", "short": "#10",
"relative": "#10", "relative": "#10",
...@@ -1729,7 +1729,7 @@ Example response: ...@@ -1729,7 +1729,7 @@ Example response:
"username": "user1", "username": "user1",
"state": "active", "state": "active",
"avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon", "avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon",
"web_url": "http://localhost/user1" "web_url": "http://gitlab.example.com/user1"
}, },
{ {
"id": 5, "id": 5,
...@@ -1737,7 +1737,7 @@ Example response: ...@@ -1737,7 +1737,7 @@ Example response:
"username": "user5", "username": "user5",
"state": "active", "state": "active",
"avatar_url": "http://www.gravatar.com/avatar/4aea8cf834ed91844a2da4ff7ae6b491?s=80&d=identicon", "avatar_url": "http://www.gravatar.com/avatar/4aea8cf834ed91844a2da4ff7ae6b491?s=80&d=identicon",
"web_url": "http://localhost/user5" "web_url": "http://gitlab.example.com/user5"
} }
] ]
``` ```
......
---
title: Add _links to epic API entity
merge_request: 37395
author:
type: added
...@@ -55,12 +55,14 @@ module API ...@@ -55,12 +55,14 @@ module API
params do params do
requires :epic_iid, type: Integer, desc: 'The iid of the epic' requires :epic_iid, type: Integer, desc: 'The iid of the epic'
end end
get ':id/(-/)epics/:epic_iid/issues' do [':id/epics/:epic_iid/issues', ':id/-/epics/:epic_iid/issues'].each do |path|
authorize_can_read! get path do
authorize_can_read!
present epic.issues_readable_by(current_user), present epic.issues_readable_by(current_user),
with: EE::API::Entities::EpicIssue, with: EE::API::Entities::EpicIssue,
current_user: current_user current_user: current_user
end
end end
desc 'Assign an issue to the epic' do desc 'Assign an issue to the epic' do
......
...@@ -39,12 +39,14 @@ module API ...@@ -39,12 +39,14 @@ module API
optional :my_reaction_emoji, type: String, desc: 'Return epics reacted by the authenticated user by the given emoji' optional :my_reaction_emoji, type: String, desc: 'Return epics reacted by the authenticated user by the given emoji'
use :pagination use :pagination
end end
get ':id/(-/)epics' do [':id/epics', ':id/-/epics'].each do |path|
epics = paginate(find_epics(finder_params: { group_id: user_group.id })).with_api_entity_associations get path do
epics = paginate(find_epics(finder_params: { group_id: user_group.id })).with_api_entity_associations
# issuable_metadata has to be set because `Entities::Epic` doesn't inherit from `Entities::IssuableEntity` # issuable_metadata has to be set because `Entities::Epic` doesn't inherit from `Entities::IssuableEntity`
extra_options = { issuable_metadata: Gitlab::IssuableMetadata.new(current_user, epics).data, with_labels_details: declared_params[:with_labels_details] } extra_options = { issuable_metadata: Gitlab::IssuableMetadata.new(current_user, epics).data, with_labels_details: declared_params[:with_labels_details] }
present epics, epic_options.merge(extra_options) present epics, epic_options.merge(extra_options)
end
end end
desc 'Get details of an epic' do desc 'Get details of an epic' do
...@@ -53,10 +55,12 @@ module API ...@@ -53,10 +55,12 @@ module API
params do params do
requires :epic_iid, type: Integer, desc: 'The internal ID of an epic' requires :epic_iid, type: Integer, desc: 'The internal ID of an epic'
end end
get ':id/(-/)epics/:epic_iid' do [':id/epics/:epic_iid', ':id/-/epics/:epic_iid'].each do |path|
authorize_can_read! get path do
authorize_can_read!
present epic, epic_options.merge(include_subscribed: true) present epic, epic_options.merge(include_subscribed: true)
end
end end
desc 'Create a new epic' do desc 'Create a new epic' do
......
...@@ -4,6 +4,8 @@ module EE ...@@ -4,6 +4,8 @@ module EE
module API module API
module Entities module Entities
class Epic < Grape::Entity class Epic < Grape::Entity
include ::API::Helpers::RelatedResourcesHelpers
can_admin_epic = ->(epic, opts) { Ability.allowed?(opts[:user], :admin_epic, epic) } can_admin_epic = ->(epic, opts) { Ability.allowed?(opts[:user], :admin_epic, epic) }
expose :id expose :id
...@@ -77,6 +79,20 @@ module EE ...@@ -77,6 +79,20 @@ module EE
def web_edit_url def web_edit_url
::Gitlab::Routing.url_helpers.group_epic_path(object.group, object) ::Gitlab::Routing.url_helpers.group_epic_path(object.group, object)
end end
expose :_links do
expose :self do |epic|
expose_url(api_v4_groups_epics_path(id: epic.group_id, epic_iid: epic.iid))
end
expose :epic_issues do |epic|
expose_url(api_v4_groups_epics_issues_path(id: epic.group_id, epic_iid: epic.iid))
end
expose :group do |epic|
expose_url(api_v4_groups_path(id: epic.group_id))
end
end
end end
end end
end end
......
...@@ -51,10 +51,19 @@ ...@@ -51,10 +51,19 @@
"relative": {"type": "string"}, "relative": {"type": "string"},
"full": {"type": "string"} "full": {"type": "string"}
}, },
"subscribed": { "type": ["boolean", "null"] } "subscribed": { "type": ["boolean", "null"] },
"_links": {
"type": "object",
"properties": {
"self": { "type": "uri" },
"epic_issues": { "type": "uri" },
"group": { "type": "uri" },
"additionalProperties": false
}
}
}, },
"required": [ "required": [
"id", "iid", "group_id", "title", "confidential" "id", "iid", "group_id", "title", "confidential", "_links"
], ],
"additionalProperties": false "additionalProperties": false
} }
...@@ -522,6 +522,16 @@ RSpec.describe API::Epics do ...@@ -522,6 +522,16 @@ RSpec.describe API::Epics do
expect(json_response['references']['full']).to eq("#{epic.group.path}&#{epic.iid}") expect(json_response['references']['full']).to eq("#{epic.group.path}&#{epic.iid}")
end end
it 'exposes links' do
get api(url)
links = json_response['_links']
expect(links['self']).to end_with("/api/v4/groups/#{epic.group.id}/epics/#{epic.iid}")
expect(links['epic_issues']).to end_with("/api/v4/groups/#{epic.group.id}/epics/#{epic.iid}/issues")
expect(links['group']).to end_with("/api/v4/groups/#{epic.group.id}")
end
it_behaves_like 'can admin epics' it_behaves_like 'can admin epics'
end end
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