From ddbe978041753d7851780165f8c6c66865570862 Mon Sep 17 00:00:00 2001
From: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
Date: Tue, 18 Feb 2014 12:27:02 +0200
Subject: [PATCH] Complete api files CRUD

Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
---
 app/models/repository.rb             |  12 ++--
 app/views/help/_api_layout.html.haml |   2 +-
 doc/api/README.md                    |   1 +
 doc/api/repositories.md              |  41 -----------
 doc/api/repository_files.md          | 102 +++++++++++++++++++++++++++
 lib/api/files.rb                     |  53 +++++++++++++-
 spec/requests/api/files_spec.rb      |  30 ++++++++
 7 files changed, 192 insertions(+), 49 deletions(-)
 create mode 100644 doc/api/repository_files.md

diff --git a/app/models/repository.rb b/app/models/repository.rb
index 2c2bf242b9..99d908b5d8 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -180,13 +180,13 @@ class Repository
   end
 
   def blob_at_branch(branch_name, path)
-     last_commit = commit(branch_name)
+    last_commit = commit(branch_name)
 
-     if last_commit
-       blob_at(last_commit.sha, path)
-     else
-       nil
-     end
+    if last_commit
+      blob_at(last_commit.sha, path)
+    else
+      nil
+    end
   end
 
   # Returns url for submodule
diff --git a/app/views/help/_api_layout.html.haml b/app/views/help/_api_layout.html.haml
index c211b65841..9f7bc78355 100644
--- a/app/views/help/_api_layout.html.haml
+++ b/app/views/help/_api_layout.html.haml
@@ -5,7 +5,7 @@
         %i.icon-angle-left
         Back to help
     %ul.nav.nav-pills.nav-stacked
-      - %w(README projects project_snippets repositories deploy_keys users groups session issues milestones merge_requests notes system_hooks).each do |file|
+      - %w(README projects project_snippets repositories repository_files deploy_keys users groups session issues milestones merge_requests notes system_hooks).each do |file|
         %li{class: file == @category ? 'active' : nil}
           = link_to file.titleize, help_api_file_path(file)
 
