Patch #19116 » 0001-REST-API-add-files-REST-API_v2.patch
app/controllers/files_controller.rb | ||
---|---|---|
20 | 20 | |
21 | 21 |
before_filter :find_project_by_project_id |
22 | 22 |
before_filter :authorize |
23 |
accept_api_auth :index, :create |
|
23 | 24 | |
24 | 25 |
helper :sort |
26 |
helper :attachments |
|
25 | 27 |
include SortHelper |
26 | 28 | |
27 | 29 |
def index |
... | ... | |
35 | 37 |
references(:attachments).reorder(sort_clause).find(@project.id)] |
36 | 38 |
@containers += @project.versions.includes(:attachments). |
37 | 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 |
|
44 | ||
39 | 45 |
end |
40 | 46 | |
41 | 47 |
def new |
... | ... | |
43 | 49 |
end |
44 | 50 | |
45 | 51 |
def create |
46 |
container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id])) |
|
47 |
attachments = Attachment.attach_files(container, params[:attachments]) |
|
52 |
version_id = params[:version_id] || (params[:file] && params[:file][:version_id]) |
|
53 |
container = version_id.blank? ? @project : @project.versions.find_by_id(version_id) |
|
54 |
attachments = Attachment.attach_files(container, (params[:attachments] || (params[:file] && params[:file][:token] && params))) |
|
48 | 55 |
render_attachment_warning_if_needed(container) |
49 | 56 | |
50 | 57 |
if !attachments.empty? && !attachments[:files].blank? && Setting.notified_events.include?('file_added') |
51 | 58 |
Mailer.attachments_added(attachments[:files]).deliver |
52 | 59 |
end |
53 |
redirect_to project_files_path(@project) |
|
60 | ||
61 |
respond_to do |format| |
|
62 |
format.html { redirect_to project_files_path(@project) } |
|
63 |
format.api { if attachments.empty? |
|
64 |
render :status => :bad_request |
|
65 |
else |
|
66 |
render_api_ok |
|
67 |
end } |
|
68 |
end |
|
54 | 69 |
end |
55 | 70 |
end |
app/helpers/attachments_helper.rb | ||
---|---|---|
51 | 51 |
end |
52 | 52 |
end |
53 | 53 | |
54 |
def render_api_attachment(attachment, api) |
|
54 |
def render_api_attachment(attachment, api, options={})
|
|
55 | 55 |
api.attachment do |
56 | 56 |
api.id attachment.id |
57 | 57 |
api.filename attachment.filename |
... | ... | |
64 | 64 |
end |
65 | 65 |
api.author(:id => attachment.author.id, :name => attachment.author.name) if attachment.author |
66 | 66 |
api.created_on attachment.created_on |
67 |
options.each { |key, value| eval("api.#{key} value") } |
|
67 | 68 |
end |
68 | 69 |
end |
69 | 70 |
end |
app/views/files/index.api.rsb | ||
---|---|---|
1 |
api.array :files do |
|
2 |
@containers.each do |container| |
|
3 |
next if container.attachments.empty? |
|
4 |
if container.is_a?(Version) |
|
5 |
version_id = container.name |
|
6 |
end |
|
7 | ||
8 |
container.attachments.each do |file| |
|
9 |
render_api_attachment(file, api, { version_id: version_id, digest: file.digest, downloads: file.downloads } ) |
|
10 |
end |
|
11 |
end |
|
12 |
end |
test/integration/api_test/api_routing_test.rb | ||
---|---|---|
32 | 32 |
should_route 'GET /enumerations/issue_priorities' => 'enumerations#index', :type => 'issue_priorities' |
33 | 33 |
end |
34 | 34 | |
35 |
def test_files |
|
36 |
should_route 'GET /projects/foo/files' => 'files#index', :project_id => 'foo' |
|
37 |
should_route 'POST /projects/foo/files' => 'files#create', :project_id => 'foo' |
|
38 |
end |
|
39 | ||
35 | 40 |
def test_groups |
36 | 41 |
should_route 'GET /groups' => 'groups#index' |
37 | 42 |
should_route 'POST /groups' => 'groups#create' |
test/integration/api_test/files_test.rb | ||
---|---|---|
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 attachment 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 |