]> source.dussan.org Git - redmine.git/commitdiff
Files REST API (#19116).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Wed, 21 Dec 2016 10:05:55 +0000 (10:05 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Wed, 21 Dec 2016 10:05:55 +0000 (10:05 +0000)
Patch by Lucile Quirion and Jake Kemme.

git-svn-id: http://svn.redmine.org/redmine/trunk@16109 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/controllers/files_controller.rb
app/helpers/attachments_helper.rb
app/views/files/index.api.rsb [new file with mode: 0644]
test/integration/api_test/api_routing_test.rb
test/integration/api_test/files_test.rb [new file with mode: 0644]

index 90a07cddc31a160d47d623d62729094e05d70075..535f14f5b020b3264c4ead41fee2e86ada926464 100644 (file)
@@ -20,7 +20,9 @@ class FilesController < ApplicationController
 
   before_action :find_project_by_project_id
   before_action :authorize
+  accept_api_auth :index, :create
 
+  helper :attachments 
   helper :sort
   include SortHelper
 
@@ -35,7 +37,10 @@ class FilesController < ApplicationController
                      references(:attachments).reorder(sort_clause).find(@project.id)]
     @containers += @project.versions.includes(:attachments).
                     references(:attachments).reorder(sort_clause).to_a.sort.reverse
-    render :layout => !request.xhr?
+    respond_to do |format|
+      format.html { render :layout => !request.xhr? }
+      format.api
+    end
   end
 
   def new
@@ -43,20 +48,29 @@ class FilesController < ApplicationController
   end
 
   def create
-    container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id]))
-    attachments = Attachment.attach_files(container, params[:attachments])
+    version_id = params[:version_id] || (params[:file] && params[:file][:version_id])
+    container = version_id.blank? ? @project : @project.versions.find_by_id(version_id)
+    attachments = Attachment.attach_files(container, (params[:attachments] || (params[:file] && params[:file][:token] && params)))
     render_attachment_warning_if_needed(container)
 
     if attachments[:files].present?
       if Setting.notified_events.include?('file_added')
         Mailer.attachments_added(attachments[:files]).deliver
       end
-      flash[:notice] = l(:label_file_added)
-      redirect_to project_files_path(@project)
+      respond_to do |format|
+        format.html {
+          flash[:notice] = l(:label_file_added)
+          redirect_to project_files_path(@project) }
+        format.api { render_api_ok }
+      end
     else
-      flash.now[:error] = l(:label_attachment) + " " + l('activerecord.errors.messages.invalid')
-      new
-      render :action => 'new'
+      respond_to do |format|
+        format.html {
+          flash.now[:error] = l(:label_attachment) + " " + l('activerecord.errors.messages.invalid')
+          new
+          render :action => 'new' }
+        format.api { render :status => :bad_request }
+      end
     end
   end
 end
index f3969f4b427af872715da63bb4866a9104c88139..217382476f5a24274cb2f0f5a06ca27d94af2d4b 100644 (file)
@@ -51,19 +51,26 @@ module AttachmentsHelper
     end
   end
 
-  def render_api_attachment(attachment, api)
+  def render_api_attachment(attachment, api, options={})
     api.attachment do
-      api.id attachment.id
-      api.filename attachment.filename
-      api.filesize attachment.filesize
-      api.content_type attachment.content_type
-      api.description attachment.description
-      api.content_url download_named_attachment_url(attachment, attachment.filename)
-      if attachment.thumbnailable?
-        api.thumbnail_url thumbnail_url(attachment)
-      end
-      api.author(:id => attachment.author.id, :name => attachment.author.name) if attachment.author
-      api.created_on attachment.created_on
+      render_api_attachment_attributes(attachment, api)
+      options.each { |key, value| eval("api.#{key} value") }
     end
   end
+
+  def render_api_attachment_attributes(attachment, api)
+    api.id attachment.id
+    api.filename attachment.filename
+    api.filesize attachment.filesize
+    api.content_type attachment.content_type
+    api.description attachment.description
+    api.content_url download_named_attachment_url(attachment, attachment.filename)
+    if attachment.thumbnailable?
+      api.thumbnail_url thumbnail_url(attachment)
+    end
+    if attachment.author
+      api.author(:id => attachment.author.id, :name => attachment.author.name)
+    end
+    api.created_on attachment.created_on
+  end
 end