diff --git a/doc/api/README.md b/doc/api/README.md
index 517a9fae6f..f13f319a84 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -127,6 +127,7 @@ But when you want to create a link to web page - use  `http:://host/project/issu
 + [Projects](projects.md)
 + [Project Snippets](project_snippets.md)
 + [Repositories](repositories.md)
++ [Repository Files](repository_files.md)
 + [Merge Requests](merge_requests.md)
 + [Issues](issues.md)
 + [Milestones](milestones.md)
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index 0160726300..70e297f1bc 100644
--- a/doc/api/repositories.md
+++ b/doc/api/repositories.md
@@ -388,44 +388,3 @@ GET /projects/:id/repository/archive
 Parameters:
 + `id` (required) - The ID of a project
 + `sha` (optional) - The commit sha to download defaults to the tip of the default branch
-
-
-## Create new file in repository
-
-```
-POST /projects/:id/repository/files
-```
-
-Parameters:
-
-+ `file_path` (optional) - Full path to new file. Ex. lib/class.rb
-+ `branch_name` (required) - The name of branch
-+ `encoding` (optional) - 'text' or 'base64'. Text is default.
-+ `content` (required) - File content
-+ `commit_message` (required) - Commit message
-
-## Update existing file in repository
-
-```
-PUT /projects/:id/repository/files
-```
-
-Parameters:
-
-+ `file_path` (required) - Full path to file. Ex. lib/class.rb
-+ `branch_name` (required) - The name of branch
-+ `encoding` (optional) - 'text' or 'base64'. Text is default.
-+ `content` (required) - New file content
-+ `commit_message` (required) - Commit message
-
-## Delete existing file in repository
-
-```
-DELETE /projects/:id/repository/files
-```
-
-Parameters:
-
-+ `file_path` (required) - Full path to file. Ex. lib/class.rb
-+ `branch_name` (required) - The name of branch
-+ `commit_message` (required) - Commit message
diff --git a/doc/api/repository_files.md b/doc/api/repository_files.md
new file mode 100644
index 0000000000..cafab8c828
--- /dev/null
+++ b/doc/api/repository_files.md
@@ -0,0 +1,102 @@
+# CRUD for repository files
+
+## Create, read, update and delete repository files using this API
+
+- - -
+
+## Get file from repository
+
+Allows you to receive information about file in repository like name, size, content.
+Note that file content is Base64 encoded.
+
+```
+GET /projects/:id/repository/files
+```
+
+Example response:
+
+```json
+{
+  "file_name": "key.rb",
+  "file_path": "app/models/key.rb",
+  "size": 1476,
+  "encoding": "base64",
+  "content": "IyA9PSBTY2hlbWEgSW5mb3...",
+  "ref": "master",
+  "blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83",
+  "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50"
+}
+```
+
+Parameters:
+
++ `file_path` (required) - Full path to new file. Ex. lib/class.rb
++ `ref` (required) - The name of branch, tag or commit
+
+## Create new file in repository
+
+```
+POST /projects/:id/repository/files
+```
+
+Example response:
+
+```json
+{
+  "file_name": "app/project.rb",
+  "branch_name": "master",
+}
+```
+
+Parameters:
+
++ `file_path` (required) - Full path to new file. Ex. lib/class.rb
++ `branch_name` (required) - The name of branch
++ `encoding` (optional) - 'text' or 'base64'. Text is default.
++ `content` (required) - File content
++ `commit_message` (required) - Commit message
+
+## Update existing file in repository
+
+```
+PUT /projects/:id/repository/files
+```
+
+Example response:
+
+```json
+{
+  "file_name": "app/project.rb",
+  "branch_name": "master",
+}
+```
+
+Parameters:
+
++ `file_path` (required) - Full path to file. Ex. lib/class.rb
++ `branch_name` (required) - The name of branch
++ `encoding` (optional) - 'text' or 'base64'. Text is default.
++ `content` (required) - New file content
++ `commit_message` (required) - Commit message
+
+## Delete existing file in repository
+
+```
+DELETE /projects/:id/repository/files
+```
+
+Example response:
+
+```json
+{
+  "file_name": "app/project.rb",
+  "branch_name": "master",
+}
+```
+
+Parameters:
+
++ `file_path` (required) - Full path to file. Ex. lib/class.rb
++ `branch_name` (required) - The name of branch
++ `commit_message` (required) - Commit message
+
diff --git a/lib/api/files.rb b/lib/api/files.rb
index 213604915a..e0c46f92b8 100644
--- a/lib/api/files.rb
+++ b/lib/api/files.rb
@@ -5,10 +5,61 @@ module API
     before { authorize! :push_code, user_project }
 
     resource :projects do
+      # Get file from repository
+      # File content is Base64 encoded
+      #
+      # Parameters:
+      #   file_path (required) - The path to the file. Ex. lib/class.rb
+      #   ref (required) - The name of branch, tag or commit
+      #
+      # Example Request:
+      #   GET /projects/:id/repository/files
+      #
+      # Example response:
+      # {
+      #   "file_name": "key.rb",
+      #   "file_path": "app/models/key.rb",
+      #   "size": 1476,
+      #   "encoding": "base64",
+      #   "content": "IyA9PSBTY2hlbWEgSW5mb3...",
+      #   "ref": "master",
+      #   "blob_id": "79f7bbd25901e8334750839545a9bd021f0e4c83",
+      #   "commit_id": "d5a3ff139356ce33e37e73add446f16869741b50"
+      # }
+      #
+      get ":id/repository/files" do
+        required_attributes! [:file_path, :ref]
+        attrs = attributes_for_keys [:file_path, :ref]
+        ref = attrs.delete(:ref)
+        file_path = attrs.delete(:file_path)
+
+        commit = user_project.repository.commit(ref)
+        not_found! "Commit" unless commit
+
+        blob = user_project.repository.blob_at(commit.sha, file_path)
+
+        if blob
+          status(200)
+
+          {
+            file_name: blob.name,
+            file_path: blob.path,
+            size: blob.size,
+            encoding: "base64",
+            content: Base64.encode64(blob.data),
+            ref: ref,
+            blob_id: blob.id,
+            commit_id: commit.id,
+          }
+        else
+          render_api_error!('File not found', 404)
+        end
+      end
+
       # Create new file in repository
       #
       # Parameters:
-      #   file_path (optional) - The path to new file. Ex. lib/class.rb
+      #   file_path (required) - The path to new file. Ex. lib/class.rb
       #   branch_name (required) - The name of branch
       #   content (required) - File content
       #   commit_message (required) - Commit message
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index acef7df877..fa25a4bec6 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -9,6 +9,36 @@ describe API::API do
   let!(:project) { create(:project, namespace: user.namespace ) }
   before { project.team << [user, :developer] }
 
+  describe "GET /projects/:id/repository/files" do
+    it "should return file info" do
+      params = {
+        file_path: 'app/models/key.rb',
+        ref: 'master',
+      }
+
+      get api("/projects/#{project.id}/repository/files", user), params
+      response.status.should == 200
+      json_response['file_path'].should == 'app/models/key.rb'
+      json_response['file_name'].should == 'key.rb'
+      Base64.decode64(json_response['content']).lines.first.should == "class Key < ActiveRecord::Base\n"
+    end
+
+    it "should return a 400 bad request if no params given" do
+      get api("/projects/#{project.id}/repository/files", user)
+      response.status.should == 400
+    end
+
+    it "should return a 404 if such file does not exist" do
+      params = {
+        file_path: 'app/models/application.rb',
+        ref: 'master',
+      }
+
+      get api("/projects/#{project.id}/repository/files", user), params
+      response.status.should == 404
+    end
+  end
+
   describe "POST /projects/:id/repository/files" do
     let(:valid_params) {
       {
-- 
2.30.9