Commit 9053d78e authored by Felipe Artur's avatar Felipe Artur

Update API endpoints for raw files

parent 7f2819b7
---
title: Update API endpoints for raw files
merge_request:
author:
...@@ -72,10 +72,11 @@ Parameters: ...@@ -72,10 +72,11 @@ Parameters:
] ]
``` ```
## Raw file content ## Get file from repository
Get the raw file contents for a file by commit SHA and path. This endpoint can Allows you to receive information about file in repository like size and
be accessed without authentication if the repository is publicly accessible. content. Note that file content is Base64 encoded. This endpoint can be accessed
without authentication if the repository is publicly accessible.
``` ```
GET /projects/:id/repository/blobs/:sha GET /projects/:id/repository/blobs/:sha
...@@ -85,7 +86,6 @@ Parameters: ...@@ -85,7 +86,6 @@ Parameters:
- `id` (required) - The ID of a project - `id` (required) - The ID of a project
- `sha` (required) - The commit or branch name - `sha` (required) - The commit or branch name
- `filepath` (required) - The path the file
## Raw blob content ## Raw blob content
...@@ -93,7 +93,7 @@ Get the raw file contents for a blob by blob SHA. This endpoint can be accessed ...@@ -93,7 +93,7 @@ Get the raw file contents for a blob by blob SHA. This endpoint can be accessed
without authentication if the repository is publicly accessible. without authentication if the repository is publicly accessible.
``` ```
GET /projects/:id/repository/raw_blobs/:sha GET /projects/:id/repository/blobs/:sha/raw
``` ```
Parameters: Parameters:
......
...@@ -11,11 +11,11 @@ content. Note that file content is Base64 encoded. This endpoint can be accessed ...@@ -11,11 +11,11 @@ content. Note that file content is Base64 encoded. This endpoint can be accessed
without authentication if the repository is publicly accessible. without authentication if the repository is publicly accessible.
``` ```
GET /projects/:id/repository/files GET /projects/:id/repository/files/:file_path
``` ```
```bash ```bash
curl --request GET --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files?file_path=app/models/key.rb&ref=master' curl --request GET --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fmodels%2Fkey%2Erb?ref=master'
``` ```
Example response: Example response:
...@@ -36,17 +36,32 @@ Example response: ...@@ -36,17 +36,32 @@ Example response:
Parameters: Parameters:
- `file_path` (required) - Full path to new file. Ex. lib/class.rb - `file_path` (required) - Url encoded full path to new file. Ex. lib%2Fclass%2Erb
- `ref` (required) - The name of branch, tag or commit
## Get raw file from repository
```
GET /projects/:id/repository/files/:file_path/raw
```
```bash
curl --request GET --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fmodels%2Fkey%2Erb/raw?ref=master'
```
Parameters:
- `file_path` (required) - Url encoded full path to new file. Ex. lib%2Fclass%2Erb
- `ref` (required) - The name of branch, tag or commit - `ref` (required) - The name of branch, tag or commit
## Create new file in repository ## Create new file in repository
``` ```
POST /projects/:id/repository/files POST /projects/:id/repository/files/:file_path
``` ```
```bash ```bash
curl --request POST --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files?file_path=app/project.rb&branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&content=some%20content&commit_message=create%20a%20new%20file' curl --request POST --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/app%2Fprojectrb%2E?branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&content=some%20content&commit_message=create%20a%20new%20file'
``` ```
Example response: Example response:
...@@ -60,7 +75,7 @@ Example response: ...@@ -60,7 +75,7 @@ Example response:
Parameters: Parameters:
- `file_path` (required) - Full path to new file. Ex. lib/class.rb - `file_path` (required) - Url encoded full path to new file. Ex. lib%2Fclass%2Erb
- `branch` (required) - The name of branch - `branch` (required) - The name of branch
- `encoding` (optional) - Change encoding to 'base64'. Default is text. - `encoding` (optional) - Change encoding to 'base64'. Default is text.
- `author_email` (optional) - Specify the commit author's email address - `author_email` (optional) - Specify the commit author's email address
...@@ -71,11 +86,11 @@ Parameters: ...@@ -71,11 +86,11 @@ Parameters:
## Update existing file in repository ## Update existing file in repository
``` ```
PUT /projects/:id/repository/files PUT /projects/:id/repository/files/:file_path
``` ```
```bash ```bash
curl --request PUT --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files?file_path=app/project.rb&branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&content=some%20other%20content&commit_message=update%20file' curl --request PUT --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/app%2Fproject%2Erb?branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&content=some%20other%20content&commit_message=update%20file'
``` ```
Example response: Example response:
...@@ -89,7 +104,7 @@ Example response: ...@@ -89,7 +104,7 @@ Example response:
Parameters: Parameters:
- `file_path` (required) - Full path to file. Ex. lib/class.rb - `file_path` (required) - Url encoded full path to new file. Ex. lib%2Fclass%2Erb
- `branch` (required) - The name of branch - `branch` (required) - The name of branch
- `encoding` (optional) - Change encoding to 'base64'. Default is text. - `encoding` (optional) - Change encoding to 'base64'. Default is text.
- `author_email` (optional) - Specify the commit author's email address - `author_email` (optional) - Specify the commit author's email address
...@@ -109,11 +124,11 @@ Currently gitlab-shell has a boolean return code, preventing GitLab from specify ...@@ -109,11 +124,11 @@ Currently gitlab-shell has a boolean return code, preventing GitLab from specify
## Delete existing file in repository ## Delete existing file in repository
``` ```
DELETE /projects/:id/repository/files DELETE /projects/:id/repository/files/:file_path
``` ```
```bash ```bash
curl --request DELETE --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files?file_path=app/project.rb&branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&commit_message=delete%20file' curl --request DELETE --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/app%2Fproject%2Erb?branch=master&author_email=author%40example.com&author_name=Firstname%20Lastname&commit_message=delete%20file'
``` ```
Example response: Example response:
...@@ -127,7 +142,7 @@ Example response: ...@@ -127,7 +142,7 @@ Example response:
Parameters: Parameters:
- `file_path` (required) - Full path to file. Ex. lib/class.rb - `file_path` (required) - Url encoded full path to new file. Ex. lib%2Fclass%2Erb
- `branch` (required) - The name of branch - `branch` (required) - The name of branch
- `author_email` (optional) - Specify the commit author's email address - `author_email` (optional) - Specify the commit author's email address
- `author_name` (optional) - Specify the commit author's name - `author_name` (optional) - Specify the commit author's name
......
...@@ -71,3 +71,7 @@ changes are in V4: ...@@ -71,3 +71,7 @@ changes are in V4:
- Simplify project payload exposed on Environment endpoints [!9675](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9675) - Simplify project payload exposed on Environment endpoints [!9675](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9675)
- API uses merge request `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the merge requests, award emoji, todos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9530) - API uses merge request `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the merge requests, award emoji, todos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9530)
- API uses issue `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the issues, award emoji, todos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9530) - API uses issue `IID`s (internal ID, as in the web UI) rather than `ID`s. This affects the issues, award emoji, todos, and time tracking APIs. [!9530](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9530)
- Update endpoints for repository files [!9637](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9637)
- Moved `/projects/:id/repository/files` to `/projects/:id/repository/files/:filepath` (`:filepath` should be URL-encoded)
- Moved `/projects/:id/repository/blobs/:sha` to `/projects/:id/repository/files/:filepath/raw`
- Moved `/projects/:id/repository/raw_blobs/:sha` to `/projects/:id/repository/blobs/:sha/raw`
...@@ -14,6 +14,19 @@ module API ...@@ -14,6 +14,19 @@ module API
} }
end end
def assign_file_vars!
@commit = user_project.commit(params[:ref])
not_found!('Commit') unless @commit
@repo = user_project.repository
@file_path = params[:file_path]
@file_path = [params[:file_path], params[:format]].join('.') if params[:format].present?
@blob = @repo.blob_at(@commit.sha, @file_path)
not_found!('File') unless @blob
@blob.load_all_data!(@repo)
end
def commit_response(attrs) def commit_response(attrs)
{ {
file_path: attrs[:file_path], file_path: attrs[:file_path],
...@@ -22,7 +35,7 @@ module API ...@@ -22,7 +35,7 @@ module API
end end
params :simple_file_params do params :simple_file_params do
requires :file_path, type: String, desc: 'The path to new file. Ex. lib/class.rb' requires :file_path, type: String, desc: 'The path to the file. Ex. lib/class.rb'
requires :branch, type: String, desc: 'The name of branch' requires :branch, type: String, desc: 'The name of branch'
requires :commit_message, type: String, desc: 'Commit Message' requires :commit_message, type: String, desc: 'Commit Message'
optional :author_email, type: String, desc: 'The email of the author' optional :author_email, type: String, desc: 'The email of the author'
...@@ -40,34 +53,41 @@ module API ...@@ -40,34 +53,41 @@ module API
requires :id, type: String, desc: 'The project ID' requires :id, type: String, desc: 'The project ID'
end end
resource :projects do resource :projects do
desc 'Get a file from repository' desc 'Get a file from repository in raw format'
params do params do
requires :file_path, type: String, desc: 'The path to the file. Ex. lib/class.rb'
requires :ref, type: String, desc: 'The name of branch, tag, or commit' requires :ref, type: String, desc: 'The name of branch, tag, or commit'
end end
get ":id/repository/files" do get ":id/repository/files/:file_path/raw" do
authorize! :download_code, user_project authorize! :download_code, user_project
commit = user_project.commit(params[:ref]) assign_file_vars!
not_found!('Commit') unless commit
status(200)
send_git_blob @repo, @blob
end
desc 'Get a file from repository in base64 format'
params do
requires :ref, type: String, desc: 'The name of branch, tag, or commit'
end
get ":id/repository/files/:file_path" do
authorize! :download_code, user_project
repo = user_project.repository assign_file_vars!
blob = repo.blob_at(commit.sha, params[:file_path])
not_found!('File') unless blob
blob.load_all_data!(repo)
status(200) status(200)
{ {
file_name: blob.name, file_name: @blob.name,
file_path: blob.path, file_path: @blob.path,
size: blob.size, size: @blob.size,
encoding: "base64", encoding: "base64",
content: Base64.strict_encode64(blob.data), content: Base64.strict_encode64(@blob.data),
ref: params[:ref], ref: params[:ref],
blob_id: blob.id, blob_id: @blob.id,
commit_id: commit.id, commit_id: @commit.id,
last_commit_id: repo.last_commit_id_for_path(commit.sha, params[:file_path]) last_commit_id: @repo.last_commit_id_for_path(@commit.sha, @file_path)
} }
end end
...@@ -75,7 +95,7 @@ module API ...@@ -75,7 +95,7 @@ module API
params do params do
use :extended_file_params use :extended_file_params
end end
post ":id/repository/files" do post ":id/repository/files/:file_path", requirements: { file_path: /.+/ } do
authorize! :push_code, user_project authorize! :push_code, user_project
file_params = declared_params(include_missing: false) file_params = declared_params(include_missing: false)
...@@ -93,7 +113,7 @@ module API ...@@ -93,7 +113,7 @@ module API
params do params do
use :extended_file_params use :extended_file_params
end end
put ":id/repository/files" do put ":id/repository/files/:file_path", requirements: { file_path: /.+/ } do
authorize! :push_code, user_project authorize! :push_code, user_project
file_params = declared_params(include_missing: false) file_params = declared_params(include_missing: false)
...@@ -112,7 +132,7 @@ module API ...@@ -112,7 +132,7 @@ module API
params do params do
use :simple_file_params use :simple_file_params
end end
delete ":id/repository/files" do delete ":id/repository/files/:file_path", requirements: { file_path: /.+/ } do
authorize! :push_code, user_project authorize! :push_code, user_project
file_params = declared_params(include_missing: false) file_params = declared_params(include_missing: false)
......
...@@ -17,6 +17,18 @@ module API ...@@ -17,6 +17,18 @@ module API
end end
not_found! not_found!
end end
def assign_blob_vars!
@repo = user_project.repository
begin
@blob = Gitlab::Git::Blob.raw(@repo, params[:sha])
rescue
not_found! 'Blob'
end
not_found! 'Blob' unless @blob
end
end end
desc 'Get a project repository tree' do desc 'Get a project repository tree' do
...@@ -45,7 +57,7 @@ module API ...@@ -45,7 +57,7 @@ module API
requires :sha, type: String, desc: 'The commit, branch name, or tag name' requires :sha, type: String, desc: 'The commit, branch name, or tag name'
requires :filepath, type: String, desc: 'The path to the file to display' requires :filepath, type: String, desc: 'The path to the file to display'
end end
get [":id/repository/blobs/:sha", ":id/repository/commits/:sha/blob"] do get ":id/repository/commits/:sha/blob" do
repo = user_project.repository repo = user_project.repository
commit = repo.commit(params[:sha]) commit = repo.commit(params[:sha])
...@@ -57,22 +69,33 @@ module API ...@@ -57,22 +69,33 @@ module API
send_git_blob repo, blob send_git_blob repo, blob
end end
desc 'Get a raw blob contents by blob sha' desc 'Get raw blob by sha'
params do params do
requires :sha, type: String, desc: 'The commit, branch name, or tag name' requires :sha, type: String, desc: 'The commit, branch name, or tag name'
end end
get ':id/repository/raw_blobs/:sha' do get ':id/repository/blobs/:sha/:raw' do
repo = user_project.repository assign_blob_vars!
begin status(200)
blob = Gitlab::Git::Blob.raw(repo, params[:sha])
rescue
not_found! 'Blob'
end
not_found! 'Blob' unless blob send_git_blob @repo, @blob
end
send_git_blob repo, blob desc 'Get blob base4 encoded content by sha'
params do
requires :sha, type: String, desc: 'The commit, branch name, or tag name'
end
get ':id/repository/blobs/:sha' do
assign_blob_vars!
status(200)
{
size: @blob.size,
encoding: "base64",
content: Base64.strict_encode64(@blob.data),
sha: @blob.id
}
end end
desc 'Get an archive of the repository' desc 'Get an archive of the repository'
......
...@@ -5,10 +5,9 @@ describe API::Files, api: true do ...@@ -5,10 +5,9 @@ describe API::Files, api: true do
let(:user) { create(:user) } let(:user) { create(:user) }
let!(:project) { create(:project, :repository, namespace: user.namespace ) } let!(:project) { create(:project, :repository, namespace: user.namespace ) }
let(:guest) { create(:user) { |u| project.add_guest(u) } } let(:guest) { create(:user) { |u| project.add_guest(u) } }
let(:file_path) { 'files/ruby/popen.rb' } let(:file_path) { "files%2Fruby%2Fpopen%2Erb" }
let(:params) do let(:params) do
{ {
file_path: file_path,
ref: 'master' ref: 'master'
} }
end end
...@@ -30,36 +29,42 @@ describe API::Files, api: true do ...@@ -30,36 +29,42 @@ describe API::Files, api: true do
before { project.team << [user, :developer] } before { project.team << [user, :developer] }
describe "GET /projects/:id/repository/files" do def route(file_path = nil)
let(:route) { "/projects/#{project.id}/repository/files" } "/projects/#{project.id}/repository/files/#{file_path}"
end
describe "GET /projects/:id/repository/files/:file_path" do
shared_examples_for 'repository files' do shared_examples_for 'repository files' do
it "returns file info" do it 'returns file attributes as json' do
get api(route, current_user), params get api(route(file_path), current_user), params
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['file_path']).to eq(file_path) expect(json_response['file_path']).to eq(CGI.unescape(file_path))
expect(json_response['file_name']).to eq('popen.rb') expect(json_response['file_name']).to eq('popen.rb')
expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d') expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d')
expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n") expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n")
end end
context 'when no params are given' do it 'returns raw file info' do
url = route(file_path) + "/raw"
expect(Gitlab::Workhorse).to receive(:send_git_blob)
get api(url, current_user), params
expect(response).to have_http_status(200)
end
context 'when mandatory params are not given' do
it_behaves_like '400 response' do it_behaves_like '400 response' do
let(:request) { get api(route, current_user) } let(:request) { get api(route("any%2Ffile"), current_user) }
end end
end end
context 'when file_path does not exist' do context 'when file_path does not exist' do
let(:params) do let(:params) { { ref: 'master' } }
{
file_path: 'app/models/application.rb',
ref: 'master',
}
end
it_behaves_like '404 response' do it_behaves_like '404 response' do
let(:request) { get api(route, current_user), params } let(:request) { get api(route('app%2Fmodels%2Fapplication%2Erb'), current_user), params }
let(:message) { '404 File Not Found' } let(:message) { '404 File Not Found' }
end end
end end
...@@ -68,7 +73,7 @@ describe API::Files, api: true do ...@@ -68,7 +73,7 @@ describe API::Files, api: true do
include_context 'disabled repository' include_context 'disabled repository'
it_behaves_like '403 response' do it_behaves_like '403 response' do
let(:request) { get api(route, current_user), params } let(:request) { get api(route(file_path), current_user), params }
end end
end end
end end
...@@ -82,7 +87,7 @@ describe API::Files, api: true do ...@@ -82,7 +87,7 @@ describe API::Files, api: true do
context 'when unauthenticated', 'and project is private' do context 'when unauthenticated', 'and project is private' do
it_behaves_like '404 response' do it_behaves_like '404 response' do
let(:request) { get api(route), params } let(:request) { get api(route(file_path)), params }
let(:message) { '404 Project Not Found' } let(:message) { '404 Project Not Found' }
end end
end end
...@@ -95,33 +100,95 @@ describe API::Files, api: true do ...@@ -95,33 +100,95 @@ describe API::Files, api: true do
context 'when authenticated', 'as a guest' do context 'when authenticated', 'as a guest' do
it_behaves_like '403 response' do it_behaves_like '403 response' do
let(:request) { get api(route, guest), params } let(:request) { get api(route(file_path), guest), params }
end end
end end
end end
describe "POST /projects/:id/repository/files" do describe "GET /projects/:id/repository/files/:file_path/raw" do
shared_examples_for 'repository raw files' do
it 'returns raw file info' do
url = route(file_path) + "/raw"
expect(Gitlab::Workhorse).to receive(:send_git_blob)
get api(url, current_user), params
expect(response).to have_http_status(200)
end
context 'when mandatory params are not given' do
it_behaves_like '400 response' do
let(:request) { get api(route("any%2Ffile"), current_user) }
end
end
context 'when file_path does not exist' do
let(:params) { { ref: 'master' } }
it_behaves_like '404 response' do
let(:request) { get api(route('app%2Fmodels%2Fapplication%2Erb'), current_user), params }
let(:message) { '404 File Not Found' }
end
end
context 'when repository is disabled' do
include_context 'disabled repository'
it_behaves_like '403 response' do
let(:request) { get api(route(file_path), current_user), params }
end
end
end
context 'when unauthenticated', 'and project is public' do
it_behaves_like 'repository raw files' do
let(:project) { create(:project, :public) }
let(:current_user) { nil }
end
end
context 'when unauthenticated', 'and project is private' do
it_behaves_like '404 response' do
let(:request) { get api(route(file_path)), params }
let(:message) { '404 Project Not Found' }
end
end
context 'when authenticated', 'as a developer' do
it_behaves_like 'repository raw files' do
let(:current_user) { user }
end
end
context 'when authenticated', 'as a guest' do
it_behaves_like '403 response' do
let(:request) { get api(route(file_path), guest), params }
end
end
end
describe "POST /projects/:id/repository/files/:file_path" do
let!(:file_path) { "new_subfolder%2Fnewfile%2Erb" }
let(:valid_params) do let(:valid_params) do
{ {
file_path: 'newfile.rb', branch: "master",
branch: 'master', content: "puts 8",
content: 'puts 8', commit_message: "Added newfile"
commit_message: 'Added newfile'
} }
end end
it "creates a new file in project repo" do it "creates a new file in project repo" do
post api("/projects/#{project.id}/repository/files", user), valid_params post api(route(file_path), user), valid_params
expect(response).to have_http_status(201) expect(response).to have_http_status(201)
expect(json_response['file_path']).to eq('newfile.rb') expect(json_response["file_path"]).to eq(CGI.unescape(file_path))
last_commit = project.repository.commit.raw last_commit = project.repository.commit.raw
expect(last_commit.author_email).to eq(user.email) expect(last_commit.author_email).to eq(user.email)
expect(last_commit.author_name).to eq(user.name) expect(last_commit.author_name).to eq(user.name)
end end
it "returns a 400 bad request if no params given" do it "returns a 400 bad request if no mandatory params given" do
post api("/projects/#{project.id}/repository/files", user) post api(route("any%2Etxt"), user)
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
end end
...@@ -130,7 +197,7 @@ describe API::Files, api: true do ...@@ -130,7 +197,7 @@ describe API::Files, api: true do
allow_any_instance_of(Repository).to receive(:create_file). allow_any_instance_of(Repository).to receive(:create_file).
and_return(false) and_return(false)
post api("/projects/#{project.id}/repository/files", user), valid_params post api(route("any%2Etxt"), user), valid_params
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
end end
...@@ -139,7 +206,7 @@ describe API::Files, api: true do ...@@ -139,7 +206,7 @@ describe API::Files, api: true do
it "creates a new file with the specified author" do it "creates a new file with the specified author" do
valid_params.merge!(author_email: author_email, author_name: author_name) valid_params.merge!(author_email: author_email, author_name: author_name)
post api("/projects/#{project.id}/repository/files", user), valid_params post api(route("new_file_with_author%2Etxt"), user), valid_params
expect(response).to have_http_status(201) expect(response).to have_http_status(201)
last_commit = project.repository.commit.raw last_commit = project.repository.commit.raw
...@@ -152,7 +219,7 @@ describe API::Files, api: true do ...@@ -152,7 +219,7 @@ describe API::Files, api: true do
let!(:project) { create(:project_empty_repo, namespace: user.namespace ) } let!(:project) { create(:project_empty_repo, namespace: user.namespace ) }
it "creates a new file in project repo" do it "creates a new file in project repo" do
post api("/projects/#{project.id}/repository/files", user), valid_params post api(route("newfile%2Erb"), user), valid_params
expect(response).to have_http_status(201) expect(response).to have_http_status(201)
expect(json_response['file_path']).to eq('newfile.rb') expect(json_response['file_path']).to eq('newfile.rb')
...@@ -166,7 +233,6 @@ describe API::Files, api: true do ...@@ -166,7 +233,6 @@ describe API::Files, api: true do
describe "PUT /projects/:id/repository/files" do describe "PUT /projects/:id/repository/files" do
let(:valid_params) do let(:valid_params) do
{ {
file_path: file_path,
branch: 'master', branch: 'master',
content: 'puts 8', content: 'puts 8',
commit_message: 'Changed file' commit_message: 'Changed file'
...@@ -174,17 +240,17 @@ describe API::Files, api: true do ...@@ -174,17 +240,17 @@ describe API::Files, api: true do
end end
it "updates existing file in project repo" do it "updates existing file in project repo" do
put api("/projects/#{project.id}/repository/files", user), valid_params put api(route(file_path), user), valid_params
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['file_path']).to eq(file_path) expect(json_response['file_path']).to eq(CGI.unescape(file_path))
last_commit = project.repository.commit.raw last_commit = project.repository.commit.raw
expect(last_commit.author_email).to eq(user.email) expect(last_commit.author_email).to eq(user.email)
expect(last_commit.author_name).to eq(user.name) expect(last_commit.author_name).to eq(user.name)
end end
it "returns a 400 bad request if no params given" do it "returns a 400 bad request if no params given" do
put api("/projects/#{project.id}/repository/files", user) put api(route(file_path), user)
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
end end
...@@ -193,7 +259,7 @@ describe API::Files, api: true do ...@@ -193,7 +259,7 @@ describe API::Files, api: true do
it "updates a file with the specified author" do it "updates a file with the specified author" do
valid_params.merge!(author_email: author_email, author_name: author_name, content: "New content") valid_params.merge!(author_email: author_email, author_name: author_name, content: "New content")
put api("/projects/#{project.id}/repository/files", user), valid_params put api(route(file_path), user), valid_params
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
last_commit = project.repository.commit.raw last_commit = project.repository.commit.raw
...@@ -206,20 +272,19 @@ describe API::Files, api: true do ...@@ -206,20 +272,19 @@ describe API::Files, api: true do
describe "DELETE /projects/:id/repository/files" do describe "DELETE /projects/:id/repository/files" do
let(:valid_params) do let(:valid_params) do
{ {
file_path: file_path,
branch: 'master', branch: 'master',
commit_message: 'Changed file' commit_message: 'Changed file'
} }
end end
it "deletes existing file in project repo" do it "deletes existing file in project repo" do
delete api("/projects/#{project.id}/repository/files", user), valid_params delete api(route(file_path), user), valid_params
expect(response).to have_http_status(204) expect(response).to have_http_status(204)
end end
it "returns a 400 bad request if no params given" do it "returns a 400 bad request if no params given" do
delete api("/projects/#{project.id}/repository/files", user) delete api(route(file_path), user)
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
end end
...@@ -227,7 +292,7 @@ describe API::Files, api: true do ...@@ -227,7 +292,7 @@ describe API::Files, api: true do
it "returns a 400 if fails to create file" do it "returns a 400 if fails to create file" do
allow_any_instance_of(Repository).to receive(:delete_file).and_return(false) allow_any_instance_of(Repository).to receive(:delete_file).and_return(false)
delete api("/projects/#{project.id}/repository/files", user), valid_params delete api(route(file_path), user), valid_params
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
end end
...@@ -236,7 +301,7 @@ describe API::Files, api: true do ...@@ -236,7 +301,7 @@ describe API::Files, api: true do
it "removes a file with the specified author" do it "removes a file with the specified author" do
valid_params.merge!(author_email: author_email, author_name: author_name) valid_params.merge!(author_email: author_email, author_name: author_name)
delete api("/projects/#{project.id}/repository/files", user), valid_params delete api(route(file_path), user), valid_params
expect(response).to have_http_status(204) expect(response).to have_http_status(204)
end end
...@@ -244,10 +309,9 @@ describe API::Files, api: true do ...@@ -244,10 +309,9 @@ describe API::Files, api: true do
end end
describe "POST /projects/:id/repository/files with binary file" do describe "POST /projects/:id/repository/files with binary file" do
let(:file_path) { 'test.bin' } let(:file_path) { 'test%2Ebin' }
let(:put_params) do let(:put_params) do
{ {
file_path: file_path,
branch: 'master', branch: 'master',
content: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=', content: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=',
commit_message: 'Binary file with a \n should not be touched', commit_message: 'Binary file with a \n should not be touched',
...@@ -256,21 +320,20 @@ describe API::Files, api: true do ...@@ -256,21 +320,20 @@ describe API::Files, api: true do
end end
let(:get_params) do let(:get_params) do
{ {
file_path: file_path,
ref: 'master', ref: 'master',
} }
end end
before do before do
post api("/projects/#{project.id}/repository/files", user), put_params post api(route(file_path), user), put_params
end end
it "remains unchanged" do it "remains unchanged" do
get api("/projects/#{project.id}/repository/files", user), get_params get api(route(file_path), user), get_params
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['file_path']).to eq(file_path) expect(json_response['file_path']).to eq(CGI.unescape(file_path))
expect(json_response['file_name']).to eq(file_path) expect(json_response['file_name']).to eq(CGI.unescape(file_path))
expect(json_response['content']).to eq(put_params[:content]) expect(json_response['content']).to eq(put_params[:content])
end end
end end
......
...@@ -100,82 +100,136 @@ describe API::Repositories, api: true do ...@@ -100,82 +100,136 @@ describe API::Repositories, api: true do
end end
end end
{ describe "GET /projects/:id/repository/commits/:sha/blob" do
'blobs/:sha' => 'blobs/master', let(:route) { "/projects/#{project.id}/repository/commits/master/blob?filepath=README.md" }
'commits/:sha/blob' => 'commits/master/blob'
}.each do |desc_path, example_path|
describe "GET /projects/:id/repository/#{desc_path}" do
let(:route) { "/projects/#{project.id}/repository/#{example_path}?filepath=README.md" }
shared_examples_for 'repository blob' do
it 'returns the repository blob' do
get api(route, current_user)
expect(response).to have_http_status(200)
end
context 'when sha does not exist' do shared_examples_for 'repository blob' do
it_behaves_like '404 response' do it 'returns the repository blob' do
let(:request) { get api(route.sub('master', 'invalid_branch_name'), current_user) } get api(route, current_user)
let(:message) { '404 Commit Not Found' }
end expect(response).to have_http_status(200)
end
context 'when sha does not exist' do
it_behaves_like '404 response' do
let(:request) { get api(route.sub('master', 'invalid_branch_name'), current_user) }
let(:message) { '404 Commit Not Found' }
end end
end
context 'when filepath does not exist' do context 'when filepath does not exist' do
it_behaves_like '404 response' do it_behaves_like '404 response' do
let(:request) { get api(route.sub('README.md', 'README.invalid'), current_user) } let(:request) { get api(route.sub('README.md', 'README.invalid'), current_user) }
let(:message) { '404 File Not Found' } let(:message) { '404 File Not Found' }
end
end end
end
context 'when no filepath is given' do context 'when no filepath is given' do
it_behaves_like '400 response' do it_behaves_like '400 response' do
let(:request) { get api(route.sub('?filepath=README.md', ''), current_user) } let(:request) { get api(route.sub('?filepath=README.md', ''), current_user) }
end
end end
end
context 'when repository is disabled' do context 'when repository is disabled' do
include_context 'disabled repository' include_context 'disabled repository'
it_behaves_like '403 response' do it_behaves_like '403 response' do
let(:request) { get api(route, current_user) } let(:request) { get api(route, current_user) }
end
end end
end end
end
context 'when unauthenticated', 'and project is public' do context 'when unauthenticated', 'and project is public' do
it_behaves_like 'repository blob' do it_behaves_like 'repository blob' do
let(:project) { create(:project, :public, :repository) } let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil } let(:current_user) { nil }
end
end end
end
context 'when unauthenticated', 'and project is private' do context 'when unauthenticated', 'and project is private' do
it_behaves_like '404 response' do it_behaves_like '404 response' do
let(:request) { get api(route) } let(:request) { get api(route) }
let(:message) { '404 Project Not Found' } let(:message) { '404 Project Not Found' }
end
end end
end
context 'when authenticated', 'as a developer' do context 'when authenticated', 'as a developer' do
it_behaves_like 'repository blob' do it_behaves_like 'repository blob' do
let(:current_user) { user } let(:current_user) { user }
end
end
context 'when authenticated', 'as a guest' do
it_behaves_like '403 response' do
let(:request) { get api(route, guest) }
end
end
end
describe "GET /projects/:id/repository/blobs/:sha" do
let(:route) { "/projects/#{project.id}/repository/blobs/#{sample_blob.oid}" }
shared_examples_for 'repository blob' do
it 'returns blob attributes as json' do
get api(route, current_user)
expect(response).to have_http_status(200)
expect(json_response['size']).to eq(111)
expect(json_response['encoding']).to eq("base64")
expect(Base64.decode64(json_response['content']).lines.first).to eq("class Commit\n")
expect(json_response['sha']).to eq(sample_blob.oid)
end
context 'when sha does not exist' do
it_behaves_like '404 response' do
let(:request) { get api(route.sub(sample_blob.oid, '123456'), current_user) }
let(:message) { '404 Blob Not Found' }
end end
end end
context 'when authenticated', 'as a guest' do context 'when repository is disabled' do
include_context 'disabled repository'
it_behaves_like '403 response' do it_behaves_like '403 response' do
let(:request) { get api(route, guest) } let(:request) { get api(route, current_user) }
end end
end end
end end
context 'when unauthenticated', 'and project is public' do
it_behaves_like 'repository blob' do
let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
context 'when unauthenticated', 'and project is private' do
it_behaves_like '404 response' do
let(:request) { get api(route) }
let(:message) { '404 Project Not Found' }
end
end
context 'when authenticated', 'as a developer' do
it_behaves_like 'repository blob' do
let(:current_user) { user }
end
end
context 'when authenticated', 'as a guest' do
it_behaves_like '403 response' do
let(:request) { get api(route, guest) }
end
end
end end
describe "GET /projects/:id/repository/raw_blobs/:sha" do describe "GET /projects/:id/repository/blobs/:sha/raw" do
let(:route) { "/projects/#{project.id}/repository/raw_blobs/#{sample_blob.oid}" } let(:route) { "/projects/#{project.id}/repository/blobs/#{sample_blob.oid}/raw" }
shared_examples_for 'repository raw blob' do shared_examples_for 'repository raw blob' do
it 'returns the repository raw blob' do it 'returns the repository raw blob' do
expect(Gitlab::Workhorse).to receive(:send_git_blob)
get api(route, current_user) get api(route, current_user)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
......
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