diff options
author | Kerri Miller <kerrizor@kerrizor.com> | 2019-05-31 14:21:15 +0000 |
---|---|---|
committer | Lin Jen-Shin <godfat@godfat.org> | 2019-05-31 14:21:15 +0000 |
commit | 35c37fb29343974bf2edc3959d6a21db9c384307 (patch) | |
tree | e5f0065b8b9347c3920190e476e5ba816497c23f | |
parent | 9a8955d3c06c23cfc95914254ec77cc78e2cf6b5 (diff) | |
download | gitlab-ce-35c37fb29343974bf2edc3959d6a21db9c384307.tar.gz |
Add optional param :start_project to allow variable commit targets
This extends POST#:id/repository/commits to allow the optional parameter
`:start_project`, which will allow targeting other projects besides the
one derived from `:id`.
Resolves https://gitlab.com/gitlab-org/gitlab-ce/issues/50850
-rw-r--r-- | changelogs/unreleased/50850-kerrizor-extend-api-to-accept-start_project-option.yml | 5 | ||||
-rw-r--r-- | doc/api/commits.md | 1 | ||||
-rw-r--r-- | lib/api/commits.rb | 10 | ||||
-rw-r--r-- | spec/requests/api/commits_spec.rb | 92 |
4 files changed, 108 insertions, 0 deletions
diff --git a/changelogs/unreleased/50850-kerrizor-extend-api-to-accept-start_project-option.yml b/changelogs/unreleased/50850-kerrizor-extend-api-to-accept-start_project-option.yml new file mode 100644 index 00000000000..45770e1012c --- /dev/null +++ b/changelogs/unreleased/50850-kerrizor-extend-api-to-accept-start_project-option.yml @@ -0,0 +1,5 @@ +--- +title: Add API support for committing changes to different projects in same fork network +merge_request: 27915 +author: +type: added diff --git a/doc/api/commits.md b/doc/api/commits.md index 92f53c7b5e6..25015fad9e3 100644 --- a/doc/api/commits.md +++ b/doc/api/commits.md @@ -75,6 +75,7 @@ POST /projects/:id/repository/commits | `branch` | string | yes | Name of the branch to commit into. To create a new branch, also provide `start_branch`. | | `commit_message` | string | yes | Commit message | | `start_branch` | string | no | Name of the branch to start the new commit from | +| `start_project` | integer/string | no | The project ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) to start the commit from. Defaults to the value of `id`. | | `actions[]` | array | yes | An array of action hashes to commit as a batch. See the next table for what attributes it can take. | | `author_email` | string | no | Specify the commit author's email address | | `author_name` | string | no | Specify the commit author's name | diff --git a/lib/api/commits.rb b/lib/api/commits.rb index 65eb9bfb87e..80913f4ca07 100644 --- a/lib/api/commits.rb +++ b/lib/api/commits.rb @@ -96,17 +96,27 @@ module API end end optional :start_branch, type: String, desc: 'Name of the branch to start the new commit from' + optional :start_project, types: [Integer, String], desc: 'The ID or path of the project to start the commit from' optional :author_email, type: String, desc: 'Author email for commit' optional :author_name, type: String, desc: 'Author name for commit' optional :stats, type: Boolean, default: true, desc: 'Include commit stats' optional :force, type: Boolean, default: false, desc: 'When `true` overwrites the target branch with a new commit based on the `start_branch`' end post ':id/repository/commits' do + if params[:start_project] + start_project = find_project!(params[:start_project]) + + unless user_project.forked_from?(start_project) + forbidden!("Project is not included in the fork network for #{start_project.full_name}") + end + end + authorize_push_to_branch!(params[:branch]) attrs = declared_params attrs[:branch_name] = attrs.delete(:branch) attrs[:start_branch] ||= attrs[:branch_name] + attrs[:start_project] = start_project if start_project result = ::Files::MultiService.new(user_project, current_user, attrs).execute diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index a132b85b878..f104da6ebba 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -2,6 +2,8 @@ require 'spec_helper' require 'mime/types' describe API::Commits do + include ProjectForksHelper + let(:user) { create(:user) } let(:guest) { create(:user).tap { |u| project.add_guest(u) } } let(:project) { create(:project, :repository, creator: user, path: 'my.project') } @@ -317,6 +319,96 @@ describe API::Commits do expect(response).to have_gitlab_http_status(201) end end + + context 'when the API user is a guest' do + def last_commit_id(project, branch_name) + project.repository.find_branch(branch_name)&.dereferenced_target&.id + end + + let(:public_project) { create(:project, :public, :repository) } + let!(:url) { "/projects/#{public_project.id}/repository/commits" } + let(:guest) { create(:user).tap { |u| public_project.add_guest(u) } } + + it 'returns a 403' do + post api(url, guest), params: valid_c_params + + expect(response).to have_gitlab_http_status(403) + end + + context 'when start_project is provided' do + context 'when posting to a forked project the user owns' do + let!(:forked_project) { fork_project(public_project, guest, namespace: guest.namespace, repository: true) } + let!(:url) { "/projects/#{forked_project.id}/repository/commits" } + + before do + valid_c_params[:start_branch] = "master" + valid_c_params[:branch] = "patch" + end + + context 'identified by Integer (id)' do + before do + valid_c_params[:start_project] = public_project.id + end + + it 'adds a new commit to forked_project and returns a 201' do + expect { post api(url, guest), params: valid_c_params } + .to change { last_commit_id(forked_project, valid_c_params[:branch]) } + .and not_change { last_commit_id(public_project, valid_c_params[:start_branch]) } + + expect(response).to have_gitlab_http_status(201) + end + end + + context 'identified by String (full_path)' do + before do + valid_c_params[:start_project] = public_project.full_path + end + + it 'adds a new commit to forked_project and returns a 201' do + expect { post api(url, guest), params: valid_c_params } + .to change { last_commit_id(forked_project, valid_c_params[:branch]) } + .and not_change { last_commit_id(public_project, valid_c_params[:start_branch]) } + + expect(response).to have_gitlab_http_status(201) + end + end + end + + context 'when the target project is not part of the fork network of start_project' do + let(:unrelated_project) { create(:project, :public, :repository, creator: guest) } + let!(:url) { "/projects/#{unrelated_project.id}/repository/commits" } + + before do + valid_c_params[:start_branch] = "master" + valid_c_params[:branch] = "patch" + valid_c_params[:start_project] = public_project.id + end + + it 'returns a 403' do + post api(url, guest), params: valid_c_params + + expect(response).to have_gitlab_http_status(403) + end + end + end + + context 'when posting to a forked project the user does not have write access' do + let!(:forked_project) { fork_project(public_project, user, namespace: user.namespace, repository: true) } + let!(:url) { "/projects/#{forked_project.id}/repository/commits" } + + before do + valid_c_params[:start_branch] = "master" + valid_c_params[:branch] = "patch" + valid_c_params[:start_project] = public_project.id + end + + it 'returns a 403' do + post api(url, guest), params: valid_c_params + + expect(response).to have_gitlab_http_status(403) + end + end + end end describe 'delete' do |