Commit 8dde697d authored by Sean McGivern's avatar Sean McGivern

Merge branch '31301-expose-reference-path-in-api-for-issuables' into 'master'

Expose reference path for issuables

See merge request gitlab-org/gitlab!20354
parents 7f55ca2d 77ffac17
---
title: Expose full reference path for issuables in API
merge_request: 20354
author:
type: changed
...@@ -29,6 +29,14 @@ are paginated. ...@@ -29,6 +29,14 @@ are paginated.
Read more on [pagination](README.md#pagination). Read more on [pagination](README.md#pagination).
CAUTION: **Deprecation**
> `reference` attribute in response is deprecated in favour of `references`.
> Introduced [GitLab 12.6](https://gitlab.com/gitlab-org/gitlab/merge_requests/20354)
NOTE: **Note**
> `references.relative` is relative to the group that the epic is being requested. When epic is fetched from its origin group
> `relative` format would be the same as `short` format and when requested cross groups it is expected to be the same as `full` format.
## List epics for a group ## List epics for a group
Gets all epics of the requested group and its subgroups. Gets all epics of the requested group and its subgroups.
...@@ -73,6 +81,51 @@ Example response: ...@@ -73,6 +81,51 @@ Example response:
"state": "opened", "state": "opened",
"web_url": "http://localhost:3001/groups/test/-/epics/4", "web_url": "http://localhost:3001/groups/test/-/epics/4",
"reference": "&4", "reference": "&4",
"references": {
"short": "&4",
"relative": "&4",
"full": "test&4"
},
"author": {
"id": 10,
"name": "Lu Mayer",
"username": "kam",
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/018729e129a6f31c80a6327a30196823?s=80&d=identicon",
"web_url": "http://localhost:3001/kam"
},
"start_date": null,
"start_date_is_fixed": false,
"start_date_fixed": null,
"start_date_from_milestones": null, //deprecated in favor of start_date_from_inherited_source
"start_date_from_inherited_source": null,
"end_date": "2018-07-31", //deprecated in favor of due_date
"due_date": "2018-07-31",
"due_date_is_fixed": false,
"due_date_fixed": null,
"due_date_from_milestones": "2018-07-31", //deprecated in favor of start_date_from_inherited_source
"due_date_from_inherited_source": "2018-07-31",
"created_at": "2018-07-17T13:36:22.770Z",
"updated_at": "2018-07-18T12:22:05.239Z",
"closed_at": "2018-08-18T12:22:05.239Z",
"labels": [],
"upvotes": 4,
"downvotes": 0
},
{
"id": 50,
"iid": 35,
"group_id": 17,
"title": "Accusamus iste et ullam ratione voluptatem omnis debitis dolor est.",
"description": "Molestias dolorem eos vitae expedita impedit necessitatibus quo voluptatum.",
"state": "opened",
"web_url": "http://localhost:3001/groups/test/sample/-/epics/4",
"reference": "&4",
"references": {
"short": "&4",
"relative": "sample&4",
"full": "test/sample&4"
},
"author": { "author": {
"id": 10, "id": 10,
"name": "Lu Mayer", "name": "Lu Mayer",
...@@ -131,6 +184,11 @@ Example response: ...@@ -131,6 +184,11 @@ Example response:
"state": "opened", "state": "opened",
"web_url": "http://localhost:3001/groups/test/-/epics/5", "web_url": "http://localhost:3001/groups/test/-/epics/5",
"reference": "&5", "reference": "&5",
"references": {
"short": "&5",
"relative": "&5",
"full": "test&5"
},
"author":{ "author":{
"id": 7, "id": 7,
"name": "Pamella Huel", "name": "Pamella Huel",
...@@ -199,8 +257,13 @@ Example response: ...@@ -199,8 +257,13 @@ Example response:
"title": "Epic", "title": "Epic",
"description": "Epic description", "description": "Epic description",
"state": "opened", "state": "opened",
"web_url": "http://localhost:3001/groups/test/-/epics/5", "web_url": "http://localhost:3001/groups/test/-/epics/6",
"reference": "&6", "reference": "&6",
"references": {
"short": "&6",
"relative": "&6",
"full": "test&6"
},
"author": { "author": {
"name" : "Alexandra Bashirian", "name" : "Alexandra Bashirian",
"avatar_url" : null, "avatar_url" : null,
...@@ -269,8 +332,13 @@ Example response: ...@@ -269,8 +332,13 @@ Example response:
"title": "New Title", "title": "New Title",
"description": "Epic description", "description": "Epic description",
"state": "opened", "state": "opened",
"web_url": "http://localhost:3001/groups/test/-/epics/5", "web_url": "http://localhost:3001/groups/test/-/epics/6",
"reference": "&6", "reference": "&6",
"references": {
"short": "&6",
"relative": "&6",
"full": "test&6"
},
"author": { "author": {
"name" : "Alexandra Bashirian", "name" : "Alexandra Bashirian",
"avatar_url" : null, "avatar_url" : null,
...@@ -372,6 +440,13 @@ Example response: ...@@ -372,6 +440,13 @@ Example response:
"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://localhost:3001/arnita"
}, },
"web_url": "http://localhost:3001/groups/test/-/epics/5",
"reference": "&5",
"references": {
"short": "&5",
"relative": "&5",
"full": "test&5"
},
"start_date": null, "start_date": null,
"end_date": null, "end_date": null,
"created_at": "2018-01-21T06:21:13.165Z", "created_at": "2018-01-21T06:21:13.165Z",
......
...@@ -12,6 +12,14 @@ are paginated. ...@@ -12,6 +12,14 @@ are paginated.
Read more on [pagination](README.md#pagination). Read more on [pagination](README.md#pagination).
CAUTION: **Deprecation**
> `reference` attribute in response is deprecated in favour of `references`.
> Introduced [GitLab 12.6](https://gitlab.com/gitlab-org/gitlab/merge_requests/20354)
NOTE: **Note**
> `references.relative` is relative to the group / project that the issue is being requested. When issue is fetched from its project
> `relative` format would be the same as `short` format and when requested across groups / projects it is expected to be the same as `full` format.
## List issues ## List issues
Get all issues the authenticated user has access to. By default it Get all issues the authenticated user has access to. By default it
...@@ -121,7 +129,12 @@ Example response: ...@@ -121,7 +129,12 @@ 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/example/example/issues/6", "web_url": "http://example.com/my-group/my-project/issues/6",
"references": {
"short": "#6",
"relative": "my-group/my-project#6",
"full": "my-group/my-project#6"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -270,7 +283,12 @@ Example response: ...@@ -270,7 +283,12 @@ 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/example/example/issues/1", "web_url": "http://example.com/my-group/my-project/issues/1",
"references": {
"short": "#1",
"relative": "my-project#1",
"full": "my-group/my-project#1"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -426,7 +444,12 @@ Example response: ...@@ -426,7 +444,12 @@ 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/example/example/issues/1", "web_url": "http://example.com/my-group/my-project/issues/1",
"references": {
"short": "#1",
"relative": "#1",
"full": "my-group/my-project#1"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -543,7 +566,12 @@ Example response: ...@@ -543,7 +566,12 @@ 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/example/example/issues/1", "web_url": "http://example.com/my-group/my-project/issues/1",
"references": {
"short": "#1",
"relative": "#1",
"full": "my-group/my-project#1"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -668,7 +696,12 @@ Example response: ...@@ -668,7 +696,12 @@ 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/example/example/issues/14", "web_url": "http://example.com/my-group/my-project/issues/14",
"references": {
"short": "#14",
"relative": "#14",
"full": "my-group/my-project#14"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -778,7 +811,12 @@ Example response: ...@@ -778,7 +811,12 @@ 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/example/example/issues/15", "web_url": "http://example.com/my-group/my-project/issues/15",
"references": {
"short": "#15",
"relative": "#15",
"full": "my-group/my-project#15"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -900,7 +938,12 @@ Example response: ...@@ -900,7 +938,12 @@ 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/example/example/issues/11", "web_url": "http://example.com/my-group/my-project/issues/11",
"references": {
"short": "#11",
"relative": "#11",
"full": "my-group/my-project#11"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -1001,7 +1044,12 @@ Example response: ...@@ -1001,7 +1044,12 @@ 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/example/example/issues/11", "web_url": "http://example.com/my-group/my-project/issues/11",
"references": {
"short": "#11",
"relative": "#11",
"full": "my-group/my-project#11"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -1095,7 +1143,12 @@ Example response: ...@@ -1095,7 +1143,12 @@ Example response:
}, },
"subscribed": false, "subscribed": false,
"due_date": null, "due_date": null,
"web_url": "http://example.com/example/example/issues/12", "web_url": "http://example.com/my-group/my-project/issues/12",
"references": {
"short": "#12",
"relative": "#12",
"full": "my-group/my-project#12"
},
"confidential": false, "confidential": false,
"discussion_locked": false, "discussion_locked": false,
"task_completion_status":{ "task_completion_status":{
...@@ -1197,7 +1250,12 @@ Example response: ...@@ -1197,7 +1250,12 @@ 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/example/example/issues/110", "web_url": "http://example.com/my-group/my-project/issues/10",
"references": {
"short": "#10",
"relative": "#10",
"full": "my-group/my-project#10"
},
"confidential": false, "confidential": false,
"discussion_locked": false, "discussion_locked": false,
"task_completion_status":{ "task_completion_status":{
...@@ -1436,6 +1494,11 @@ Example response: ...@@ -1436,6 +1494,11 @@ Example response:
"force_remove_source_branch": false, "force_remove_source_branch": false,
"reference": "!11", "reference": "!11",
"web_url": "https://gitlab.example.com/twitter/flight/merge_requests/4", "web_url": "https://gitlab.example.com/twitter/flight/merge_requests/4",
"references": {
"short": "!4",
"relative": "!4",
"full": "twitter/flight!4"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -1566,6 +1629,12 @@ Example response: ...@@ -1566,6 +1629,12 @@ Example response:
"should_remove_source_branch": null, "should_remove_source_branch": null,
"force_remove_source_branch": false, "force_remove_source_branch": false,
"web_url": "https://gitlab.example.com/gitlab-org/gitlab-test/merge_requests/6432", "web_url": "https://gitlab.example.com/gitlab-org/gitlab-test/merge_requests/6432",
"reference": "!6432",
"references": {
"short": "!6432",
"relative": "!6432",
"full": "gitlab-org/gitlab-test!6432"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
......
...@@ -2,6 +2,14 @@ ...@@ -2,6 +2,14 @@
Every API call to merge requests must be authenticated. Every API call to merge requests must be authenticated.
CAUTION: **Deprecation**
> `reference` attribute in response is deprecated in favour of `references`.
> Introduced [GitLab 12.6](https://gitlab.com/gitlab-org/gitlab/merge_requests/20354)
NOTE: **Note**
> `references.relative` is relative to the group / project that the merge request is being requested. When merge request is fetched from its project
> `relative` format would be the same as `short` format and when requested across groups / projects it is expected to be the same as `full` format.
## List merge requests ## List merge requests
> [Introduced][ce-13060] in GitLab 9.5. > [Introduced][ce-13060] in GitLab 9.5.
...@@ -134,6 +142,11 @@ Parameters: ...@@ -134,6 +142,11 @@ Parameters:
"allow_collaboration": false, "allow_collaboration": false,
"allow_maintainer_to_push": false, "allow_maintainer_to_push": false,
"web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"references": {
"short": "!1",
"relative": "my-group/my-project!1",
"full": "my-group/my-project!1"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -296,6 +309,11 @@ Parameters: ...@@ -296,6 +309,11 @@ Parameters:
"allow_collaboration": false, "allow_collaboration": false,
"allow_maintainer_to_push": false, "allow_maintainer_to_push": false,
"web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"references": {
"short": "!1",
"relative": "!1",
"full": "my-group/my-project!1"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -448,6 +466,11 @@ Parameters: ...@@ -448,6 +466,11 @@ Parameters:
"should_remove_source_branch": true, "should_remove_source_branch": true,
"force_remove_source_branch": false, "force_remove_source_branch": false,
"web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"references": {
"short": "!1",
"relative": "my-project!1",
"full": "my-group/my-project!1"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -574,6 +597,11 @@ Parameters: ...@@ -574,6 +597,11 @@ Parameters:
"allow_collaboration": false, "allow_collaboration": false,
"allow_maintainer_to_push": false, "allow_maintainer_to_push": false,
"web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"references": {
"short": "!1",
"relative": "!1",
"full": "my-group/my-project!1"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -779,7 +807,12 @@ Parameters: ...@@ -779,7 +807,12 @@ Parameters:
"should_remove_source_branch": true, "should_remove_source_branch": true,
"force_remove_source_branch": false, "force_remove_source_branch": false,
"squash": false, "squash": false,
"web_url": "http://example.com/example/example/merge_requests/1", "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"references": {
"short": "!1",
"relative": "!1",
"full": "my-group/my-project!1"
},
"discussion_locked": false, "discussion_locked": false,
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
...@@ -989,6 +1022,11 @@ order for it to take effect: ...@@ -989,6 +1022,11 @@ order for it to take effect:
"allow_collaboration": false, "allow_collaboration": false,
"allow_maintainer_to_push": false, "allow_maintainer_to_push": false,
"web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"references": {
"short": "!1",
"relative": "!1",
"full": "my-group/my-project!1"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -1143,6 +1181,11 @@ Must include at least one non-required attribute from above. ...@@ -1143,6 +1181,11 @@ Must include at least one non-required attribute from above.
"allow_collaboration": false, "allow_collaboration": false,
"allow_maintainer_to_push": false, "allow_maintainer_to_push": false,
"web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"references": {
"short": "!1",
"relative": "!1",
"full": "my-group/my-project!1"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -1313,6 +1356,11 @@ Parameters: ...@@ -1313,6 +1356,11 @@ Parameters:
"allow_collaboration": false, "allow_collaboration": false,
"allow_maintainer_to_push": false, "allow_maintainer_to_push": false,
"web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"references": {
"short": "!1",
"relative": "!1",
"full": "my-group/my-project!1"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -1486,6 +1534,11 @@ Parameters: ...@@ -1486,6 +1534,11 @@ Parameters:
"allow_collaboration": false, "allow_collaboration": false,
"allow_maintainer_to_push": false, "allow_maintainer_to_push": false,
"web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"references": {
"short": "!1",
"relative": "!1",
"full": "my-group/my-project!1"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -1772,6 +1825,11 @@ Example response: ...@@ -1772,6 +1825,11 @@ Example response:
"allow_collaboration": false, "allow_collaboration": false,
"allow_maintainer_to_push": false, "allow_maintainer_to_push": false,
"web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"references": {
"short": "!1",
"relative": "!1",
"full": "my-group/my-project!1"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -1918,6 +1976,11 @@ Example response: ...@@ -1918,6 +1976,11 @@ Example response:
"allow_collaboration": false, "allow_collaboration": false,
"allow_maintainer_to_push": false, "allow_maintainer_to_push": false,
"web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1", "web_url": "http://gitlab.example.com/my-group/my-project/merge_requests/1",
"references": {
"short": "!1",
"relative": "!1",
"full": "my-group/my-project!1"
},
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
"total_time_spent": 0, "total_time_spent": 0,
...@@ -2078,7 +2141,12 @@ Example response: ...@@ -2078,7 +2141,12 @@ Example response:
"should_remove_source_branch": true, "should_remove_source_branch": true,
"force_remove_source_branch": false, "force_remove_source_branch": false,
"squash": false, "squash": false,
"web_url": "http://example.com/example/example/merge_requests/1" "web_url": "http://example.com/my-group/my-project/merge_requests/1",
"references": {
"short": "!1",
"relative": "!1",
"full": "my-group/my-project!1"
},
}, },
"target_url": "https://gitlab.example.com/gitlab-org/gitlab-ci/merge_requests/7", "target_url": "https://gitlab.example.com/gitlab-org/gitlab-ci/merge_requests/7",
"body": "Et voluptas laudantium minus nihil recusandae ut accusamus earum aut non.", "body": "Et voluptas laudantium minus nihil recusandae ut accusamus earum aut non.",
......
...@@ -42,7 +42,8 @@ module API ...@@ -42,7 +42,8 @@ module API
epics = paginate(find_epics(finder_params: { group_id: user_group.id })).with_api_entity_associations epics = paginate(find_epics(finder_params: { group_id: user_group.id })).with_api_entity_associations
# issuable_metadata is the standard used by the Todo API # issuable_metadata is the standard used by the Todo API
present epics, with: EE::API::Entities::Epic, user: current_user, issuable_metadata: issuable_meta_data(epics, 'Epic')
present epics, epic_options.merge(issuable_metadata: issuable_meta_data(epics, 'Epic'))
end end
desc 'Get details of an epic' do desc 'Get details of an epic' do
...@@ -54,9 +55,7 @@ module API ...@@ -54,9 +55,7 @@ module API
get ':id/(-/)epics/:epic_iid' do get ':id/(-/)epics/:epic_iid' do
authorize_can_read! authorize_can_read!
present epic, options, user: current_user, present epic, epic_options.merge(include_subscribed: true)
with: EE::API::Entities::Epic,
include_subscribed: true
end end
desc 'Create a new epic' do desc 'Create a new epic' do
...@@ -77,7 +76,7 @@ module API ...@@ -77,7 +76,7 @@ module API
epic = ::Epics::CreateService.new(user_group, current_user, declared_params(include_missing: false)).execute epic = ::Epics::CreateService.new(user_group, current_user, declared_params(include_missing: false)).execute
if epic.valid? if epic.valid?
present epic, with: EE::API::Entities::Epic, user: current_user present epic, epic_options
else else
render_validation_error!(epic) render_validation_error!(epic)
end end
...@@ -106,7 +105,7 @@ module API ...@@ -106,7 +105,7 @@ module API
result = ::Epics::UpdateService.new(user_group, current_user, update_params).execute(epic) result = ::Epics::UpdateService.new(user_group, current_user, update_params).execute(epic)
if result.valid? if result.valid?
present result, with: EE::API::Entities::Epic, user: current_user present result, epic_options
else else
render_validation_error!(result) render_validation_error!(result)
end end
......
...@@ -41,6 +41,14 @@ module API ...@@ -41,6 +41,14 @@ module API
end end
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def epic_options
{
with: EE::API::Entities::Epic,
user: current_user,
group: user_group
}
end
end end
end end
end end
...@@ -325,6 +325,11 @@ module EE ...@@ -325,6 +325,11 @@ module EE
expose :state expose :state
expose :web_edit_url, if: can_admin_epic # @deprecated expose :web_edit_url, if: can_admin_epic # @deprecated
expose :web_url expose :web_url
expose :references, with: ::API::Entities::IssuableReferences do |epic|
epic
end
# reference is deprecated in favour of references
# Introduced [Gitlab 12.6](https://gitlab.com/gitlab-org/gitlab/merge_requests/20354)
expose :reference, if: { with_reference: true } do |epic| expose :reference, if: { with_reference: true } do |epic|
epic.to_reference(full: true) epic.to_reference(full: true)
end end
......
...@@ -45,6 +45,11 @@ ...@@ -45,6 +45,11 @@
"web_edit_url": { "type": "string" }, "web_edit_url": { "type": "string" },
"web_url": { "type": "string" }, "web_url": { "type": "string" },
"reference": { "type": "string" }, "reference": { "type": "string" },
"references": {
"short": {"type": "string"},
"relative": {"type": "string"},
"full": {"type": "string"}
},
"subscribed": { "type": ["boolean", "null"] } "subscribed": { "type": ["boolean", "null"] }
}, },
"required": [ "required": [
......
...@@ -294,6 +294,32 @@ describe API::Epics do ...@@ -294,6 +294,32 @@ describe API::Epics do
expect_paginated_array_response(epic.id) expect_paginated_array_response(epic.id)
end end
context "#to_reference" do
it 'exposes reference path' do
get api(url)
expect(json_response.first['references']['short']).to eq("&#{epic2.iid}")
expect(json_response.first['references']['relative']).to eq("&#{epic2.iid}")
expect(json_response.first['references']['full']).to eq("#{epic2.group.path}&#{epic2.iid}")
end
context 'referencing from parent group' do
let(:parent_group) { create(:group) }
before do
group.update(parent_id: parent_group.id)
end
it 'exposes full reference path' do
get api("/groups/#{parent_group.path}/epics")
expect(json_response.first['references']['short']).to eq("&#{epic2.iid}")
expect(json_response.first['references']['relative']).to eq("#{parent_group.path}/#{epic2.group.path}&#{epic2.iid}")
expect(json_response.first['references']['full']).to eq("#{parent_group.path}/#{epic2.group.path}&#{epic2.iid}")
end
end
end
it_behaves_like 'can admin epics' it_behaves_like 'can admin epics'
end end
...@@ -437,6 +463,14 @@ describe API::Epics do ...@@ -437,6 +463,14 @@ describe API::Epics do
expect(json_response['closed_at']).to be_present expect(json_response['closed_at']).to be_present
end end
it 'exposes full reference path' do
get api(url)
expect(json_response['references']['short']).to eq("&#{epic.iid}")
expect(json_response['references']['relative']).to eq("&#{epic.iid}")
expect(json_response['references']['full']).to eq("#{epic.group.path}&#{epic.iid}")
end
it_behaves_like 'can admin epics' it_behaves_like 'can admin epics'
end end
end end
......
...@@ -569,6 +569,20 @@ module API ...@@ -569,6 +569,20 @@ module API
end end
end end
class IssuableReferences < Grape::Entity
expose :short do |issuable|
issuable.to_reference
end
expose :relative do |issuable, options|
issuable.to_reference(options[:group] || options[:project])
end
expose :full do |issuable|
issuable.to_reference(full: true)
end
end
class Diff < Grape::Entity class Diff < Grape::Entity
expose :old_path, :new_path, :a_mode, :b_mode expose :old_path, :new_path, :a_mode, :b_mode
expose :new_file?, as: :new_file expose :new_file?, as: :new_file
...@@ -676,6 +690,10 @@ module API ...@@ -676,6 +690,10 @@ module API
end end
end end
expose :references, with: IssuableReferences do |issue|
issue
end
# Calculating the value of subscribed field triggers Markdown # Calculating the value of subscribed field triggers Markdown
# processing. We can't do that for multiple issues / merge # processing. We can't do that for multiple issues / merge
# requests in a single API request. # requests in a single API request.
...@@ -787,10 +805,16 @@ module API ...@@ -787,10 +805,16 @@ module API
# Deprecated # Deprecated
expose :allow_collaboration, as: :allow_maintainer_to_push, if: -> (merge_request, _) { merge_request.for_fork? } expose :allow_collaboration, as: :allow_maintainer_to_push, if: -> (merge_request, _) { merge_request.for_fork? }
# reference is deprecated in favour of references
# Introduced [Gitlab 12.6](https://gitlab.com/gitlab-org/gitlab/merge_requests/20354)
expose :reference do |merge_request, options| expose :reference do |merge_request, options|
merge_request.to_reference(options[:project]) merge_request.to_reference(options[:project])
end end
expose :references, with: IssuableReferences do |merge_request|
merge_request
end
expose :web_url do |merge_request| expose :web_url do |merge_request|
Gitlab::UrlBuilder.build(merge_request) Gitlab::UrlBuilder.build(merge_request)
end end
......
...@@ -122,16 +122,15 @@ module API ...@@ -122,16 +122,15 @@ module API
use :issues_params use :issues_params
end end
get ":id/issues" do get ":id/issues" do
group = find_group!(params[:id]) issues = paginate(find_issues(group_id: user_group.id, include_subgroups: true))
issues = paginate(find_issues(group_id: group.id, include_subgroups: true))
options = { options = {
with: Entities::Issue, with: Entities::Issue,
with_labels_details: declared_params[:with_labels_details], with_labels_details: declared_params[:with_labels_details],
current_user: current_user, current_user: current_user,
issuable_metadata: issuable_meta_data(issues, 'Issue', current_user), issuable_metadata: issuable_meta_data(issues, 'Issue', current_user),
include_subscribed: false include_subscribed: false,
group: user_group
} }
present issues, options present issues, options
...@@ -142,9 +141,7 @@ module API ...@@ -142,9 +141,7 @@ module API
use :issues_stats_params use :issues_stats_params
end end
get ":id/issues_statistics" do get ":id/issues_statistics" do
group = find_group!(params[:id]) present issues_statistics(group_id: user_group.id, include_subgroups: true), with: Grape::Presenters::Presenter
present issues_statistics(group_id: group.id, include_subgroups: true), with: Grape::Presenters::Presenter
end end
end end
...@@ -161,9 +158,7 @@ module API ...@@ -161,9 +158,7 @@ module API
use :issues_params use :issues_params
end end
get ":id/issues" do get ":id/issues" do
project = find_project!(params[:id]) issues = paginate(find_issues(project_id: user_project.id))
issues = paginate(find_issues(project_id: project.id))
options = { options = {
with: Entities::Issue, with: Entities::Issue,
...@@ -182,9 +177,7 @@ module API ...@@ -182,9 +177,7 @@ module API
use :issues_stats_params use :issues_stats_params
end end
get ":id/issues_statistics" do get ":id/issues_statistics" do
project = find_project!(params[:id]) present issues_statistics(project_id: user_project.id), with: Grape::Presenters::Presenter
present issues_statistics(project_id: project.id), with: Grape::Presenters::Presenter
end end
desc 'Get a single project issue' do desc 'Get a single project issue' do
......
...@@ -157,11 +157,9 @@ module API ...@@ -157,11 +157,9 @@ module API
use :merge_requests_params use :merge_requests_params
end end
get ":id/merge_requests" do get ":id/merge_requests" do
group = find_group!(params[:id]) merge_requests = find_merge_requests(group_id: user_group.id, include_subgroups: true)
merge_requests = find_merge_requests(group_id: group.id, include_subgroups: true) present merge_requests, serializer_options_for(merge_requests).merge(group: user_group)
present merge_requests, serializer_options_for(merge_requests)
end end
end end
...@@ -215,7 +213,7 @@ module API ...@@ -215,7 +213,7 @@ module API
merge_requests = find_merge_requests(project_id: user_project.id) merge_requests = find_merge_requests(project_id: user_project.id)
options = serializer_options_for(merge_requests) options = serializer_options_for(merge_requests).merge(project: user_project)
options[:project] = user_project options[:project] = user_project
present merge_requests, options present merge_requests, options
......
...@@ -84,6 +84,11 @@ ...@@ -84,6 +84,11 @@
"total_time_spent": { "type": "integer" }, "total_time_spent": { "type": "integer" },
"human_time_estimate": { "type": ["string", "null"] }, "human_time_estimate": { "type": ["string", "null"] },
"human_total_time_spent": { "type": ["string", "null"] } "human_total_time_spent": { "type": ["string", "null"] }
},
"references": {
"short": {"type": "string"},
"relative": {"type": "string"},
"full": {"type": "string"}
} }
}, },
"required": [ "required": [
......
...@@ -113,7 +113,12 @@ ...@@ -113,7 +113,12 @@
"human_total_time_spent": { "type": ["string", "null"] } "human_total_time_spent": { "type": ["string", "null"] }
}, },
"allow_collaboration": { "type": ["boolean", "null"] }, "allow_collaboration": { "type": ["boolean", "null"] },
"allow_maintainer_to_push": { "type": ["boolean", "null"] } "allow_maintainer_to_push": { "type": ["boolean", "null"] },
"references": {
"short": {"type": "string"},
"relative": {"type": "string"},
"full": {"type": "string"}
}
}, },
"required": [ "required": [
"id", "iid", "project_id", "title", "description", "id", "iid", "project_id", "title", "description",
......
...@@ -688,5 +688,32 @@ describe API::Issues do ...@@ -688,5 +688,32 @@ describe API::Issues do
end end
end end
end end
context "#to_reference" do
it 'exposes reference path in context of group' do
get api(base_url, user)
expect(json_response.first['references']['short']).to eq("##{group_closed_issue.iid}")
expect(json_response.first['references']['relative']).to eq("#{group_closed_issue.project.path}##{group_closed_issue.iid}")
expect(json_response.first['references']['full']).to eq("#{group_closed_issue.project.full_path}##{group_closed_issue.iid}")
end
context 'referencing from parent group' do
let(:parent_group) { create(:group) }
before do
group.update(parent_id: parent_group.id)
group_closed_issue.reload
end
it 'exposes reference path in context of parent group' do
get api("/groups/#{parent_group.id}/issues")
expect(json_response.first['references']['short']).to eq("##{group_closed_issue.iid}")
expect(json_response.first['references']['relative']).to eq("#{group_closed_issue.project.full_path}##{group_closed_issue.iid}")
expect(json_response.first['references']['full']).to eq("#{group_closed_issue.project.full_path}##{group_closed_issue.iid}")
end
end
end
end end
end end
...@@ -805,6 +805,17 @@ describe API::Issues do ...@@ -805,6 +805,17 @@ describe API::Issues do
end end
end end
describe 'GET /projects/:id/issues/:issue_iid' do
it 'exposes full reference path' do
get api("/projects/#{project.id}/issues/#{issue.iid}", user)
expect(response).to have_gitlab_http_status(200)
expect(json_response['references']['short']).to eq("##{issue.iid}")
expect(json_response['references']['relative']).to eq("##{issue.iid}")
expect(json_response['references']['full']).to eq("#{project.parent.path}/#{project.path}##{issue.iid}")
end
end
describe 'DELETE /projects/:id/issues/:issue_iid' do describe 'DELETE /projects/:id/issues/:issue_iid' do
it 'rejects a non member from deleting an issue' do it 'rejects a non member from deleting an issue' do
delete api("/projects/#{project.id}/issues/#{issue.iid}", non_member) delete api("/projects/#{project.id}/issues/#{issue.iid}", non_member)
......
...@@ -736,6 +736,33 @@ describe API::MergeRequests do ...@@ -736,6 +736,33 @@ describe API::MergeRequests do
it_behaves_like 'merge requests list' it_behaves_like 'merge requests list'
end end
context "#to_reference" do
it 'exposes reference path in context of group' do
get api("/groups/#{group.id}/merge_requests", user)
expect(json_response.first['references']['short']).to eq("!#{merge_request_merged.iid}")
expect(json_response.first['references']['relative']).to eq("#{merge_request_merged.target_project.path}!#{merge_request_merged.iid}")
expect(json_response.first['references']['full']).to eq("#{merge_request_merged.target_project.full_path}!#{merge_request_merged.iid}")
end
context 'referencing from parent group' do
let(:parent_group) { create(:group) }
before do
group.update(parent_id: parent_group.id)
merge_request_merged.reload
end
it 'exposes reference path in context of parent group' do
get api("/groups/#{parent_group.id}/merge_requests")
expect(json_response.first['references']['short']).to eq("!#{merge_request_merged.iid}")
expect(json_response.first['references']['relative']).to eq("#{merge_request_merged.target_project.full_path}!#{merge_request_merged.iid}")
expect(json_response.first['references']['full']).to eq("#{merge_request_merged.target_project.full_path}!#{merge_request_merged.iid}")
end
end
end
end end
describe "GET /projects/:id/merge_requests/:merge_request_iid" do describe "GET /projects/:id/merge_requests/:merge_request_iid" do
...@@ -783,6 +810,9 @@ describe API::MergeRequests do ...@@ -783,6 +810,9 @@ describe API::MergeRequests do
expect(json_response).not_to include('rebase_in_progress') expect(json_response).not_to include('rebase_in_progress')
expect(json_response['has_conflicts']).to be_falsy expect(json_response['has_conflicts']).to be_falsy
expect(json_response['blocking_discussions_resolved']).to be_truthy expect(json_response['blocking_discussions_resolved']).to be_truthy
expect(json_response['references']['short']).to eq("!#{merge_request.iid}")
expect(json_response['references']['relative']).to eq("!#{merge_request.iid}")
expect(json_response['references']['full']).to eq("#{merge_request.target_project.full_path}!#{merge_request.iid}")
end end
it 'exposes description and title html when render_html is true' do it 'exposes description and title html when render_html is true' do
......
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