From 5c966f70fb218d6f4de0f888733604293f36c33e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rebeca=20M=C3=A9ndez?= Date: Mon, 29 Aug 2016 17:23:40 +0200 Subject: Issue #4270: Recursive option for files through API --- app/models/repository.rb | 4 +- app/models/tree.rb | 22 ++++++++++- changelogs/unreleased/master-recursiveTree.yml | 4 ++ doc/api/repositories.md | 54 ++++++++++++++++---------- lib/api/entities.rb | 2 +- lib/api/repositories.rb | 4 +- spec/requests/api/repositories_spec.rb | 35 +++++++++++++++++ 7 files changed, 99 insertions(+), 26 deletions(-) create mode 100644 changelogs/unreleased/master-recursiveTree.yml diff --git a/app/models/repository.rb b/app/models/repository.rb index 063dc74021d..4282197faa5 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -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) diff --git a/app/models/tree.rb b/app/models/tree.rb index 7c4ed6e393b..2d1d68dbd81 100644 --- a/app/models/tree.rb +++ b/app/models/tree.rb @@ -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 diff --git a/changelogs/unreleased/master-recursiveTree.yml b/changelogs/unreleased/master-recursiveTree.yml new file mode 100644 index 00000000000..c6384d172e2 --- /dev/null +++ b/changelogs/unreleased/master-recursiveTree.yml @@ -0,0 +1,4 @@ +--- +title: API: allow recursive tree request +merge_request: 6088 +author: Rebeca Méndez diff --git a/doc/api/repositories.md b/doc/api/repositories.md index b6cca5d4e2a..bcf8b955044 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -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" } ] ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 1942aeea656..8f1aaaaaaa0 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -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) diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb index f55aceed92c..0bb2f74809a 100644 --- a/lib/api/repositories.rb +++ b/lib/api/repositories.rb @@ -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 diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index c4dc2d9006a..38c8ad34f9d 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -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) -- cgit v1.2.1