Commit 3f1b7b88 authored by Sean McGivern's avatar Sean McGivern

Merge branch 'tc-api-root-merge-requests' into 'master'

Add top-level /merge_requests API endpoint

Closes #28422

See merge request !13060
parents 7e17f9bc 39c39ae7
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
# group_id: integer # group_id: integer
# project_id: integer # project_id: integer
# milestone_title: string # milestone_title: string
# author_id: integer
# assignee_id: integer # assignee_id: integer
# search: string # search: string
# label_name: string # label_name: string
......
---
title: Add top-level merge_requests API endpoint
merge_request: 13060
author:
...@@ -14,7 +14,9 @@ Read more on [pagination](README.md#pagination). ...@@ -14,7 +14,9 @@ Read more on [pagination](README.md#pagination).
## List issues ## List issues
Get all issues created by the authenticated user. Get all issues the authenticated user has access to. By default it
returns only issues created by the current user. To get all issues,
use parameter `scope=all`.
``` ```
GET /issues GET /issues
...@@ -35,12 +37,12 @@ GET /issues?assignee_id=5 ...@@ -35,12 +37,12 @@ GET /issues?assignee_id=5
| `state` | string | no | Return all issues or just those that are `opened` or `closed` | | `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels | | `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
| `milestone` | string | no | The milestone title | | `milestone` | string | no | The milestone title |
| `scope` | string | no | Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all` | | `scope` | string | no | Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all`. Defaults to `created-by-me` _([Introduced][ce-13004] in GitLab 9.5)_ |
| `author_id` | integer | no | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned-to-me`. | | `author_id` | integer | no | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned-to-me`. _([Introduced][ce-13004] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Return issues assigned to the given user `id` | | `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
| `iids` | Array[integer] | no | Return only the issues having the given `iid` | | `iids` | Array[integer] | no | Return only the issues having the given `iid` |
| `order_by` | string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | | `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | | `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Search issues against their `title` and `description` | | `search` | string | no | Search issues against their `title` and `description` |
```bash ```bash
...@@ -132,12 +134,12 @@ GET /groups/:id/issues?assignee_id=5 ...@@ -132,12 +134,12 @@ GET /groups/:id/issues?assignee_id=5
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels | | `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
| `iids` | Array[integer] | no | Return only the issues having the given `iid` | | `iids` | Array[integer] | no | Return only the issues having the given `iid` |
| `milestone` | string | no | The milestone title | | `milestone` | string | no | The milestone title |
| `scope` | string | no | Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all` | | `scope` | string | no | Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all` _([Introduced][ce-13004] in GitLab 9.5)_ |
| `author_id` | integer | no | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned-to-me`. | | `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Return issues assigned to the given user `id` | | `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
| `order_by` | string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | | `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | | `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Search group issues against their `title` and `description` | | `search` | string | no | Search group issues against their `title` and `description` |
```bash ```bash
...@@ -229,12 +231,12 @@ GET /projects/:id/issues?assignee_id=5 ...@@ -229,12 +231,12 @@ GET /projects/:id/issues?assignee_id=5
| `state` | string | no | Return all issues or just those that are `opened` or `closed` | | `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels | | `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
| `milestone` | string | no | The milestone title | | `milestone` | string | no | The milestone title |
| `scope` | string | no | Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all` | | `scope` | string | no | Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all` _([Introduced][ce-13004] in GitLab 9.5)_ |
| `author_id` | integer | no | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned-to-me`. | | `author_id` | integer | no | Return issues created by the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Return issues assigned to the given user `id` | | `assignee_id` | integer | no | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_ |
| `order_by` | string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` | | `order_by` | string | no | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` | | `sort` | string | no | Return issues sorted in `asc` or `desc` order. Default is `desc` |
| `search` | string | no | Search project issues against their `title` and `description` | | `search` | string | no | Search project issues against their `title` and `description` |
| `created_after` | datetime | no | Return issues created after the given time (inclusive) | | `created_after` | datetime | no | Return issues created after the given time (inclusive) |
| `created_before` | datetime | no | Return issues created before the given time (inclusive) | | `created_before` | datetime | no | Return issues created before the given time (inclusive) |
...@@ -1035,3 +1037,5 @@ Example response: ...@@ -1035,3 +1037,5 @@ Example response:
"akismet_submitted": false "akismet_submitted": false
} }
``` ```
[ce-13004]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13004
# Merge requests API # Merge requests API
Every API call to merge requests must be authenticated.
## List merge requests ## List merge requests
> [Introduced][ce-13060] in GitLab 9.5.
Get all merge requests the authenticated user has access to. By
default it returns only merge requests created by the current user. To
get all merge requests, use parameter `scope=all`.
The `state` parameter can be used to get only merge requests with a
given state (`opened`, `closed`, or `merged`) or all of them (`all`).
The pagination parameters `page` and `per_page` can be used to
restrict the list of merge requests.
```
GET /merge_requests
GET /merge_requests?state=opened
GET /merge_requests?state=all
GET /merge_requests?milestone=release
GET /merge_requests?labels=bug,reproduced
GET /merge_requests?author_id=5
GET /merge_requests?scope=assigned-to-me
```
Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, or `merged`|
| `order_by`| string | no | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at` |
| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc` |
| `milestone` | string | no | Return merge requests for a specific milestone |
| `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request |
| `labels` | string | no | Return merge requests matching a comma separated list of labels |
| `created_after` | datetime | no | Return merge requests created after the given time (inclusive) |
| `created_before` | datetime | no | Return merge requests created before the given time (inclusive) |
| `scope` | string | no | Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all`. Defaults to `created-by-me` |
| `author_id` | integer | no | Returns merge requests created by the given user `id`. Combine with `scope=all` or `scope=assigned-to-me` |
| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id` |
```json
[
{
"id": 1,
"iid": 1,
"target_branch": "master",
"source_branch": "test1",
"project_id": 3,
"title": "test1",
"state": "opened",
"upvotes": 0,
"downvotes": 0,
"author": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
},
"assignee": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"name": "Administrator",
"state": "active",
"created_at": "2012-04-29T08:46:00Z"
},
"source_project_id": 2,
"target_project_id": 3,
"labels": [ ],
"description": "fixed login page css paddings",
"work_in_progress": false,
"milestone": {
"id": 5,
"iid": 1,
"project_id": 3,
"title": "v2.0",
"description": "Assumenda aut placeat expedita exercitationem labore sunt enim earum.",
"state": "closed",
"created_at": "2015-02-02T19:49:26.013Z",
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": null
},
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"sha": "8888888888888888888888888888888888888888",
"merge_commit_sha": null,
"user_notes_count": 1,
"should_remove_source_branch": true,
"force_remove_source_branch": false,
"web_url": "http://example.com/example/example/merge_requests/1"
}
]
```
## List project merge requests
Get all merge requests for this project. Get all merge requests for this project.
The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`). The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`).
The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests. The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests.
...@@ -29,6 +126,9 @@ Parameters: ...@@ -29,6 +126,9 @@ Parameters:
| `labels` | string | no | Return merge requests matching a comma separated list of labels | | `labels` | string | no | Return merge requests matching a comma separated list of labels |
| `created_after` | datetime | no | Return merge requests created after the given time (inclusive) | | `created_after` | datetime | no | Return merge requests created after the given time (inclusive) |
| `created_before` | datetime | no | Return merge requests created before the given time (inclusive) | | `created_before` | datetime | no | Return merge requests created before the given time (inclusive) |
| `scope` | string | no | Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all` _([Introduced][ce-13060] in GitLab 9.5)_ |
| `author_id` | integer | no | Returns merge requests created by the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ |
| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id` _([Introduced][ce-13060] in GitLab 9.5)_ |
```json ```json
[ [
...@@ -1165,3 +1265,5 @@ Example response: ...@@ -1165,3 +1265,5 @@ Example response:
"total_time_spent": 3600 "total_time_spent": 3600
} }
``` ```
[ce-13060]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13060
...@@ -32,7 +32,7 @@ module API ...@@ -32,7 +32,7 @@ module API
optional :author_id, type: Integer, desc: 'Return issues which are authored by the user with the given ID' optional :author_id, type: Integer, desc: 'Return issues which are authored by the user with the given ID'
optional :assignee_id, type: Integer, desc: 'Return issues which are assigned to the user with the given ID' optional :assignee_id, type: Integer, desc: 'Return issues which are assigned to the user with the given ID'
optional :scope, type: String, values: %w[created-by-me assigned-to-me all], optional :scope, type: String, values: %w[created-by-me assigned-to-me all],
desc: 'Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all`' desc: 'Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all`'
use :pagination use :pagination
end end
...@@ -60,7 +60,7 @@ module API ...@@ -60,7 +60,7 @@ module API
desc: 'Return opened, closed, or all issues' desc: 'Return opened, closed, or all issues'
use :issues_params use :issues_params
optional :scope, type: String, values: %w[created-by-me assigned-to-me all], default: 'created-by-me', optional :scope, type: String, values: %w[created-by-me assigned-to-me all], default: 'created-by-me',
desc: 'Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all`' desc: 'Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all`'
end end
get do get do
issues = find_issues issues = find_issues
......
...@@ -4,14 +4,77 @@ module API ...@@ -4,14 +4,77 @@ module API
before { authenticate! } before { authenticate! }
helpers ::Gitlab::IssuableMetadata
helpers do
def find_merge_requests(args = {})
args = params.merge(args)
args[:milestone_title] = args.delete(:milestone)
args[:label_name] = args.delete(:labels)
merge_requests = MergeRequestsFinder.new(current_user, args).execute
.reorder(args[:order_by] => args[:sort])
merge_requests = paginate(merge_requests)
.preload(:target_project)
return merge_requests if args[:view] == 'simple'
merge_requests
.preload(:notes, :author, :assignee, :milestone, :merge_request_diff, :labels)
end
params :merge_requests_params do
optional :state, type: String, values: %w[opened closed merged all], default: 'all',
desc: 'Return opened, closed, merged, or all merge requests'
optional :order_by, type: String, values: %w[created_at updated_at], default: 'created_at',
desc: 'Return merge requests ordered by `created_at` or `updated_at` fields.'
optional :sort, type: String, values: %w[asc desc], default: 'desc',
desc: 'Return merge requests sorted in `asc` or `desc` order.'
optional :milestone, type: String, desc: 'Return merge requests for a specific milestone'
optional :labels, type: String, desc: 'Comma-separated list of label names'
optional :created_after, type: DateTime, desc: 'Return merge requests created after the specified time'
optional :created_before, type: DateTime, desc: 'Return merge requests created before the specified time'
optional :view, type: String, values: %w[simple], desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request'
optional :author_id, type: Integer, desc: 'Return merge requests which are authored by the user with the given ID'
optional :assignee_id, type: Integer, desc: 'Return merge requests which are assigned to the user with the given ID'
optional :scope, type: String, values: %w[created-by-me assigned-to-me all],
desc: 'Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all`'
use :pagination
end
end
resource :merge_requests do
desc 'List merge requests' do
success Entities::MergeRequestBasic
end
params do
use :merge_requests_params
optional :scope, type: String, values: %w[created-by-me assigned-to-me all], default: 'created-by-me',
desc: 'Return merge requests for the given scope: `created-by-me`, `assigned-to-me` or `all`'
end
get do
merge_requests = find_merge_requests
options = { with: Entities::MergeRequestBasic,
current_user: current_user }
if params[:view] == 'simple'
options[:with] = Entities::MergeRequestSimple
else
options[:issuable_metadata] = issuable_meta_data(merge_requests, 'MergeRequest')
end
present merge_requests, options
end
end
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: { id: %r{[^/]+} } do
include TimeTrackingEndpoints include TimeTrackingEndpoints
helpers ::Gitlab::IssuableMetadata
helpers do helpers do
def handle_merge_request_errors!(errors) def handle_merge_request_errors!(errors)
if errors[:project_access].any? if errors[:project_access].any?
...@@ -29,23 +92,6 @@ module API ...@@ -29,23 +92,6 @@ module API
render_api_error!(errors, 400) render_api_error!(errors, 400)
end end
def find_merge_requests(args = {})
args = params.merge(args)
args[:milestone_title] = args.delete(:milestone)
args[:label_name] = args.delete(:labels)
merge_requests = MergeRequestsFinder.new(current_user, args).execute
.reorder(args[:order_by] => args[:sort])
merge_requests = paginate(merge_requests)
.preload(:target_project)
return merge_requests if args[:view] == 'simple'
merge_requests
.preload(:notes, :author, :assignee, :milestone, :merge_request_diff, :labels)
end
params :optional_params_ce do params :optional_params_ce do
optional :description, type: String, desc: 'The description of the merge request' optional :description, type: String, desc: 'The description of the merge request'
optional :assignee_id, type: Integer, desc: 'The ID of a user to assign the merge request' optional :assignee_id, type: Integer, desc: 'The ID of a user to assign the merge request'
...@@ -63,19 +109,8 @@ module API ...@@ -63,19 +109,8 @@ module API
success Entities::MergeRequestBasic success Entities::MergeRequestBasic
end end
params do params do
optional :state, type: String, values: %w[opened closed merged all], default: 'all', use :merge_requests_params
desc: 'Return opened, closed, merged, or all merge requests'
optional :order_by, type: String, values: %w[created_at updated_at], default: 'created_at',
desc: 'Return merge requests ordered by `created_at` or `updated_at` fields.'
optional :sort, type: String, values: %w[asc desc], default: 'desc',
desc: 'Return merge requests sorted in `asc` or `desc` order.'
optional :iids, type: Array[Integer], desc: 'The IID array of merge requests' optional :iids, type: Array[Integer], desc: 'The IID array of merge requests'
optional :milestone, type: String, desc: 'Return merge requests for a specific milestone'
optional :labels, type: String, desc: 'Comma-separated list of label names'
optional :created_after, type: DateTime, desc: 'Return merge requests created after the specified time'
optional :created_before, type: DateTime, desc: 'Return merge requests created before the specified time'
optional :view, type: String, values: %w[simple], desc: 'If simple, returns the `iid`, URL, title, description, and basic state of merge request'
use :pagination
end end
get ":id/merge_requests" do get ":id/merge_requests" do
authorize! :read_merge_request, user_project authorize! :read_merge_request, user_project
......
...@@ -26,6 +26,100 @@ describe API::MergeRequests do ...@@ -26,6 +26,100 @@ describe API::MergeRequests do
project.team << [user, :reporter] project.team << [user, :reporter]
end end
describe 'GET /merge_requests' do
context 'when unauthenticated' do
it 'returns authentication error' do
get api('/merge_requests')
expect(response).to have_http_status(401)
end
end
context 'when authenticated' do
let!(:project2) { create(:empty_project, :public, namespace: user.namespace) }
let!(:merge_request2) { create(:merge_request, :simple, author: user, assignee: user, source_project: project2, target_project: project2) }
let(:user2) { create(:user) }
it 'returns an array of all merge requests' do
get api('/merge_requests', user), scope: :all
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.map { |mr| mr['id'] })
.to contain_exactly(merge_request.id, merge_request_closed.id, merge_request_merged.id, merge_request2.id)
end
it 'does not return unauthorized merge requests' do
private_project = create(:empty_project, :private)
merge_request3 = create(:merge_request, :simple, source_project: private_project, target_project: private_project, source_branch: 'other-branch')
get api('/merge_requests', user), scope: :all
expect(response).to have_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.map { |mr| mr['id'] })
.not_to include(merge_request3.id)
end
it 'returns an array of merge requests created by current user if no scope is given' do
merge_request3 = create(:merge_request, :simple, author: user2, assignee: user, source_project: project2, target_project: project2, source_branch: 'other-branch')
get api('/merge_requests', user2)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
expect(json_response.first['id']).to eq(merge_request3.id)
end
it 'returns an array of merge requests authored by the given user' do
merge_request3 = create(:merge_request, :simple, author: user2, assignee: user, source_project: project2, target_project: project2, source_branch: 'other-branch')
get api('/merge_requests', user), author_id: user2.id, scope: :all
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
expect(json_response.first['id']).to eq(merge_request3.id)
end
it 'returns an array of merge requests assigned to the given user' do
merge_request3 = create(:merge_request, :simple, author: user, assignee: user2, source_project: project2, target_project: project2, source_branch: 'other-branch')
get api('/merge_requests', user), assignee_id: user2.id, scope: :all
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
expect(json_response.first['id']).to eq(merge_request3.id)
end
it 'returns an array of merge requests assigned to me' do
merge_request3 = create(:merge_request, :simple, author: user, assignee: user2, source_project: project2, target_project: project2, source_branch: 'other-branch')
get api('/merge_requests', user2), scope: 'assigned-to-me'
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
expect(json_response.first['id']).to eq(merge_request3.id)
end
it 'returns an array of merge requests created by me' do
merge_request3 = create(:merge_request, :simple, author: user2, assignee: user, source_project: project2, target_project: project2, source_branch: 'other-branch')
get api('/merge_requests', user2), scope: 'created-by-me'
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.length).to eq(1)
expect(json_response.first['id']).to eq(merge_request3.id)
end
end
end
describe "GET /projects/:id/merge_requests" do describe "GET /projects/:id/merge_requests" do
context "when unauthenticated" do context "when unauthenticated" do
it "returns authentication error" do it "returns authentication error" 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