diff options
| -rw-r--r-- | app/finders/merge_requests_finder.rb | 1 | ||||
| -rw-r--r-- | changelogs/unreleased/tc-api-root-merge-requests.yml | 4 | ||||
| -rw-r--r-- | doc/api/issues.md | 40 | ||||
| -rw-r--r-- | doc/api/merge_requests.md | 102 | ||||
| -rw-r--r-- | lib/api/issues.rb | 4 | ||||
| -rw-r--r-- | lib/api/merge_requests.rb | 97 | ||||
| -rw-r--r-- | spec/requests/api/merge_requests_spec.rb | 94 | 
7 files changed, 291 insertions, 51 deletions
| diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb index 2fc34f186ad..771da3d441d 100644 --- a/app/finders/merge_requests_finder.rb +++ b/app/finders/merge_requests_finder.rb @@ -10,6 +10,7 @@  #     group_id: integer  #     project_id: integer  #     milestone_title: string +#     author_id: integer  #     assignee_id: integer  #     search: string  #     label_name: string diff --git a/changelogs/unreleased/tc-api-root-merge-requests.yml b/changelogs/unreleased/tc-api-root-merge-requests.yml new file mode 100644 index 00000000000..17456f943eb --- /dev/null +++ b/changelogs/unreleased/tc-api-root-merge-requests.yml @@ -0,0 +1,4 @@ +--- +title: Add top-level merge_requests API endpoint +merge_request: 13060 +author: diff --git a/doc/api/issues.md b/doc/api/issues.md index 6cb2eac37c2..6bac2927339 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -14,7 +14,9 @@ Read more on [pagination](README.md#pagination).  ## 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 @@ -35,12 +37,12 @@ GET /issues?assignee_id=5  | `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 |  | `milestone` | string         | no       | The milestone title                                                                                                         | -| `scope`     | string         | no       | Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all`                                               | -| `author_id` | integer        | no       | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned-to-me`.                           | -| `assignee_id` | integer      | no       | Return issues assigned to the given user `id`                                                                               | +| `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`. _([Introduced][ce-13004] in GitLab 9.5)_ | +| `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`                                                                               | -| `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`                                                          | +| `order_by`  | string         | no       | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at`                                       | +| `sort`      | string         | no       | Return issues sorted in `asc` or `desc` order. Default is `desc`                                                            |  | `search`    | string         | no       | Search issues against their `title` and `description`                                                                       |  ```bash @@ -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 |  | `iids`      | Array[integer] | no       | Return only the issues having the given `iid`                                                                               |  | `milestone` | string         | no       | The milestone title                                                                                                         | -| `scope`     | string         | no       | Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all`                                               | -| `author_id` | integer        | no       | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned-to-me`.                           | -| `assignee_id` | integer      | no       | Return issues assigned to the given user `id`                                                                               | -| `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`                                                          | -| `search`    | string         | no       | Search group issues against their `title` and `description`                                                                  | +| `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` _([Introduced][ce-13004] in GitLab 9.5)_                                       | +| `assignee_id` | integer      | no       | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_                                      | +| `order_by`  | string         | no       | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at`                                       | +| `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`                                                                 |  ```bash @@ -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`                                                               |  | `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                                                                                                         | -| `scope`     | string         | no       | Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all`                                               | -| `author_id` | integer        | no       | Return issues created by the given user `id`. Combine with `scope=all` or `scope=assigned-to-me`.                           | -| `assignee_id` | integer      | no       | Return issues assigned to the given user `id`                                                                               | -| `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`                                                          | -| `search`    | string         | no       | Search project issues against their `title` and `description`                                                                | +| `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` _([Introduced][ce-13004] in GitLab 9.5)_                                       | +| `assignee_id` | integer      | no       | Return issues assigned to the given user `id` _([Introduced][ce-13004] in GitLab 9.5)_                                      | +| `order_by`  | string         | no       | Return issues ordered by `created_at` or `updated_at` fields. Default is `created_at`                                       | +| `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`                                                               |  | `created_after` | datetime | no | Return issues created after the given time (inclusive) |  | `created_before` | datetime | no | Return issues created before the given time (inclusive) | @@ -1035,3 +1037,5 @@ Example response:    "akismet_submitted": false  }  ``` + +[ce-13004]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13004 diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index c90d95e4dd0..d0725b5e06e 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -1,7 +1,104 @@  # Merge requests API +Every API call to merge requests must be authenticated. +  ## 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.  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. @@ -29,6 +126,9 @@ Parameters:  | `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` _([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  [ @@ -1165,3 +1265,5 @@ Example response:    "total_time_spent": 3600  }  ``` + +[ce-13060]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13060 diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 009c6d6bcd4..4cec1145f3a 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -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 :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], -                         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        end @@ -60,7 +60,7 @@ module API                           desc: 'Return opened, closed, or all issues'          use :issues_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`' +                         desc: 'Return issues for the given scope: `created-by-me`, `assigned-to-me` or `all`'        end        get do          issues = find_issues diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb index f64ac659413..8810d4e441d 100644 --- a/lib/api/merge_requests.rb +++ b/lib/api/merge_requests.rb @@ -4,14 +4,77 @@ module API      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        requires :id, type: String, desc: 'The ID of a project'      end      resource :projects, requirements: { id: %r{[^/]+} } do        include TimeTrackingEndpoints -      helpers ::Gitlab::IssuableMetadata -        helpers do          def handle_merge_request_errors!(errors)            if errors[:project_access].any? @@ -29,23 +92,6 @@ module API            render_api_error!(errors, 400)          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            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' @@ -63,19 +109,8 @@ module API          success Entities::MergeRequestBasic        end        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.' +        use :merge_requests_params          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        get ":id/merge_requests" do          authorize! :read_merge_request, user_project diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 35b6522ea98..2760c4ffde2 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -26,6 +26,100 @@ describe API::MergeRequests do      project.team << [user, :reporter]    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      context "when unauthenticated" do        it "returns authentication error" do | 
