Commit 462ff32b authored by Ash McKenzie's avatar Ash McKenzie

Merge branch 'ab/projects-id-filter' into 'master'

Add id_before, id_after filter param to projects API

See merge request gitlab-org/gitlab!19949
parents 9130e9c4 9fba1176
...@@ -110,7 +110,10 @@ class ProjectsFinder < UnionFinder ...@@ -110,7 +110,10 @@ class ProjectsFinder < UnionFinder
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def by_ids(items) def by_ids(items)
project_ids_relation ? items.where(id: project_ids_relation) : items items = items.where(id: project_ids_relation) if project_ids_relation
items = items.where('id > ?', params[:id_after]) if params[:id_after]
items = items.where('id < ?', params[:id_before]) if params[:id_before]
items
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
---
title: Add id_before, id_after filter param to projects API
merge_request: 19949
author:
type: added
...@@ -58,6 +58,8 @@ GET /projects ...@@ -58,6 +58,8 @@ GET /projects
| `wiki_checksum_failed` | boolean | no | **(PREMIUM)** Limit projects where the wiki checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2) | | `wiki_checksum_failed` | boolean | no | **(PREMIUM)** Limit projects where the wiki checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2) |
| `repository_checksum_failed` | boolean | no | **(PREMIUM)** Limit projects where the repository checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2) | | `repository_checksum_failed` | boolean | no | **(PREMIUM)** Limit projects where the repository checksum calculation has failed ([Introduced](https://gitlab.com/gitlab-org/gitlab/merge_requests/6137) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.2) |
| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) | | `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
| `id_after` | integer | no | Limit results to projects with IDs greater than the specified ID |
| `id_before` | integer | no | Limit results to projects with IDs less than the specified ID |
When `simple=true` or the user is unauthenticated this returns something like: When `simple=true` or the user is unauthenticated this returns something like:
...@@ -304,6 +306,8 @@ GET /users/:user_id/projects ...@@ -304,6 +306,8 @@ GET /users/:user_id/projects
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature | | `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
| `with_programming_language` | string | no | Limit by projects which use the given programming language | | `with_programming_language` | string | no | Limit by projects which use the given programming language |
| `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) | | `min_access_level` | integer | no | Limit by current user minimal [access level](members.md) |
| `id_after` | integer | no | Limit results to projects with IDs greater than the specified ID |
| `id_before` | integer | no | Limit results to projects with IDs less than the specified ID |
```json ```json
[ [
......
...@@ -479,6 +479,8 @@ module API ...@@ -479,6 +479,8 @@ module API
finder_params[:user] = params.delete(:user) if params[:user] finder_params[:user] = params.delete(:user) if params[:user]
finder_params[:custom_attributes] = params[:custom_attributes] if params[:custom_attributes] finder_params[:custom_attributes] = params[:custom_attributes] if params[:custom_attributes]
finder_params[:min_access_level] = params[:min_access_level] if params[:min_access_level] finder_params[:min_access_level] = params[:min_access_level] if params[:min_access_level]
finder_params[:id_after] = params[:id_after] if params[:id_after]
finder_params[:id_before] = params[:id_before] if params[:id_before]
finder_params finder_params
end end
......
...@@ -61,6 +61,8 @@ module API ...@@ -61,6 +61,8 @@ module API
optional :with_merge_requests_enabled, type: Boolean, default: false, desc: 'Limit by enabled merge requests feature' optional :with_merge_requests_enabled, type: Boolean, default: false, desc: 'Limit by enabled merge requests feature'
optional :with_programming_language, type: String, desc: 'Limit to repositories which use the given programming language' optional :with_programming_language, type: String, desc: 'Limit to repositories which use the given programming language'
optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user' optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user'
optional :id_after, type: Integer, desc: 'Limit results to projects with IDs greater than the specified ID'
optional :id_before, type: Integer, desc: 'Limit results to projects with IDs less than the specified ID'
use :optional_filter_params_ee use :optional_filter_params_ee
end end
......
...@@ -58,6 +58,31 @@ describe ProjectsFinder, :do_not_mock_admin_mode do ...@@ -58,6 +58,31 @@ describe ProjectsFinder, :do_not_mock_admin_mode do
it { is_expected.to eq([internal_project]) } it { is_expected.to eq([internal_project]) }
end end
describe 'with id_after' do
context 'only returns projects with a project id greater than given' do
let(:params) { { id_after: internal_project.id }}
it { is_expected.to eq([public_project]) }
end
end
describe 'with id_before' do
context 'only returns projects with a project id less than given' do
let(:params) { { id_before: public_project.id }}
it { is_expected.to eq([internal_project]) }
end
end
describe 'with both id_before and id_after' do
context 'only returns projects with a project id less than given' do
let!(:projects) { create_list(:project, 5, :public) }
let(:params) { { id_after: projects.first.id, id_before: projects.last.id }}
it { is_expected.to contain_exactly(*projects[1..-2]) }
end
end
describe 'filter by visibility_level' do describe 'filter by visibility_level' do
before do before do
private_project.add_maintainer(user) private_project.add_maintainer(user)
......
...@@ -362,6 +362,30 @@ describe API::Projects do ...@@ -362,6 +362,30 @@ describe API::Projects do
end end
end end
context 'and using id_after' do
it_behaves_like 'projects response' do
let(:filter) { { id_after: project2.id } }
let(:current_user) { user }
let(:projects) { [public_project, project, project2, project3].select { |p| p.id > project2.id } }
end
end
context 'and using id_before' do
it_behaves_like 'projects response' do
let(:filter) { { id_before: project2.id } }
let(:current_user) { user }
let(:projects) { [public_project, project, project2, project3].select { |p| p.id < project2.id } }
end
end
context 'and using both id_after and id_before' do
it_behaves_like 'projects response' do
let(:filter) { { id_before: project2.id, id_after: public_project.id } }
let(:current_user) { user }
let(:projects) { [public_project, project, project2, project3].select { |p| p.id < project2.id && p.id > public_project.id } }
end
end
context 'and membership=true' do context 'and membership=true' do
it_behaves_like 'projects response' do it_behaves_like 'projects response' do
let(:filter) { { membership: true } } let(:filter) { { membership: true } }
...@@ -848,6 +872,63 @@ describe API::Projects do ...@@ -848,6 +872,63 @@ describe API::Projects do
expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id) expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id)
end end
context 'and using id_after' do
let!(:another_public_project) { create(:project, :public, name: 'another_public_project', creator_id: user4.id, namespace: user4.namespace) }
it 'only returns projects with id_after filter given' do
get api("/users/#{user4.id}/projects?id_after=#{public_project.id}", user)
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.map { |project| project['id'] }).to contain_exactly(another_public_project.id)
end
it 'returns both projects without a id_after filter' do
get api("/users/#{user4.id}/projects", user)
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id, another_public_project.id)
end
end
context 'and using id_before' do
let!(:another_public_project) { create(:project, :public, name: 'another_public_project', creator_id: user4.id, namespace: user4.namespace) }
it 'only returns projects with id_before filter given' do
get api("/users/#{user4.id}/projects?id_before=#{another_public_project.id}", user)
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id)
end
it 'returns both projects without a id_before filter' do
get api("/users/#{user4.id}/projects", user)
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.map { |project| project['id'] }).to contain_exactly(public_project.id, another_public_project.id)
end
end
context 'and using both id_before and id_after' do
let!(:more_projects) { create_list(:project, 5, :public, creator_id: user4.id, namespace: user4.namespace) }
it 'only returns projects with id matching the range' do
get api("/users/#{user4.id}/projects?id_after=#{more_projects.first.id}&id_before=#{more_projects.last.id}", user)
expect(response).to have_gitlab_http_status(200)
expect(response).to include_pagination_headers
expect(json_response).to be_an Array
expect(json_response.map { |project| project['id'] }).to contain_exactly(*more_projects[1..-2].map(&:id))
end
end
it 'returns projects filtered by username' do it 'returns projects filtered by username' do
get api("/users/#{user4.username}/projects/", user) get api("/users/#{user4.username}/projects/", user)
......
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