Commit dbbd8d05 authored by Achilleas Pipinellis's avatar Achilleas Pipinellis

Merge branch 'api-file-sha56-and-head' into 'master'

Add SHA256 and HEAD on File API

See merge request gitlab-org/gitlab-ce!19439
parents a17d0527 80f4e757
---
title: Add SHA256 and HEAD on File API
merge_request: 19439
author: ahmet2mir
type: added
...@@ -27,6 +27,7 @@ Example response: ...@@ -27,6 +27,7 @@ Example response:
"size": 1476, "size": 1476,
"encoding": "base64", "encoding": "base64",
"content": "IyA9PSBTY2hlbWEgSW5mb3...", "content": "IyA9PSBTY2hlbWEgSW5mb3...",
"content_sha256": "4c294617b60715c1d218e61164a3abd4808a4284cbc30e6728a01ad9aada4481",
"ref": "master", "ref": "master",
"blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83", "blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83",
"commit_id": "d5a3ff139356ce33e37e73add446f16869741b50", "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50",
...@@ -39,6 +40,36 @@ Parameters: ...@@ -39,6 +40,36 @@ Parameters:
- `file_path` (required) - Url encoded full path to new file. Ex. lib%2Fclass%2Erb - `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
NOTE: **Note:**
`blob_id` is the blob sha, see [repositories - Get a blob from repository](repositories.md#get-a-blob-from-repository)
In addition to the `GET` method, you can also use `HEAD` to get just file metadata.
```
HEAD /projects/:id/repository/files/:file_path
```
```bash
curl --head --header 'PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK' 'https://gitlab.example.com/api/v4/projects/13083/repository/files/app%2Fmodels%2Fkey%2Erb?ref=master'
```
Example response:
```text
HTTP/1.1 200 OK
...
X-Gitlab-Blob-Id: 79f7bbd25901e8334750839545a9bd021f0e4c83
X-Gitlab-Commit-Id: d5a3ff139356ce33e37e73add446f16869741b50
X-Gitlab-Content-Sha256: 4c294617b60715c1d218e61164a3abd4808a4284cbc30e6728a01ad9aada4481
X-Gitlab-Encoding: base64
X-Gitlab-File-Name: key.rb
X-Gitlab-File-Path: app/models/key.rb
X-Gitlab-Last-Commit-Id: 570e7b2abdd848b95f2f578043fc23bd6f6fd24d
X-Gitlab-Ref: master
X-Gitlab-Size: 1476
...
```
## Get raw file from repository ## Get raw file from repository
``` ```
...@@ -54,6 +85,9 @@ Parameters: ...@@ -54,6 +85,9 @@ Parameters:
- `file_path` (required) - Url encoded full path to new file. Ex. lib%2Fclass%2Erb - `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
NOTE: **Note:**
Like [Get file from repository](repository_files.md#get-file-from-repository) you can use `HEAD` to get just file metadata.
## Create new file in repository ## Create new file in repository
``` ```
......
...@@ -5,6 +5,8 @@ module API ...@@ -5,6 +5,8 @@ module API
# Prevents returning plain/text responses for files with .txt extension # Prevents returning plain/text responses for files with .txt extension
after_validation { content_type "application/json" } after_validation { content_type "application/json" }
helpers ::API::Helpers::HeadersHelpers
helpers do helpers do
def commit_params(attrs) def commit_params(attrs)
{ {
...@@ -40,6 +42,20 @@ module API ...@@ -40,6 +42,20 @@ module API
} }
end end
def blob_data
{
file_name: @blob.name,
file_path: @blob.path,
size: @blob.size,
encoding: "base64",
content_sha256: Digest::SHA256.hexdigest(@blob.data),
ref: params[:ref],
blob_id: @blob.id,
commit_id: @commit.id,
last_commit_id: @repo.last_commit_id_for_path(@commit.sha, params[:file_path])
}
end
params :simple_file_params do params :simple_file_params do
requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb' requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb'
requires :branch, type: String, desc: 'Name of the branch to commit into. To create a new branch, also provide `start_branch`.' requires :branch, type: String, desc: 'Name of the branch to commit into. To create a new branch, also provide `start_branch`.'
...@@ -61,6 +77,17 @@ module API ...@@ -61,6 +77,17 @@ module API
requires :id, type: String, desc: 'The project ID' requires :id, type: String, desc: 'The project ID'
end end
resource :projects, requirements: FILE_ENDPOINT_REQUIREMENTS do resource :projects, requirements: FILE_ENDPOINT_REQUIREMENTS do
desc 'Get raw file metadata from repository'
params do
requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb'
requires :ref, type: String, desc: 'The name of branch, tag or commit'
end
head ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS do
assign_file_vars!
set_http_headers(blob_data)
end
desc 'Get raw file contents from the repository' desc 'Get raw file contents from the repository'
params do params do
requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb' requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb'
...@@ -69,9 +96,22 @@ module API ...@@ -69,9 +96,22 @@ module API
get ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS do get ":id/repository/files/:file_path/raw", requirements: FILE_ENDPOINT_REQUIREMENTS do
assign_file_vars! assign_file_vars!
set_http_headers(blob_data)
send_git_blob @repo, @blob send_git_blob @repo, @blob
end end
desc 'Get file metadata from repository'
params do
requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb'
requires :ref, type: String, desc: 'The name of branch, tag or commit'
end
head ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do
assign_file_vars!
set_http_headers(blob_data)
end
desc 'Get a file from the repository' desc 'Get a file from the repository'
params do params do
requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb' requires :file_path, type: String, desc: 'The url encoded path to the file. Ex. lib%2Fclass%2Erb'
...@@ -80,17 +120,11 @@ module API ...@@ -80,17 +120,11 @@ module API
get ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do get ":id/repository/files/:file_path", requirements: FILE_ENDPOINT_REQUIREMENTS do
assign_file_vars! assign_file_vars!
{ data = blob_data
file_name: @blob.name,
file_path: @blob.path, set_http_headers(data)
size: @blob.size,
encoding: "base64", data.merge(content: Base64.strict_encode64(@blob.data))
content: Base64.strict_encode64(@blob.data),
ref: params[:ref],
blob_id: @blob.id,
commit_id: @commit.id,
last_commit_id: @repo.last_commit_id_for_path(@commit.sha, params[:file_path])
}
end end
desc 'Create new file in repository' desc 'Create new file in repository'
......
module API
module Helpers
module HeadersHelpers
def set_http_headers(header_data)
header_data.each do |key, value|
header "X-Gitlab-#{key.to_s.split('_').collect(&:capitalize).join('-')}", value
end
end
end
end
end
...@@ -21,6 +21,89 @@ describe API::Files do ...@@ -21,6 +21,89 @@ describe API::Files do
"/projects/#{project.id}/repository/files/#{file_path}" "/projects/#{project.id}/repository/files/#{file_path}"
end end
describe "HEAD /projects/:id/repository/files/:file_path" do
shared_examples_for 'repository files' do
it 'returns file attributes in headers' do
head api(route(file_path), current_user), params
expect(response).to have_gitlab_http_status(200)
expect(response.headers['X-Gitlab-File-Path']).to eq(CGI.unescape(file_path))
expect(response.headers['X-Gitlab-File-Name']).to eq('popen.rb')
expect(response.headers['X-Gitlab-Last-Commit-Id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d')
expect(response.headers['X-Gitlab-Content-Sha256']).to eq('c440cd09bae50c4632cc58638ad33c6aa375b6109d811e76a9cc3a613c1e8887')
end
it 'returns file by commit sha' do
# This file is deleted on HEAD
file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee"
params[:ref] = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9"
head api(route(file_path), current_user), params
expect(response).to have_gitlab_http_status(200)
expect(response.headers['X-Gitlab-File-Name']).to eq('commit.js.coffee')
expect(response.headers['X-Gitlab-Content-Sha256']).to eq('08785f04375b47f81f46e68cc125d5ef368aa20576ddb53f91f4d83f1d04b929')
end
context 'when mandatory params are not given' do
it "responds with a 400 status" do
head api(route("any%2Ffile"), current_user)
expect(response).to have_gitlab_http_status(400)
end
end
context 'when file_path does not exist' do
it "responds with a 404 status" do
params[:ref] = 'master'
head api(route('app%2Fmodels%2Fapplication%2Erb'), current_user), params
expect(response).to have_gitlab_http_status(404)
end
end
context 'when file_path does not exist' do
include_context 'disabled repository'
it "responds with a 403 status" do
head api(route(file_path), current_user), params
expect(response).to have_gitlab_http_status(403)
end
end
end
context 'when unauthenticated', 'and project is public' do
it_behaves_like 'repository files' do
let(:project) { create(:project, :public, :repository) }
let(:current_user) { nil }
end
end
context 'when unauthenticated', 'and project is private' do
it "responds with a 404 status" do
current_user = nil
head api(route(file_path), current_user), params
expect(response).to have_gitlab_http_status(404)
end
end
context 'when authenticated', 'as a developer' do
it_behaves_like 'repository files' do
let(:current_user) { user }
end
end
context 'when authenticated', 'as a guest' do
it_behaves_like '403 response' do
let(:request) { head api(route(file_path), guest), params }
end
end
end
describe "GET /projects/:id/repository/files/:file_path" do describe "GET /projects/:id/repository/files/:file_path" do
shared_examples_for 'repository files' do shared_examples_for 'repository files' do
it 'returns file attributes as json' do it 'returns file attributes as json' do
...@@ -30,6 +113,7 @@ describe API::Files do ...@@ -30,6 +113,7 @@ describe API::Files do
expect(json_response['file_path']).to eq(CGI.unescape(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(json_response['content_sha256']).to eq('c440cd09bae50c4632cc58638ad33c6aa375b6109d811e76a9cc3a613c1e8887')
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
...@@ -51,6 +135,7 @@ describe API::Files do ...@@ -51,6 +135,7 @@ describe API::Files do
expect(response).to have_gitlab_http_status(200) expect(response).to have_gitlab_http_status(200)
expect(json_response['file_name']).to eq('commit.js.coffee') expect(json_response['file_name']).to eq('commit.js.coffee')
expect(json_response['content_sha256']).to eq('08785f04375b47f81f46e68cc125d5ef368aa20576ddb53f91f4d83f1d04b929')
expect(Base64.decode64(json_response['content']).lines.first).to eq("class Commit\n") expect(Base64.decode64(json_response['content']).lines.first).to eq("class Commit\n")
end end
......
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