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 ...@@ -631,7 +631,7 @@ class Repository
@head_tree ||= Tree.new(self, head_commit.sha, nil) @head_tree ||= Tree.new(self, head_commit.sha, nil)
end end
def tree(sha = :head, path = nil) def tree(sha = :head, path = nil, recursive: false)
if sha == :head if sha == :head
if path.nil? if path.nil?
return head_tree return head_tree
...@@ -640,7 +640,7 @@ class Repository ...@@ -640,7 +640,7 @@ class Repository
end end
end end
Tree.new(self, sha, path) Tree.new(self, sha, path, recursive: recursive)
end end
def blob_at_branch(branch_name, path) def blob_at_branch(branch_name, path)
......
...@@ -3,15 +3,16 @@ class Tree ...@@ -3,15 +3,16 @@ class Tree
attr_accessor :repository, :sha, :path, :entries attr_accessor :repository, :sha, :path, :entries
def initialize(repository, sha, path = '/') def initialize(repository, sha, path = '/', recursive: false)
path = '/' if path.blank? path = '/' if path.blank?
@repository = repository @repository = repository
@sha = sha @sha = sha
@path = path @path = path
@recursive = recursive
git_repo = @repository.raw_repository git_repo = @repository.raw_repository
@entries = Gitlab::Git::Tree.where(git_repo, @sha, @path) @entries = get_entries(git_repo, @sha, @path, recursive: @recursive)
end end
def readme def readme
...@@ -58,4 +59,21 @@ class Tree ...@@ -58,4 +59,21 @@ class Tree
def sorted_entries def sorted_entries
trees + blobs + submodules trees + blobs + submodules
end 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 end
---
title: API: allow recursive tree request
merge_request: 6088
author: Rebeca Méndez
...@@ -13,44 +13,58 @@ Parameters: ...@@ -13,44 +13,58 @@ Parameters:
- `id` (required) - The ID of a project - `id` (required) - The ID of a project
- `path` (optional) - The path inside repository. Used to get contend of subdirectories - `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 - `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 ```json
[ [
{ {
"name": "assets", "id": "a1e8f8d745cc87e3a9248358d9352bb7f9a0aeba",
"name": "html",
"type": "tree", "type": "tree",
"mode": "040000", "path": "files/html",
"id": "6229c43a7e16fcc7e95f923f8ddadb8281d9c6c6" "mode": "040000"
}, },
{ {
"name": "contexts", "id": "4535904260b1082e14f867f7a24fd8c21495bde3",
"name": "images",
"type": "tree", "type": "tree",
"mode": "040000", "path": "files/images",
"id": "faf1cdf33feadc7973118ca42d35f1e62977e91f" "mode": "040000"
}, },
{ {
"name": "controllers", "id": "31405c5ddef582c5a9b7a85230413ff90e2fe720",
"name": "js",
"type": "tree", "type": "tree",
"mode": "040000", "path": "files/js",
"id": "95633e8d258bf3dfba3a5268fb8440d263218d74" "mode": "040000"
}, },
{ {
"name": "Rakefile", "id": "cc71111cfad871212dc99572599a568bfe1e7e00",
"type": "blob", "name": "lfs",
"mode": "100644", "type": "tree",
"id": "35b2f05cbb4566b71b34554cf184a9d0bd9d46d6" "path": "files/lfs",
"mode": "040000"
}, },
{ {
"name": "VERSION", "id": "fd581c619bf59cfdfa9c8282377bb09c2f897520",
"type": "blob", "name": "markdown",
"mode": "100644", "type": "tree",
"id": "803e4a4f3727286c3093c63870c2b6524d30ec4f" "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", "type": "blob",
"mode": "100644", "path": "files/whitespace",
"id": "dfd2d862237323aa599be31b473d70a8a817943b" "mode": "100644"
} }
] ]
``` ```
......
...@@ -159,7 +159,7 @@ module API ...@@ -159,7 +159,7 @@ module API
end end
class RepoTreeObject < Grape::Entity class RepoTreeObject < Grape::Entity
expose :id, :name, :type expose :id, :name, :type, :path
expose :mode do |obj, options| expose :mode do |obj, options|
filemode = obj.mode.to_s(8) filemode = obj.mode.to_s(8)
......
...@@ -21,16 +21,18 @@ module API ...@@ -21,16 +21,18 @@ module API
# Parameters: # Parameters:
# id (required) - The ID of a project # 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 # 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: # Example Request:
# GET /projects/:id/repository/tree # GET /projects/:id/repository/tree
get ':id/repository/tree' do get ':id/repository/tree' do
ref = params[:ref_name] || user_project.try(:default_branch) || 'master' ref = params[:ref_name] || user_project.try(:default_branch) || 'master'
path = params[:path] || nil path = params[:path] || nil
recursive = to_boolean(params[:recursive])
commit = user_project.commit(ref) commit = user_project.commit(ref)
not_found!('Tree') unless commit 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 present tree.sorted_entries, with: Entities::RepoTreeObject
end end
......
...@@ -18,6 +18,7 @@ describe API::API, api: true do ...@@ -18,6 +18,7 @@ describe API::API, api: true do
it "returns project commits" do it "returns project commits" do
get api("/projects/#{project.id}/repository/tree", user) get api("/projects/#{project.id}/repository/tree", user)
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response).to be_an Array expect(json_response).to be_an Array
...@@ -43,6 +44,40 @@ describe API::API, api: true do ...@@ -43,6 +44,40 @@ describe API::API, api: true do
end end
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 describe "GET /projects/:id/repository/blobs/:sha" do
it "gets the raw file contents" do it "gets the raw file contents" do
get api("/projects/#{project.id}/repository/blobs/master?filepath=README.md", user) 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