Commit b3616e30 authored by Douwe Maan's avatar Douwe Maan

Merge branch 'master-recursiveTree' into 'master'

Issue #4270: Recursive option for files through API

## What does this MR do?
- Adds recursive param to tree API request. With this param we can get all repository paths in a single request. 
- Related [old github pull request](https://github.com/gitlabhq/gitlabhq/pull/9311)

## Are there points in the code the reviewer needs to double check?

## Why was this MR needed?
Requested in #4270 
## Screenshots (if relevant)

## Does this MR meet the acceptance criteria?

- [X] [CHANGELOG](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CHANGELOG) entry added
- [X] [Documentation created/updated](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/doc_styleguide.md)
- [X] API support added
- Tests
  - [X] Added for this feature/bug
  - [x] All builds are passing
- [x] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#style-guides)
- [x] Branch has no merge conflicts with `master` (if you do - rebase it please)
- [x] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)

## What are the relevant issue numbers?
Requested in #4270

See merge request !6088
parents 374b8e95 5c966f70
......@@ -631,7 +631,7 @@ class Repository
@head_tree ||= Tree.new(self, head_commit.sha, nil)
end
def tree(sha = :head, path = nil)
def tree(sha = :head, path = nil, recursive: false)
if sha == :head
if path.nil?
return head_tree
......@@ -640,7 +640,7 @@ class Repository
end
end
Tree.new(self, sha, path)
Tree.new(self, sha, path, recursive: recursive)
end
def blob_at_branch(branch_name, path)
......
......@@ -3,15 +3,16 @@ class Tree
attr_accessor :repository, :sha, :path, :entries
def initialize(repository, sha, path = '/')
def initialize(repository, sha, path = '/', recursive: false)
path = '/' if path.blank?
@repository = repository
@sha = sha
@path = path
@recursive = recursive
git_repo = @repository.raw_repository
@entries = Gitlab::Git::Tree.where(git_repo, @sha, @path)
@entries = get_entries(git_repo, @sha, @path, recursive: @recursive)
end
def readme
......@@ -58,4 +59,21 @@ class Tree
def sorted_entries
trees + blobs + submodules
end
private
def get_entries(git_repo, sha, path, recursive: false)
current_path_entries = Gitlab::Git::Tree.where(git_repo, sha, path)
ordered_entries = []
current_path_entries.each do |entry|
ordered_entries << entry
if recursive && entry.dir?
ordered_entries.concat(get_entries(git_repo, sha, entry.path, recursive: true))
end
end
ordered_entries
end
end
---
title: API: allow recursive tree request
merge_request: 6088
author: Rebeca Méndez
......@@ -13,44 +13,58 @@ Parameters:
- `id` (required) - The ID of a project
- `path` (optional) - The path inside repository. Used to get contend of subdirectories
- `ref_name` (optional) - The name of a repository branch or tag or if not given the default branch
- `recursive` (optional) - Boolean value used to get a recursive tree (false by default)
```json
[
{
"name": "assets",
"id": "a1e8f8d745cc87e3a9248358d9352bb7f9a0aeba",
"name": "html",
"type": "tree",
"mode": "040000",
"id": "6229c43a7e16fcc7e95f923f8ddadb8281d9c6c6"
"path": "files/html",
"mode": "040000"
},
{
"name": "contexts",
"id": "4535904260b1082e14f867f7a24fd8c21495bde3",
"name": "images",
"type": "tree",
"mode": "040000",
"id": "faf1cdf33feadc7973118ca42d35f1e62977e91f"
"path": "files/images",
"mode": "040000"
},
{
"name": "controllers",
"id": "31405c5ddef582c5a9b7a85230413ff90e2fe720",
"name": "js",
"type": "tree",
"mode": "040000",
"id": "95633e8d258bf3dfba3a5268fb8440d263218d74"
"path": "files/js",
"mode": "040000"
},
{
"name": "Rakefile",
"type": "blob",
"mode": "100644",
"id": "35b2f05cbb4566b71b34554cf184a9d0bd9d46d6"
"id": "cc71111cfad871212dc99572599a568bfe1e7e00",
"name": "lfs",
"type": "tree",
"path": "files/lfs",
"mode": "040000"
},
{
"name": "VERSION",
"type": "blob",
"mode": "100644",
"id": "803e4a4f3727286c3093c63870c2b6524d30ec4f"
"id": "fd581c619bf59cfdfa9c8282377bb09c2f897520",
"name": "markdown",
"type": "tree",
"path": "files/markdown",
"mode": "040000"
},
{
"id": "23ea4d11a4bdd960ee5320c5cb65b5b3fdbc60db",
"name": "ruby",
"type": "tree",
"path": "files/ruby",
"mode": "040000"
},
{
"name": "config.ru",
"id": "7d70e02340bac451f281cecf0a980907974bd8be",
"name": "whitespace",
"type": "blob",
"mode": "100644",
"id": "dfd2d862237323aa599be31b473d70a8a817943b"
"path": "files/whitespace",
"mode": "100644"
}
]
```
......
......@@ -159,7 +159,7 @@ module API
end
class RepoTreeObject < Grape::Entity
expose :id, :name, :type
expose :id, :name, :type, :path
expose :mode do |obj, options|
filemode = obj.mode.to_s(8)
......
......@@ -21,16 +21,18 @@ module API
# Parameters:
# id (required) - The ID of a project
# ref_name (optional) - The name of a repository branch or tag, if not given the default branch is used
# recursive (optional) - Used to get a recursive tree
# Example Request:
# GET /projects/:id/repository/tree
get ':id/repository/tree' do
ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
path = params[:path] || nil
recursive = to_boolean(params[:recursive])
commit = user_project.commit(ref)
not_found!('Tree') unless commit
tree = user_project.repository.tree(commit.id, path)
tree = user_project.repository.tree(commit.id, path, recursive: recursive)
present tree.sorted_entries, with: Entities::RepoTreeObject
end
......
......@@ -18,6 +18,7 @@ describe API::API, api: true do
it "returns project commits" do
get api("/projects/#{project.id}/repository/tree", user)
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
......@@ -43,6 +44,40 @@ describe API::API, api: true do
end
end
describe 'GET /projects/:id/repository/tree?recursive=1' do
context 'authorized user' do
before { project.team << [user2, :reporter] }
it 'should return recursive project paths tree' do
get api("/projects/#{project.id}/repository/tree?recursive=1", user)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response[4]['name']).to eq('html')
expect(json_response[4]['path']).to eq('files/html')
expect(json_response[4]['type']).to eq('tree')
expect(json_response[4]['mode']).to eq('040000')
end
it 'returns a 404 for unknown ref' do
get api("/projects/#{project.id}/repository/tree?ref_name=foo&recursive=1", user)
expect(response).to have_http_status(404)
expect(json_response).to be_an Object
json_response['message'] == '404 Tree Not Found'
end
end
context "unauthorized user" do
it "does not return project commits" do
get api("/projects/#{project.id}/repository/tree?recursive=1")
expect(response).to have_http_status(401)
end
end
end
describe "GET /projects/:id/repository/blobs/:sha" do
it "gets the raw file contents" do
get api("/projects/#{project.id}/repository/blobs/master?filepath=README.md", user)
......
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