##// END OF EJS Templates
Files REST API (#19116)....
Jean-Philippe Lang -
r15727:e068f855dcc7
parent child
Show More
@@ -0,0 +1,14
1 api.array :files do
2 @containers.each do |container|
3 container.attachments.each do |attachment|
4 api.file do
5 render_api_attachment_attributes(attachment, api)
6 if container.is_a?(Version)
7 api.version :id => container.id, :name => container.name
8 end
9 api.digest attachment.digest
10 api.downloads attachment.downloads
11 end
12 end
13 end
14 end
@@ -0,0 +1,115
1 # Redmine - project management software
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require File.expand_path('../../../test_helper', __FILE__)
19
20 class Redmine::ApiTest::FilesTest < Redmine::ApiTest::Base
21 fixtures :projects,
22 :users,
23 :members,
24 :roles,
25 :member_roles,
26 :enabled_modules,
27 :attachments,
28 :versions
29
30 test "GET /projects/:project_id/files.xml should return the list of uploaded files" do
31 get '/projects/1/files.xml', {}, credentials('jsmith')
32 assert_response :success
33 assert_select 'files>file>id', :text => '8'
34 end
35
36 test "POST /projects/:project_id/files.json should create a file" do
37 set_tmp_attachments_directory
38 post '/uploads.xml', 'File content', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
39 token = Attachment.last.token
40 payload = <<-JSON
41 { "file": {
42 "token": "#{token}"
43 }
44 }
45 JSON
46 post '/projects/1/files.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
47 assert_response :success
48 assert_equal 1, Attachment.last.container_id
49 assert_equal "Project", Attachment.last.container_type
50 end
51
52 test "POST /projects/:project_id/files.xml should create a file" do
53 set_tmp_attachments_directory
54 post '/uploads.xml', 'File content', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
55 token = Attachment.last.token
56 payload = <<-XML
57 <file>
58 <token>#{token}</token>
59 </file>
60 XML
61 post '/projects/1/files.xml', payload, {"CONTENT_TYPE" => 'application/xml'}.merge(credentials('jsmith'))
62 assert_response :success
63 assert_equal 1, Attachment.last.container_id
64 assert_equal "Project", Attachment.last.container_type
65 end
66
67 test "POST /projects/:project_id/files.json should refuse requests without the :token parameter" do
68 payload = <<-JSON
69 { "file": {
70 "filename": "project_file.zip",
71 }
72 }
73 JSON
74 post '/projects/1/files.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
75 assert_response :bad_request
76 end
77
78 test "POST /projects/:project_id/files.json should accept :filename, :description, :content_type as optional parameters" do
79 set_tmp_attachments_directory
80 post '/uploads.xml', 'File content', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
81 token = Attachment.last.token
82 payload = <<-JSON
83 { "file": {
84 "filename": "New filename",
85 "description": "New description",
86 "content_type": "application/txt",
87 "token": "#{token}"
88 }
89 }
90 JSON
91 post '/projects/1/files.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
92 assert_response :success
93 assert_equal "New filename", Attachment.last.filename
94 assert_equal "New description", Attachment.last.description
95 assert_equal "application/txt", Attachment.last.content_type
96 end
97
98 test "POST /projects/:project_id/files.json should accept :version_id to attach the files to a version" do
99 set_tmp_attachments_directory
100 post '/uploads.xml', 'File content', {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
101 token = Attachment.last.token
102 payload = <<-JSON
103 { "file": {
104 "version_id": 3,
105 "filename": "New filename",
106 "description": "New description",
107 "token": "#{token}"
108 }
109 }
110 JSON
111 post '/projects/1/files.json', payload, {"CONTENT_TYPE" => 'application/json'}.merge(credentials('jsmith'))
112 assert_equal 3, Attachment.last.container_id
113 assert_equal "Version", Attachment.last.container_type
114 end
115 end
@@ -20,7 +20,9 class FilesController < ApplicationController
20
20
21 before_action :find_project_by_project_id
21 before_action :find_project_by_project_id
22 before_action :authorize
22 before_action :authorize
23 accept_api_auth :index, :create
23
24
25 helper :attachments
24 helper :sort
26 helper :sort
25 include SortHelper
27 include SortHelper
26
28
@@ -35,7 +37,10 class FilesController < ApplicationController
35 references(:attachments).reorder(sort_clause).find(@project.id)]
37 references(:attachments).reorder(sort_clause).find(@project.id)]
36 @containers += @project.versions.includes(:attachments).
38 @containers += @project.versions.includes(:attachments).
37 references(:attachments).reorder(sort_clause).to_a.sort.reverse
39 references(:attachments).reorder(sort_clause).to_a.sort.reverse
38 render :layout => !request.xhr?
40 respond_to do |format|
41 format.html { render :layout => !request.xhr? }
42 format.api
43 end
39 end
44 end
40
45
41 def new
46 def new
@@ -43,20 +48,29 class FilesController < ApplicationController
43 end
48 end
44
49
45 def create
50 def create
46 container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id]))
51 version_id = params[:version_id] || (params[:file] && params[:file][:version_id])
47 attachments = Attachment.attach_files(container, params[:attachments])
52 container = version_id.blank? ? @project : @project.versions.find_by_id(version_id)
53 attachments = Attachment.attach_files(container, (params[:attachments] || (params[:file] && params[:file][:token] && params)))
48 render_attachment_warning_if_needed(container)
54 render_attachment_warning_if_needed(container)
49
55
50 if attachments[:files].present?
56 if attachments[:files].present?
51 if Setting.notified_events.include?('file_added')
57 if Setting.notified_events.include?('file_added')
52 Mailer.attachments_added(attachments[:files]).deliver
58 Mailer.attachments_added(attachments[:files]).deliver
53 end
59 end
54 flash[:notice] = l(:label_file_added)
60 respond_to do |format|
55 redirect_to project_files_path(@project)
61 format.html {
62 flash[:notice] = l(:label_file_added)
63 redirect_to project_files_path(@project) }
64 format.api { render_api_ok }
65 end
56 else
66 else
57 flash.now[:error] = l(:label_attachment) + " " + l('activerecord.errors.messages.invalid')
67 respond_to do |format|
58 new
68 format.html {
59 render :action => 'new'
69 flash.now[:error] = l(:label_attachment) + " " + l('activerecord.errors.messages.invalid')
70 new
71 render :action => 'new' }
72 format.api { render :status => :bad_request }
73 end
60 end
74 end
61 end
75 end
62 end
76 end
@@ -51,19 +51,26 module AttachmentsHelper
51 end
51 end
52 end
52 end
53
53
54 def render_api_attachment(attachment, api)
54 def render_api_attachment(attachment, api, options={})
55 api.attachment do
55 api.attachment do
56 api.id attachment.id
56 render_api_attachment_attributes(attachment, api)
57 api.filename attachment.filename
57 options.each { |key, value| eval("api.#{key} value") }
58 api.filesize attachment.filesize
59 api.content_type attachment.content_type
60 api.description attachment.description
61 api.content_url download_named_attachment_url(attachment, attachment.filename)
62 if attachment.thumbnailable?
63 api.thumbnail_url thumbnail_url(attachment)
64 end
65 api.author(:id => attachment.author.id, :name => attachment.author.name) if attachment.author
66 api.created_on attachment.created_on
67 end
58 end
68 end
59 end
60
61 def render_api_attachment_attributes(attachment, api)
62 api.id attachment.id
63 api.filename attachment.filename
64 api.filesize attachment.filesize
65 api.content_type attachment.content_type
66 api.description attachment.description
67 api.content_url download_named_attachment_url(attachment, attachment.filename)
68 if attachment.thumbnailable?
69 api.thumbnail_url thumbnail_url(attachment)
70 end
71 if attachment.author
72 api.author(:id => attachment.author.id, :name => attachment.author.name)
73 end
74 api.created_on attachment.created_on
75 end
69 end
76 end
@@ -34,6 +34,11 class Redmine::ApiTest::ApiRoutingTest < Redmine::ApiTest::Routing
34 should_route 'GET /enumerations/issue_priorities' => 'enumerations#index', :type => 'issue_priorities'
34 should_route 'GET /enumerations/issue_priorities' => 'enumerations#index', :type => 'issue_priorities'
35 end
35 end
36
36
37 def test_files
38 should_route 'GET /projects/foo/files' => 'files#index', :project_id => 'foo'
39 should_route 'POST /projects/foo/files' => 'files#create', :project_id => 'foo'
40 end
41
37 def test_groups
42 def test_groups
38 should_route 'GET /groups' => 'groups#index'
43 should_route 'GET /groups' => 'groups#index'
39 should_route 'POST /groups' => 'groups#create'
44 should_route 'POST /groups' => 'groups#create'
General Comments 0
You need to be logged in to leave comments. Login now