summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Fletcher <mark@gitlab.com>2017-09-19 15:48:22 +1000
committerMark Fletcher <mark@gitlab.com>2017-09-20 08:27:16 +1000
commit63e0a42317cad084b982d1deff8737bd2a1bf2d1 (patch)
tree259993080deba01244a6fd17bc0db1c260cffd80
parent5f331ab7008ae6d5931873fb674daf9bc8aa06be (diff)
downloadgitlab-ce-63e0a42317cad084b982d1deff8737bd2a1bf2d1.tar.gz
Add an API endpoint to determine the forks of a project
-rw-r--r--changelogs/unreleased/20049-projects-api-forks.yml5
-rw-r--r--doc/api/projects.md92
-rw-r--r--lib/api/projects.rb23
-rw-r--r--spec/requests/api/projects_spec.rb53
4 files changed, 169 insertions, 4 deletions
diff --git a/changelogs/unreleased/20049-projects-api-forks.yml b/changelogs/unreleased/20049-projects-api-forks.yml
new file mode 100644
index 00000000000..c6470620f57
--- /dev/null
+++ b/changelogs/unreleased/20049-projects-api-forks.yml
@@ -0,0 +1,5 @@
+---
+title: Add an API endpoint to determine the forks of a project
+merge_request:
+author:
+type: added
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 3144220e588..07331d05231 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -635,6 +635,98 @@ POST /projects/:id/fork
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `namespace` | integer/string | yes | The ID or path of the namespace that the project will be forked to |
+## List Forks of a project
+
+>**Note:** This feature was introduced in GitLab 10.1
+
+List the projects accessible to the calling user that have an established, forked relationship with the specified project
+
+```
+GET /projects/:id/forks
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `archived` | boolean | no | Limit by archived status |
+| `visibility` | string | no | Limit by visibility `public`, `internal`, or `private` |
+| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` |
+| `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
+| `search` | string | no | Return list of projects matching the search criteria |
+| `simple` | boolean | no | Return only the ID, URL, name, and path of each project |
+| `owned` | boolean | no | Limit by projects owned by the current user |
+| `membership` | boolean | no | Limit by projects that the current user is a member of |
+| `starred` | boolean | no | Limit by projects starred by the current user |
+| `statistics` | boolean | no | Include project statistics |
+| `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
+| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
+
+```bash
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/forks"
+```
+
+Example responses:
+
+```json
+[
+ {
+ "id": 3,
+ "description": null,
+ "default_branch": "master",
+ "visibility": "internal",
+ "ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git",
+ "http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git",
+ "web_url": "http://example.com/diaspora/diaspora-project-site",
+ "tag_list": [
+ "example",
+ "disapora project"
+ ],
+ "name": "Diaspora Project Site",
+ "name_with_namespace": "Diaspora / Diaspora Project Site",
+ "path": "diaspora-project-site",
+ "path_with_namespace": "diaspora/diaspora-project-site",
+ "issues_enabled": true,
+ "open_issues_count": 1,
+ "merge_requests_enabled": true,
+ "jobs_enabled": true,
+ "wiki_enabled": true,
+ "snippets_enabled": false,
+ "resolve_outdated_diff_discussions": false,
+ "container_registry_enabled": false,
+ "created_at": "2013-09-30T13:46:02Z",
+ "last_activity_at": "2013-09-30T13:46:02Z",
+ "creator_id": 3,
+ "namespace": {
+ "id": 3,
+ "name": "Diaspora",
+ "path": "diaspora",
+ "kind": "group",
+ "full_path": "diaspora"
+ },
+ "import_status": "none",
+ "archived": true,
+ "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png",
+ "shared_runners_enabled": true,
+ "forks_count": 0,
+ "star_count": 1,
+ "public_jobs": true,
+ "shared_with_groups": [],
+ "only_allow_merge_if_pipeline_succeeds": false,
+ "only_allow_merge_if_all_discussions_are_resolved": false,
+ "request_access_enabled": false,
+ "_links": {
+ "self": "http://example.com/api/v4/projects",
+ "issues": "http://example.com/api/v4/projects/1/issues",
+ "merge_requests": "http://example.com/api/v4/projects/1/merge_requests",
+ "repo_branches": "http://example.com/api/v4/projects/1/repository_branches",
+ "labels": "http://example.com/api/v4/projects/1/labels",
+ "events": "http://example.com/api/v4/projects/1/events",
+ "members": "http://example.com/api/v4/projects/1/members"
+ }
+ }
+]
+```
+
## Star a project
Stars a given project. Returns status code `304` if the project is already starred.
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 7dc19788462..aab7a6c3f93 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -70,8 +70,11 @@ module API
optional :import_url, type: String, desc: 'URL from which the project is imported'
end
- def present_projects(options = {})
- projects = ProjectsFinder.new(current_user: current_user, params: project_finder_params).execute
+ def load_projects
+ ProjectsFinder.new(current_user: current_user, params: project_finder_params).execute
+ end
+
+ def present_projects(projects, options = {})
projects = reorder_projects(projects)
projects = projects.with_statistics if params[:statistics]
projects = projects.with_issues_enabled if params[:with_issues_enabled]
@@ -111,7 +114,7 @@ module API
params[:user] = user
- present_projects
+ present_projects load_projects
end
end
@@ -124,7 +127,7 @@ module API
use :statistics_params
end
get do
- present_projects
+ present_projects load_projects
end
desc 'Create new project' do
@@ -229,6 +232,18 @@ module API
end
end
+ desc 'List forks of this project' do
+ success Entities::Project
+ end
+ params do
+ use :collection_params
+ end
+ get ':id/forks' do
+ forks = ForkProjectsFinder.new(user_project, params: project_finder_params, current_user: current_user).execute
+
+ present_projects forks
+ end
+
desc 'Update an existing project' do
success Entities::Project
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 92e7d797cbd..508df990952 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1181,6 +1181,59 @@ describe API::Projects do
end
end
end
+
+ describe 'GET /projects/:id/forks' do
+ let(:private_fork) { create(:project, :private, :empty_repo) }
+ let(:member) { create(:user) }
+ let(:non_member) { create(:user) }
+
+ before do
+ private_fork.add_developer(member)
+ end
+
+ context 'for a forked project' do
+ before do
+ post api("/projects/#{private_fork.id}/fork/#{project_fork_source.id}", admin)
+ private_fork.reload
+ expect(private_fork.forked_from_project).not_to be_nil
+ expect(private_fork.forked?).to be_truthy
+ project_fork_source.reload
+ expect(project_fork_source.forks.length).to eq(1)
+ expect(project_fork_source.forks).to include(private_fork)
+ end
+
+ context 'for a user that can access the forks' do
+ it 'returns the forks' do
+ get api("/projects/#{project_fork_source.id}/forks", member)
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response.length).to eq(1)
+ expect(json_response[0]['name']).to eq(private_fork.name)
+ end
+ end
+
+ context 'for a user that cannot access the forks' do
+ it 'returns an empty array' do
+ get api("/projects/#{project_fork_source.id}/forks", non_member)
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response.length).to eq(0)
+ end
+ end
+ end
+
+ context 'for a non-forked project' do
+ it 'returns an empty array' do
+ get api("/projects/#{project_fork_source.id}/forks")
+
+ expect(response).to have_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response.length).to eq(0)
+ end
+ end
+ end
end
describe "POST /projects/:id/share" do