Commit ea5a006f27cfd3013f94652e0e0f0e63091036ad
1 parent
7ebbb6e3
Exists in
master
and in
4 other branches
Additon of apis for fork administration.
Added ability to add and remove the forked from/to relatioinship between existing repos.
Showing
5 changed files
with
138 additions
and
3 deletions
Show diff stats
doc/api/projects.md
@@ -453,3 +453,28 @@ Parameters: | @@ -453,3 +453,28 @@ Parameters: | ||
453 | + `id` (required) - The ID of the project. | 453 | + `id` (required) - The ID of the project. |
454 | + `branch` (required) - The name of the branch. | 454 | + `branch` (required) - The name of the branch. |
455 | 455 | ||
456 | + | ||
457 | +## Admin fork relation | ||
458 | + | ||
459 | +Allows modification of the forked relationship between existing projects. . Available only for admins. | ||
460 | + | ||
461 | +### Create a forked from/to relation between existing projects. | ||
462 | + | ||
463 | +``` | ||
464 | +POST /projects/:id/fork/:forked_from_id | ||
465 | +``` | ||
466 | + | ||
467 | +Parameters: | ||
468 | + | ||
469 | ++ `id` (required) - The ID of the project | ||
470 | ++ `forked_from_id:` (required) - The ID of the project that was forked from | ||
471 | + | ||
472 | +### Delete an existing forked from relationship | ||
473 | + | ||
474 | +``` | ||
475 | +DELETE /projects/:id/fork | ||
476 | +``` | ||
477 | + | ||
478 | +Parameter: | ||
479 | + | ||
480 | ++ `id` (required) - The ID of the project | ||
456 | \ No newline at end of file | 481 | \ No newline at end of file |
lib/api/entities.rb
@@ -25,6 +25,12 @@ module API | @@ -25,6 +25,12 @@ module API | ||
25 | expose :id, :url, :created_at | 25 | expose :id, :url, :created_at |
26 | end | 26 | end |
27 | 27 | ||
28 | + class ForkedFromProject < Grape::Entity | ||
29 | + expose :id | ||
30 | + expose :name, :name_with_namespace | ||
31 | + expose :path, :path_with_namespace | ||
32 | + end | ||
33 | + | ||
28 | class Project < Grape::Entity | 34 | class Project < Grape::Entity |
29 | expose :id, :description, :default_branch, :public, :ssh_url_to_repo, :http_url_to_repo, :web_url | 35 | expose :id, :description, :default_branch, :public, :ssh_url_to_repo, :http_url_to_repo, :web_url |
30 | expose :owner, using: Entities::UserBasic | 36 | expose :owner, using: Entities::UserBasic |
@@ -32,6 +38,7 @@ module API | @@ -32,6 +38,7 @@ module API | ||
32 | expose :path, :path_with_namespace | 38 | expose :path, :path_with_namespace |
33 | expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at, :last_activity_at | 39 | expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at, :last_activity_at |
34 | expose :namespace | 40 | expose :namespace |
41 | + expose :forked_from_project, using: Entities::ForkedFromProject, :if => lambda{ | project, options | project.forked? } | ||
35 | end | 42 | end |
36 | 43 | ||
37 | class ProjectMember < UserBasic | 44 | class ProjectMember < UserBasic |
lib/api/helpers.rb
@@ -5,12 +5,12 @@ module API | @@ -5,12 +5,12 @@ module API | ||
5 | end | 5 | end |
6 | 6 | ||
7 | def user_project | 7 | def user_project |
8 | - @project ||= find_project | 8 | + @project ||= find_project(params[:id]) |
9 | @project || not_found! | 9 | @project || not_found! |
10 | end | 10 | end |
11 | 11 | ||
12 | - def find_project | ||
13 | - project = Project.find_by_id(params[:id]) || Project.find_with_namespace(params[:id]) | 12 | + def find_project(id) |
13 | + project = Project.find_by_id(id) || Project.find_with_namespace(id) | ||
14 | 14 | ||
15 | if project && can?(current_user, :read_project, project) | 15 | if project && can?(current_user, :read_project, project) |
16 | project | 16 | project |
lib/api/projects.rb
@@ -121,6 +121,42 @@ module API | @@ -121,6 +121,42 @@ module API | ||
121 | end | 121 | end |
122 | 122 | ||
123 | 123 | ||
124 | + # Mark this project as forked from another | ||
125 | + # | ||
126 | + # Parameters: | ||
127 | + # id: (required) - The ID of the project being marked as a fork | ||
128 | + # forked_from_id: (required) - The ID of the project it was forked from | ||
129 | + # Example Request: | ||
130 | + # POST /projects/:id/fork/:forked_from_id | ||
131 | + post ":id/fork/:forked_from_id" do | ||
132 | + authenticated_as_admin! | ||
133 | + forked_from_project = find_project(params[:forked_from_id]) | ||
134 | + unless forked_from_project.nil? | ||
135 | + if user_project.forked_from_project.nil? | ||
136 | + user_project.create_forked_project_link(forked_to_project_id: user_project.id, forked_from_project_id: forked_from_project.id) | ||
137 | + else | ||
138 | + render_api_error!("Project already forked", 409) | ||
139 | + end | ||
140 | + else | ||
141 | + not_found! | ||
142 | + end | ||
143 | + | ||
144 | + end | ||
145 | + | ||
146 | + # Remove a forked_from relationship | ||
147 | + # | ||
148 | + # Parameters: | ||
149 | + # id: (required) - The ID of the project being marked as a fork | ||
150 | + # Example Request: | ||
151 | + # DELETE /projects/:id/fork | ||
152 | + delete ":id/fork" do | ||
153 | + authenticated_as_admin! | ||
154 | + unless user_project.forked_project_link.nil? | ||
155 | + user_project.forked_project_link.destroy | ||
156 | + end | ||
157 | + end | ||
158 | + | ||
159 | + | ||
124 | # Get a project team members | 160 | # Get a project team members |
125 | # | 161 | # |
126 | # Parameters: | 162 | # Parameters: |
spec/requests/api/projects_spec.rb
@@ -595,4 +595,71 @@ describe API::API do | @@ -595,4 +595,71 @@ describe API::API do | ||
595 | end | 595 | end |
596 | end | 596 | end |
597 | end | 597 | end |
598 | + | ||
599 | + describe :fork_admin do | ||
600 | + let(:project_fork_target) { create(:project) } | ||
601 | + let(:project_fork_source) { create(:project, public: true) } | ||
602 | + | ||
603 | + describe "POST /projects/:id/fork/:forked_from_id" do | ||
604 | + let(:new_project_fork_source) { create(:project, public: true) } | ||
605 | + | ||
606 | + it "shouldn't available for non admin users" do | ||
607 | + post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user) | ||
608 | + response.status.should == 403 | ||
609 | + end | ||
610 | + | ||
611 | + it "should allow project to be forked from an existing project" do | ||
612 | + project_fork_target.forked?.should_not be_true | ||
613 | + post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin) | ||
614 | + response.status.should == 201 | ||
615 | + project_fork_target.reload | ||
616 | + project_fork_target.forked_from_project.id.should == project_fork_source.id | ||
617 | + project_fork_target.forked_project_link.should_not be_nil | ||
618 | + project_fork_target.forked?.should be_true | ||
619 | + end | ||
620 | + | ||
621 | + it "should fail if forked_from project which does not exist" do | ||
622 | + post api("/projects/#{project_fork_target.id}/fork/9999", admin) | ||
623 | + response.status.should == 404 | ||
624 | + end | ||
625 | + | ||
626 | + it "should fail with 409 if already forked" do | ||
627 | + post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin) | ||
628 | + project_fork_target.reload | ||
629 | + project_fork_target.forked_from_project.id.should == project_fork_source.id | ||
630 | + post api("/projects/#{project_fork_target.id}/fork/#{new_project_fork_source.id}", admin) | ||
631 | + response.status.should == 409 | ||
632 | + project_fork_target.reload | ||
633 | + project_fork_target.forked_from_project.id.should == project_fork_source.id | ||
634 | + project_fork_target.forked?.should be_true | ||
635 | + end | ||
636 | + end | ||
637 | + | ||
638 | + describe "DELETE /projects/:id/fork" do | ||
639 | + | ||
640 | + it "shouldn't available for non admin users" do | ||
641 | + delete api("/projects/#{project_fork_target.id}/fork", user) | ||
642 | + response.status.should == 403 | ||
643 | + end | ||
644 | + | ||
645 | + it "should make forked project unforked" do | ||
646 | + post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin) | ||
647 | + project_fork_target.reload | ||
648 | + project_fork_target.forked_from_project.should_not be_nil | ||
649 | + project_fork_target.forked?.should be_true | ||
650 | + delete api("/projects/#{project_fork_target.id}/fork", admin) | ||
651 | + response.status.should == 200 | ||
652 | + project_fork_target.reload | ||
653 | + project_fork_target.forked_from_project.should be_nil | ||
654 | + project_fork_target.forked?.should_not be_true | ||
655 | + end | ||
656 | + | ||
657 | + it "should be idempotent if not forked" do | ||
658 | + project_fork_target.forked_from_project.should be_nil | ||
659 | + delete api("/projects/#{project_fork_target.id}/fork", admin) | ||
660 | + response.status.should == 200 | ||
661 | + project_fork_target.reload.forked_from_project.should be_nil | ||
662 | + end | ||
663 | + end | ||
664 | + end | ||
598 | end | 665 | end |