diff --git a/app/views/files/index.api.rsb b/app/views/files/index.api.rsb
new file mode 100644 (file)
index 0000000..0a317e4
--- /dev/null
@@ -0,0 +1,14 @@
+api.array :files do
+  @containers.each do |container|
+    container.attachments.each do |attachment|
+      api.file do
+        render_api_attachment_attributes(attachment, api)
+        if container.is_a?(Version)
+          api.version :id => container.id, :name => container.name
+        end
+        api.digest attachment.digest
+        api.downloads attachment.downloads
+      end
+    end
+  end
+end
index 8d15e35a572c1b9eaf033dc07f903cd10dd36069..8f90baac467b9be6ebc2e7c5323af3e2b38a47b8 100644 (file)
@@ -34,6 +34,11 @@ class Redmine::ApiTest::ApiRoutingTest < Redmine::ApiTest::Routing
     should_route 'GET /enumerations/issue_priorities' => 'enumerations#index', :type => 'issue_priorities'
   end
 
+  def test_files
+    should_route 'GET /projects/foo/files' => 'files#index', :project_id => 'foo'
+    should_route 'POST /projects/foo/files' => 'files#create', :project_id => 'foo'
+  end
+
   def test_groups
     should_route 'GET /groups' => 'groups#index'
     should_route 'POST /groups' => 'groups#create'
diff --git a/test/integration/api_test/files_test.rb b/test/integration/api_test/files_test.rb
new file mode 100644 (file)
index 0000000..caef7f6
--- /dev/null
@@ -0,0 +1,115 @@
+# Redmine - project management software
+# Copyright (C) 2006-2015  Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+require File.expand_path('../../../test_helper', __FILE__)
+
+class Redmine::ApiTest::FilesTest < Redmine::ApiTest::Base
+  fixtures :projects,
+           :users,
+           :members,
+           :roles,
+           :member_roles,
+           :enabled_modules,
+           :attachments,
+           :versions
+
+  test "GET /projects/:project_id/files.xml should return the list of uploaded files" do
+    get '/projects/1/files.xml', {}, credentials('jsmith')
+    assert_response :success
+    assert_select 'files>file>id', :text => '8'
+  end
+
+  test "POST /projects/:project_id/files.json should create a file" do
+    set_tmp_attachments_directory
+    post '/uploads.xml', 'File content', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
+    token = Attachment.last.token
+    payload = <<-JSON
+{ "file": {
+    "token": "#{token}"
+  }
+}
+    JSON
+    post '/projects/1/files.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
+    assert_response :success
+    assert_equal 1, Attachment.last.container_id
+    assert_equal "Project", Attachment.last.container_type
+  end
+
+  test "POST /projects/:project_id/files.xml should create a file" do
+    set_tmp_attachments_directory
+    post '/uploads.xml', 'File content', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
+    token = Attachment.last.token
+    payload = <<-XML
+<file>
+  <token>#{token}</token>
+</file>
+    XML
+    post '/projects/1/files.xml', payload, {"CONTENT_TYPE" => 'application/xml'}.merge(credentials('jsmith'))
+    assert_response :success
+    assert_equal 1, Attachment.last.container_id
+    assert_equal "Project", Attachment.last.container_type
+  end
+
+  test "POST /projects/:project_id/files.json should refuse requests without the :token parameter" do
+    payload = <<-JSON
+{ "file": {
+    "filename": "project_file.zip",
+  }
+}
+    JSON
+    post '/projects/1/files.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
+    assert_response :bad_request
+  end
+
+  test "POST /projects/:project_id/files.json should accept :filename, :description, :content_type as optional parameters" do
+    set_tmp_attachments_directory
+    post '/uploads.xml', 'File content', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
+    token = Attachment.last.token
+    payload = <<-JSON
+{ "file": {
+    "filename": "New filename",
+    "description": "New description",
+    "content_type": "application/txt",
+    "token": "#{token}"
+  }
+}
+    JSON
+    post '/projects/1/files.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
+    assert_response :success
+    assert_equal "New filename", Attachment.last.filename
+    assert_equal "New description", Attachment.last.description
+    assert_equal "application/txt", Attachment.last.content_type
+  end
+
+  test "POST /projects/:project_id/files.json should accept :version_id to attach the files to a version" do
+    set_tmp_attachments_directory
+    post '/uploads.xml', 'File content', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
+    token = Attachment.last.token
+    payload = <<-JSON
+{ "file": {
+    "version_id": 3,
+    "filename": "New filename",
+    "description": "New description",
+    "token": "#{token}"
+  }
+}
+    JSON
+    post '/projects/1/files.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
+    assert_equal 3, Attachment.last.container_id
+    assert_equal "Version", Attachment.last.container_type
+  end
+end