From 17a7b4113290435560275e408b1764f4b308295f Mon Sep 17 00:00:00 2001 From: Helmut Januschka Date: Wed, 19 Sep 2018 10:06:03 +0200 Subject: add related merge request endpoint --- changelogs/unreleased/related_mrs.yml | 5 ++ doc/api/issues.md | 87 +++++++++++++++++++++++++++++++++++ lib/api/issues.rb | 24 ++++++++++ spec/requests/api/issues_spec.rb | 68 +++++++++++++++++++++++++++ 4 files changed, 184 insertions(+) create mode 100644 changelogs/unreleased/related_mrs.yml diff --git a/changelogs/unreleased/related_mrs.yml b/changelogs/unreleased/related_mrs.yml new file mode 100644 index 00000000000..cc89e9d0cdb --- /dev/null +++ b/changelogs/unreleased/related_mrs.yml @@ -0,0 +1,5 @@ +--- +title: Add API endpoint to list issue related merge requests +merge_request: 21806 +author: Helmut Januschka +type: added diff --git a/doc/api/issues.md b/doc/api/issues.md index 6b00ead94b0..0dc9d706120 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -1113,6 +1113,93 @@ Example response: } ``` +## List merge requests related to issue + +Get all the merge requests that are related to the issue. + +``` +GET /projects/:id/issues/:issue_id/related_merge_requests +``` + +| Attribute | Type | Required | Description | +|-------------|---------|----------|--------------------------------------| +| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user | +| `issue_iid` | integer | yes | The internal ID of a project's issue | + +```sh +curl --request GET --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/issues/11/related_merge_requests +``` + +Example response: + +```json +[ + { + "id": 29, + "iid": 11, + "project_id": 1, + "title": "Provident eius eos blanditiis consequatur neque odit.", + "description": "Ut consequatur ipsa aspernatur quisquam voluptatum fugit. Qui harum corporis quo fuga ut incidunt veritatis. Autem necessitatibus et harum occaecati nihil ea.\r\n\r\ntwitter/flight#8", + "state": "opened", + "created_at": "2018-09-18T14:36:15.510Z", + "updated_at": "2018-09-19T07:45:13.089Z", + "target_branch": "v2.x", + "source_branch": "so_long_jquery", + "upvotes": 0, + "downvotes": 0, + "author": { + "id": 14, + "name": "Verna Hills", + "username": "lawanda_reinger", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/de68a91aeab1cff563795fb98a0c2cc0?s=80&d=identicon", + "web_url": "https://gitlab.example.com/lawanda_reinger" + }, + "assignee": { + "id": 19, + "name": "Jody Baumbach", + "username": "felipa.kuvalis", + "state": "active", + "avatar_url": "https://www.gravatar.com/avatar/6541fc75fc4e87e203529bd275fafd07?s=80&d=identicon", + "web_url": "https://gitlab.example.com/felipa.kuvalis" + }, + "source_project_id": 1, + "target_project_id": 1, + "labels": [], + "work_in_progress": false, + "milestone": { + "id": 27, + "iid": 2, + "project_id": 1, + "title": "v1.0", + "description": "Et tenetur voluptatem minima doloribus vero dignissimos vitae.", + "state": "active", + "created_at": "2018-09-18T14:35:44.353Z", + "updated_at": "2018-09-18T14:35:44.353Z", + "due_date": null, + "start_date": null, + "web_url": "https://gitlab.example.com/twitter/flight/milestones/2" + }, + "merge_when_pipeline_succeeds": false, + "merge_status": "cannot_be_merged", + "sha": "3b7b528e9353295c1c125dad281ac5b5deae5f12", + "merge_commit_sha": null, + "user_notes_count": 9, + "discussion_locked": null, + "should_remove_source_branch": null, + "force_remove_source_branch": false, + "web_url": "https://gitlab.example.com/twitter/flight/merge_requests/4", + "time_stats": { + "time_estimate": 0, + "total_time_spent": 0, + "human_time_estimate": null, + "human_total_time_spent": null + }, + "squash": false + } +] +``` + ## List merge requests that will close issue on merge Get all the merge requests that will close issue when merged. diff --git a/lib/api/issues.rb b/lib/api/issues.rb index e37083165f5..7909f9c7a00 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -294,6 +294,30 @@ module API end # rubocop: enable CodeReuse/ActiveRecord + desc 'List merge requests that are related to the issue' do + success Entities::MergeRequestBasic + end + params do + requires :issue_iid, type: Integer, desc: 'The internal ID of a project issue' + end + get ':id/issues/:issue_iid/related_merge_requests' do + issue = find_project_issue(params[:issue_iid]) + + merge_request_iids = ::Issues::ReferencedMergeRequestsService.new(user_project, current_user) + .execute(issue) + .flatten + .map(&:iid) + + merge_requests = + if merge_request_iids.present? + MergeRequestsFinder.new(current_user, project_id: user_project.id, iids: merge_request_iids).execute + else + MergeRequest.none + end + + present paginate(merge_requests), with: Entities::MergeRequestBasic, current_user: current_user, project: user_project + end + desc 'List merge requests closing issue' do success Entities::MergeRequestBasic end diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index 5dbe967e4fe..3d532dd83c7 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -1801,6 +1801,74 @@ describe API::Issues do end end + describe 'GET :id/issues/:issue_iid/related_merge_requests' do + def get_related_merge_requests(project_id, issue_iid, user = nil) + get api("/projects/#{project_id}/issues/#{issue_iid}/related_merge_requests", user) + end + + def create_referencing_mr(user, project, issue) + attributes = { + author: user, + source_project: project, + target_project: project, + source_branch: "master", + target_branch: "test", + description: "See #{issue.to_reference}" + } + create(:merge_request, attributes).tap do |merge_request| + create(:note, :system, project: project, noteable: issue, author: user, note: merge_request.to_reference(full: true)) + end + end + + let!(:related_mr) { create_referencing_mr(user, project, issue) } + + context 'when unauthenticated' do + it 'return list of referenced merge requests from issue' do + get_related_merge_requests(project.id, issue.iid) + + expect_paginated_array_response(size: 1) + end + + it 'renders 404 if project is not visible' do + private_project = create(:project, :private) + private_issue = create(:issue, project: private_project) + create_referencing_mr(user, private_project, private_issue) + + get_related_merge_requests(private_project.id, private_issue.iid) + + expect(response).to have_gitlab_http_status(404) + end + end + + it 'returns merge requests that mentioned a issue' do + create(:merge_request, + :simple, + author: user, + source_project: project, + target_project: project, + description: "Some description") + + get_related_merge_requests(project.id, issue.iid, user) + + expect_paginated_array_response(size: 1) + expect(json_response.first['id']).to eq(related_mr.id) + end + + context 'no merge request mentioned a issue' do + it 'returns empty array' do + get_related_merge_requests(project.id, closed_issue.iid, user) + + expect_paginated_array_response(size: 0) + end + end + + it "returns 404 when issue doesn't exists" do + get_related_merge_requests(project.id, 999999, user) + + expect(response).to have_gitlab_http_status(404) + end + end + describe "GET /projects/:id/issues/:issue_iid/user_agent_detail" do let!(:user_agent_detail) { create(:user_agent_detail, subject: issue) } -- cgit v1.2.3