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 | 453 | + `id` (required) - The ID of the project. |
454 | 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 | 481 | \ No newline at end of file | ... | ... |
lib/api/entities.rb
... | ... | @@ -25,6 +25,12 @@ module API |
25 | 25 | expose :id, :url, :created_at |
26 | 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 | 34 | class Project < Grape::Entity |
29 | 35 | expose :id, :description, :default_branch, :public, :ssh_url_to_repo, :http_url_to_repo, :web_url |
30 | 36 | expose :owner, using: Entities::UserBasic |
... | ... | @@ -32,6 +38,7 @@ module API |
32 | 38 | expose :path, :path_with_namespace |
33 | 39 | expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at, :last_activity_at |
34 | 40 | expose :namespace |
41 | + expose :forked_from_project, using: Entities::ForkedFromProject, :if => lambda{ | project, options | project.forked? } | |
35 | 42 | end |
36 | 43 | |
37 | 44 | class ProjectMember < UserBasic | ... | ... |
lib/api/helpers.rb
... | ... | @@ -5,12 +5,12 @@ module API |
5 | 5 | end |
6 | 6 | |
7 | 7 | def user_project |
8 | - @project ||= find_project | |
8 | + @project ||= find_project(params[:id]) | |
9 | 9 | @project || not_found! |
10 | 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 | 15 | if project && can?(current_user, :read_project, project) |
16 | 16 | project | ... | ... |
lib/api/projects.rb
... | ... | @@ -121,6 +121,42 @@ module API |
121 | 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 | 160 | # Get a project team members |
125 | 161 | # |
126 | 162 | # Parameters: | ... | ... |
spec/requests/api/projects_spec.rb
... | ... | @@ -595,4 +595,71 @@ describe API::API do |
595 | 595 | end |
596 | 596 | end |
597 | 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 | 665 | end | ... | ... |