Feature #44141 » 0002-Add-REST-API-for-forum-messages.patch
| app/controllers/messages_controller.rb | ||
|---|---|---|
| 20 | 20 |
class MessagesController < ApplicationController |
| 21 | 21 |
menu_item :boards |
| 22 | 22 |
default_search_scope :messages |
| 23 |
before_action :find_board, :only => [:new, :preview] |
|
| 23 |
before_action :find_board, :only => [:index, :create, :new, :preview] |
|
| 24 |
before_action :find_topic, :only => [:replies, :create_reply] |
|
| 24 | 25 |
before_action :find_attachments, :only => [:preview] |
| 25 |
before_action :find_message, :except => [:new, :preview] |
|
| 26 |
before_action :authorize, :except => [:preview, :edit, :destroy] |
|
| 26 |
before_action :find_message, :only => [:show, :reply, :edit, :update, :destroy, :quote] |
|
| 27 |
before_action :authorize, :except => [:preview, :edit, :update, :destroy] |
|
| 28 |
accept_api_auth :index, :show, :create, :update, :destroy, :replies, :create_reply |
|
| 27 | 29 | |
| 28 | 30 |
helper :boards |
| 29 | 31 |
helper :watchers |
| ... | ... | |
| 33 | 35 | |
| 34 | 36 |
REPLIES_PER_PAGE = 25 unless const_defined?(:REPLIES_PER_PAGE) |
| 35 | 37 | |
| 38 |
# List topics of a board |
|
| 39 |
def index |
|
| 40 |
@offset, @limit = api_offset_and_limit |
|
| 41 |
scope = @board.topics |
|
| 42 |
@message_count = scope.count |
|
| 43 |
@messages = scope. |
|
| 44 |
reorder(:sticky => :desc, :id => :desc). |
|
| 45 |
includes(:author, :parent, {:last_reply => :author}, {:board => :project}).
|
|
| 46 |
limit(@limit). |
|
| 47 |
offset(@offset). |
|
| 48 |
to_a |
|
| 49 | ||
| 50 |
respond_to do |format| |
|
| 51 |
format.html {render_404}
|
|
| 52 |
format.api {render :action => 'topics'}
|
|
| 53 |
end |
|
| 54 |
end |
|
| 55 | ||
| 36 | 56 |
# Show a topic and its replies |
| 37 | 57 |
def show |
| 38 |
page = params[:page] |
|
| 39 |
# Find the page of the requested reply |
|
| 40 |
if params[:r] && page.nil? |
|
| 41 |
offset = @topic.children.where("#{Message.table_name}.id < ?", params[:r].to_i).count
|
|
| 42 |
page = 1 + offset / REPLIES_PER_PAGE |
|
| 43 |
end |
|
| 58 |
respond_to do |format| |
|
| 59 |
format.html do |
|
| 60 |
page = params[:page] |
|
| 61 |
# Find the page of the requested reply |
|
| 62 |
if params[:r] && page.nil? |
|
| 63 |
offset = @topic.children.where("#{Message.table_name}.id < ?", params[:r].to_i).count
|
|
| 64 |
page = 1 + offset / REPLIES_PER_PAGE |
|
| 65 |
end |
|
| 44 | 66 | |
| 45 |
@reply_count = @topic.children.count |
|
| 46 |
@reply_pages = Paginator.new @reply_count, REPLIES_PER_PAGE, page |
|
| 47 |
@replies = @topic.children. |
|
| 48 |
includes(:author, :attachments, {:board => :project}).
|
|
| 49 |
reorder("#{Message.table_name}.created_on ASC, #{Message.table_name}.id ASC").
|
|
| 50 |
limit(@reply_pages.per_page). |
|
| 51 |
offset(@reply_pages.offset). |
|
| 52 |
to_a |
|
| 67 |
@reply_count = @topic.children.count
|
|
| 68 |
@reply_pages = Paginator.new @reply_count, REPLIES_PER_PAGE, page
|
|
| 69 |
@replies = @topic.children.
|
|
| 70 |
includes(:author, :attachments, {:board => :project}).
|
|
| 71 |
reorder("#{Message.table_name}.created_on ASC, #{Message.table_name}.id ASC").
|
|
| 72 |
limit(@reply_pages.per_page).
|
|
| 73 |
offset(@reply_pages.offset).
|
|
| 74 |
to_a
|
|
| 53 | 75 | |
| 54 |
Message.preload_reaction_details(@replies) |
|
| 76 |
Message.preload_reaction_details(@replies) |
|
| 77 | ||
| 78 |
@reply = Message.new(:subject => "RE: #{@message.subject}")
|
|
| 79 |
render :action => "show", :layout => false if request.xhr? |
|
| 80 |
end |
|
| 81 |
format.api |
|
| 82 |
end |
|
| 83 |
end |
|
| 55 | 84 | |
| 56 |
@reply = Message.new(:subject => "RE: #{@message.subject}")
|
|
| 57 |
render :action => "show", :layout => false if request.xhr? |
|
| 85 |
# Create a new topic through the REST API |
|
| 86 |
def create |
|
| 87 |
@message = Message.new |
|
| 88 |
@message.author = User.current |
|
| 89 |
@message.board = @board |
|
| 90 |
@message.safe_attributes = params[:message] |
|
| 91 |
# Ignore board_id overrides from the request body for this scoped API. |
|
| 92 |
@message.board = @board |
|
| 93 |
@message.save_attachments(params[:attachments] || (params[:message] && params[:message][:uploads])) |
|
| 94 |
if @message.save |
|
| 95 |
call_hook(:controller_messages_new_after_save, {:params => params, :message => @message})
|
|
| 96 |
render_attachment_warning_if_needed(@message) |
|
| 97 |
respond_to do |format| |
|
| 98 |
format.html {render_404}
|
|
| 99 |
format.api do |
|
| 100 |
render( |
|
| 101 |
:action => 'show', |
|
| 102 |
:status => :created, |
|
| 103 |
:location => message_url(@message) |
|
| 104 |
) |
|
| 105 |
end |
|
| 106 |
end |
|
| 107 |
else |
|
| 108 |
respond_to do |format| |
|
| 109 |
format.html {render_404}
|
|
| 110 |
format.api {render_validation_errors(@message)}
|
|
| 111 |
end |
|
| 112 |
end |
|
| 58 | 113 |
end |
| 59 | 114 | |
| 60 | 115 |
# Create a new topic |
| ... | ... | |
| 74 | 129 |
end |
| 75 | 130 |
end |
| 76 | 131 | |
| 132 |
# List replies of a topic |
|
| 133 |
def replies |
|
| 134 |
@offset, @limit = api_offset_and_limit |
|
| 135 |
scope = @topic.children |
|
| 136 |
@message_count = scope.count |
|
| 137 |
@messages = scope. |
|
| 138 |
includes(:author, :parent, {:last_reply => :author}, {:board => :project}).
|
|
| 139 |
reorder("#{Message.table_name}.created_on ASC, #{Message.table_name}.id ASC").
|
|
| 140 |
limit(@limit). |
|
| 141 |
offset(@offset). |
|
| 142 |
to_a |
|
| 143 | ||
| 144 |
respond_to do |format| |
|
| 145 |
format.html {render_404}
|
|
| 146 |
format.api |
|
| 147 |
end |
|
| 148 |
end |
|
| 149 | ||
| 77 | 150 |
# Reply to a topic |
| 78 | 151 |
def reply |
| 79 | 152 |
@reply = Message.new |
| ... | ... | |
| 90 | 163 |
redirect_to board_message_path(@board, @topic, :r => @reply) |
| 91 | 164 |
end |
| 92 | 165 | |
| 166 |
# Reply to a topic through the REST API |
|
| 167 |
def create_reply |
|
| 168 |
@reply = Message.new(:subject => "RE: #{@topic.subject}")
|
|
| 169 |
@reply.author = User.current |
|
| 170 |
@reply.board = @board |
|
| 171 |
@reply.safe_attributes = params[:reply] |
|
| 172 |
# Ignore board_id overrides from the request body for this scoped API. |
|
| 173 |
@reply.board = @board |
|
| 174 |
@reply.save_attachments(params[:attachments] || (params[:reply] && params[:reply][:uploads])) |
|
| 175 |
@topic.children << @reply |
|
| 176 |
if @reply.new_record? |
|
| 177 |
respond_to do |format| |
|
| 178 |
format.html {render_404}
|
|
| 179 |
format.api {render_validation_errors(@reply)}
|
|
| 180 |
end |
|
| 181 |
else |
|
| 182 |
call_hook(:controller_messages_reply_after_save, {:params => params, :message => @reply})
|
|
| 183 |
render_attachment_warning_if_needed(@reply) |
|
| 184 |
@message = @reply |
|
| 185 |
respond_to do |format| |
|
| 186 |
format.html {render_404}
|
|
| 187 |
format.api do |
|
| 188 |
render( |
|
| 189 |
:action => 'show', |
|
| 190 |
:status => :created, |
|
| 191 |
:location => message_url(@reply) |
|
| 192 |
) |
|
| 193 |
end |
|
| 194 |
end |
|
| 195 |
end |
|
| 196 |
end |
|
| 197 | ||
| 93 | 198 |
# Edit a message |
| 94 | 199 |
def edit |
| 95 | 200 |
(render_403; return false) unless @message.editable_by?(User.current) |
| ... | ... | |
| 105 | 210 |
end |
| 106 | 211 |
end |
| 107 | 212 | |
| 213 |
def update |
|
| 214 |
(render_403; return false) unless @message.editable_by?(User.current) |
|
| 215 |
project = @message.project |
|
| 216 |
@message.safe_attributes = params[:message] |
|
| 217 |
unless project.boards.exists?(@message.board_id) |
|
| 218 |
@message.errors.add(:board_id, :invalid) |
|
| 219 |
respond_to do |format| |
|
| 220 |
format.html {render_404}
|
|
| 221 |
format.api {render_validation_errors(@message)}
|
|
| 222 |
end |
|
| 223 |
return |
|
| 224 |
end |
|
| 225 |
@message.save_attachments(params[:attachments] || (params[:message] && params[:message][:uploads])) |
|
| 226 |
if @message.save |
|
| 227 |
render_attachment_warning_if_needed(@message) |
|
| 228 |
respond_to do |format| |
|
| 229 |
format.html {render_404}
|
|
| 230 |
format.api {render_api_ok}
|
|
| 231 |
end |
|
| 232 |
else |
|
| 233 |
respond_to do |format| |
|
| 234 |
format.html {render_404}
|
|
| 235 |
format.api {render_validation_errors(@message)}
|
|
| 236 |
end |
|
| 237 |
end |
|
| 238 |
end |
|
| 239 | ||
| 108 | 240 |
# Delete a messages |
| 109 | 241 |
def destroy |
| 110 | 242 |
(render_403; return false) unless @message.destroyable_by?(User.current) |
| 111 | 243 |
r = @message.to_param |
| 112 | 244 |
@message.destroy |
| 113 |
flash[:notice] = l(:notice_successful_delete) |
|
| 114 |
if @message.parent |
|
| 115 |
redirect_to board_message_path(@board, @message.parent, :r => r) |
|
| 116 |
else |
|
| 117 |
redirect_to project_board_path(@project, @board) |
|
| 245 |
respond_to do |format| |
|
| 246 |
format.html do |
|
| 247 |
flash[:notice] = l(:notice_successful_delete) |
|
| 248 |
if @message.parent |
|
| 249 |
redirect_to board_message_path(@board, @message.parent, :r => r) |
|
| 250 |
else |
|
| 251 |
redirect_to project_board_path(@project, @board) |
|
| 252 |
end |
|
| 253 |
end |
|
| 254 |
format.api {render_api_ok}
|
|
| 118 | 255 |
end |
| 119 | 256 |
end |
| 120 | 257 | |
| ... | ... | |
| 144 | 281 |
private |
| 145 | 282 | |
| 146 | 283 |
def find_message |
| 147 |
return unless find_board |
|
| 284 |
if params[:board_id] |
|
| 285 |
return unless find_board |
|
| 148 | 286 | |
| 149 |
@message = @board.messages.includes(:parent).find(params[:id]) |
|
| 287 |
@message = @board.messages.includes(:parent).find(params[:id]) |
|
| 288 |
else |
|
| 289 |
@message = Message.visible.includes(:parent, :author, :attachments, {:board => :project}).find(params[:id])
|
|
| 290 |
@board = @message.board |
|
| 291 |
@project = @board.project |
|
| 292 |
end |
|
| 150 | 293 |
@topic = @message.root |
| 151 | 294 |
rescue ActiveRecord::RecordNotFound |
| 152 | 295 |
render_404 |
| 153 | 296 |
end |
| 154 | 297 | |
| 298 |
def find_topic |
|
| 299 |
@topic = Message.visible.includes(:author, :parent, {:board => :project}).find(params[:topic_id])
|
|
| 300 |
if @topic.parent |
|
| 301 |
render_404 |
|
| 302 |
return false |
|
| 303 |
end |
|
| 304 |
@board = @topic.board |
|
| 305 |
@project = @board.project |
|
| 306 |
rescue ActiveRecord::RecordNotFound |
|
| 307 |
render_404 |
|
| 308 |
end |
|
| 309 | ||
| 155 | 310 |
def find_board |
| 156 | 311 |
@board = Board.includes(:project).find(params[:board_id]) |
| 157 | 312 |
@project = @board.project |
| app/helpers/messages_helper.rb | ||
|---|---|---|
| 20 | 20 |
module MessagesHelper |
| 21 | 21 |
include Redmine::QuoteReply::Helper |
| 22 | 22 |
include ReactionsHelper |
| 23 | ||
| 24 |
def render_api_message(message, api) |
|
| 25 |
api.id message.id |
|
| 26 |
api.project(:id => message.project.id, :name => message.project.name) |
|
| 27 |
api.board(:id => message.board_id, :name => message.board.name) |
|
| 28 |
api.parent(:id => message.parent_id, :subject => message.parent.subject) unless message.parent.nil? |
|
| 29 |
api.root(:id => message.root.id, :subject => message.root.subject) |
|
| 30 |
api.author(:id => message.author_id, :name => message.author.name) unless message.author.nil? |
|
| 31 |
api.subject message.subject |
|
| 32 |
api.content message.content |
|
| 33 |
api.replies_count message.replies_count |
|
| 34 |
render_api_message_last_reply(message.last_reply, api) unless message.last_reply.nil? |
|
| 35 |
api.locked message.locked? |
|
| 36 |
api.sticky message.sticky? |
|
| 37 |
api.created_on message.created_on |
|
| 38 |
api.updated_on message.updated_on |
|
| 39 | ||
| 40 |
if include_in_api_response?('attachments')
|
|
| 41 |
api.array :attachments do |
|
| 42 |
message.attachments.each do |attachment| |
|
| 43 |
render_api_attachment(attachment, api) |
|
| 44 |
end |
|
| 45 |
end |
|
| 46 |
end |
|
| 47 |
end |
|
| 48 | ||
| 49 |
def render_api_message_last_reply(message, api) |
|
| 50 |
api.last_reply(:id => message.id) do |
|
| 51 |
api.author(:id => message.author_id, :name => message.author.name) unless message.author.nil? |
|
| 52 |
api.subject message.subject |
|
| 53 |
api.created_on message.created_on |
|
| 54 |
end |
|
| 55 |
end |
|
| 23 | 56 |
end |
| app/views/messages/replies.api.rsb | ||
|---|---|---|
| 1 |
api.array :replies, api_meta(:total_count => @message_count, :offset => @offset, :limit => @limit) do |
|
| 2 |
@messages.each do |message| |
|
| 3 |
api.reply do |
|
| 4 |
render_api_message(message, api) |
|
| 5 |
end |
|
| 6 |
end |
|
| 7 |
end |
|
| app/views/messages/show.api.rsb | ||
|---|---|---|
| 1 |
api.message do |
|
| 2 |
render_api_message(@message, api) |
|
| 3 |
end |
|
| app/views/messages/topics.api.rsb | ||
|---|---|---|
| 1 |
api.array :topics, api_meta(:total_count => @message_count, :offset => @offset, :limit => @limit) do |
|
| 2 |
@messages.each do |message| |
|
| 3 |
api.topic do |
|
| 4 |
render_api_message(message, api) |
|
| 5 |
end |
|
| 6 |
end |
|
| 7 |
end |
|
| config/routes.rb | ||
|---|---|---|
| 43 | 43 | |
| 44 | 44 |
match 'projects/:id/wiki/destroy', :to => 'wikis#destroy', :via => [:get, :post] |
| 45 | 45 | |
| 46 |
get 'boards/:board_id/topics.:format', :to => 'messages#index', :constraints => {:format => /xml|json/}
|
|
| 47 |
post 'boards/:board_id/topics.:format', :to => 'messages#create', :constraints => {:format => /xml|json/}
|
|
| 46 | 48 |
match 'boards/:board_id/topics/new', :to => 'messages#new', :via => [:get, :post], :as => 'new_board_message' |
| 47 | 49 |
get 'boards/:board_id/topics/:id', :to => 'messages#show', :as => 'board_message' |
| 48 | 50 |
match 'boards/:board_id/topics/quote/:id', :to => 'messages#quote', :via => [:get, :post] |
| ... | ... | |
| 52 | 54 |
post 'boards/:board_id/topics/:id/replies', :to => 'messages#reply' |
| 53 | 55 |
post 'boards/:board_id/topics/:id/edit', :to => 'messages#edit' |
| 54 | 56 |
post 'boards/:board_id/topics/:id/destroy', :to => 'messages#destroy' |
| 57 |
get 'messages/:topic_id/replies.:format', :to => 'messages#replies', :constraints => {:format => /xml|json/}
|
|
| 58 |
post 'messages/:topic_id/replies.:format', :to => 'messages#create_reply', :constraints => {:format => /xml|json/}
|
|
| 59 |
get 'messages/:id.:format', :to => 'messages#show', :as => 'message', :constraints => {:format => /xml|json/}
|
|
| 60 |
put 'messages/:id.:format', :to => 'messages#update', :constraints => {:format => /xml|json/}
|
|
| 61 |
patch 'messages/:id.:format', :to => 'messages#update', :constraints => {:format => /xml|json/}
|
|
| 62 |
delete 'messages/:id.:format', :to => 'messages#destroy', :constraints => {:format => /xml|json/}
|
|
| 55 | 63 |
get 'boards/:id.:format', :to => 'boards#show', :as => 'board', :constraints => {:format => /xml|json/}
|
| 56 | 64 |
put 'boards/:id.:format', :to => 'boards#update', :constraints => {:format => /xml|json/}
|
| 57 | 65 |
patch 'boards/:id.:format', :to => 'boards#update', :constraints => {:format => /xml|json/}
|
| lib/redmine/preparation.rb | ||
|---|---|---|
| 144 | 144 |
end |
| 145 | 145 | |
| 146 | 146 |
map.project_module :boards do |map| |
| 147 |
map.permission :view_messages, {:boards => [:index, :show], :messages => [:show]}, :read => true
|
|
| 148 |
map.permission :add_messages, {:messages => [:new, :reply, :quote], :attachments => :upload}
|
|
| 149 |
map.permission :edit_messages, {:messages => :edit, :attachments => :upload}, :require => :member
|
|
| 150 |
map.permission :edit_own_messages, {:messages => :edit, :attachments => :upload}, :require => :loggedin
|
|
| 147 |
map.permission :view_messages, {:boards => [:index, :show], :messages => [:index, :show, :replies]}, :read => true
|
|
| 148 |
map.permission :add_messages, {:messages => [:new, :create, :reply, :create_reply, :quote], :attachments => :upload}
|
|
| 149 |
map.permission :edit_messages, {:messages => [:edit, :update], :attachments => :upload}, :require => :member
|
|
| 150 |
map.permission :edit_own_messages, {:messages => [:edit, :update], :attachments => :upload}, :require => :loggedin
|
|
| 151 | 151 |
map.permission :delete_messages, {:messages => :destroy}, :require => :member
|
| 152 | 152 |
map.permission :delete_own_messages, {:messages => :destroy}, :require => :loggedin
|
| 153 | 153 |
map.permission :view_message_watchers, {}, :read => true
|
| test/integration/api_test/api_routing_test.rb | ||
|---|---|---|
| 99 | 99 |
should_route 'DELETE /issues/12/watchers/3' => 'watchers#destroy', :object_type => 'issue', :object_id => '12', :user_id => '3' |
| 100 | 100 |
end |
| 101 | 101 | |
| 102 |
def test_messages |
|
| 103 |
should_route 'GET /boards/1/topics' => 'messages#index', :board_id => '1' |
|
| 104 |
should_route 'POST /boards/1/topics' => 'messages#create', :board_id => '1' |
|
| 105 | ||
| 106 |
should_route 'GET /messages/1/replies' => 'messages#replies', :topic_id => '1' |
|
| 107 |
should_route 'POST /messages/1/replies' => 'messages#create_reply', :topic_id => '1' |
|
| 108 | ||
| 109 |
should_route 'GET /messages/2' => 'messages#show', :id => '2' |
|
| 110 |
should_route 'PUT /messages/2' => 'messages#update', :id => '2' |
|
| 111 |
should_route 'DELETE /messages/2' => 'messages#destroy', :id => '2' |
|
| 112 |
end |
|
| 113 | ||
| 102 | 114 |
def test_memberships |
| 103 | 115 |
should_route 'GET /projects/5234/memberships' => 'members#index', :project_id => '5234' |
| 104 | 116 |
should_route 'POST /projects/5234/memberships' => 'members#create', :project_id => '5234' |
| test/integration/api_test/messages_test.rb | ||
|---|---|---|
| 1 |
# frozen_string_literal: true |
|
| 2 | ||
| 3 |
# Redmine - project management software |
|
| 4 |
# Copyright (C) 2006- Jean-Philippe Lang |
|
| 5 |
# |
|
| 6 |
# This program is free software; you can redistribute it and/or |
|
| 7 |
# modify it under the terms of the GNU General Public License |
|
| 8 |
# as published by the Free Software Foundation; either version 2 |
|
| 9 |
# of the License, or (at your option) any later version. |
|
| 10 |
# |
|
| 11 |
# This program is distributed in the hope that it will be useful, |
|
| 12 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 13 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 14 |
# GNU General Public License for more details. |
|
| 15 |
# |
|
| 16 |
# You should have received a copy of the GNU General Public License |
|
| 17 |
# along with this program; if not, write to the Free Software |
|
| 18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
| 19 | ||
| 20 |
require_relative '../../test_helper' |
|
| 21 | ||
| 22 |
class Redmine::ApiTest::MessagesTest < Redmine::ApiTest::Base |
|
| 23 |
test "GET /boards/:board_id/topics.xml should return the board topics" do |
|
| 24 |
get '/boards/1/topics.xml', :headers => credentials('jsmith')
|
|
| 25 | ||
| 26 |
assert_response :success |
|
| 27 |
assert_equal 'application/xml', @response.media_type |
|
| 28 |
assert_select 'topics[type=array] topic id', :text => '4' |
|
| 29 |
assert_select 'topics[type=array] topic parent', 0 |
|
| 30 |
end |
|
| 31 | ||
| 32 |
test "POST /boards/:board_id/topics.xml should create a topic" do |
|
| 33 |
assert_difference 'Message.count' do |
|
| 34 |
post( |
|
| 35 |
'/boards/1/topics.xml', |
|
| 36 |
:params => {:message => {:subject => 'API topic', :content => 'API topic body'}},
|
|
| 37 |
:headers => credentials('jsmith'))
|
|
| 38 |
end |
|
| 39 | ||
| 40 |
message = Message.order(id: :desc).first |
|
| 41 |
assert_response :created |
|
| 42 |
assert_equal 'application/xml', @response.media_type |
|
| 43 |
assert_nil message.parent |
|
| 44 |
assert_equal 'API topic', message.subject |
|
| 45 |
assert_equal 'API topic body', message.content |
|
| 46 |
assert_equal Board.find(1), message.board |
|
| 47 |
assert_select 'message id', :text => message.id.to_s |
|
| 48 |
end |
|
| 49 | ||
| 50 |
test "POST /boards/:board_id/topics.xml should ignore board_id parameter" do |
|
| 51 |
assert_difference 'Message.count' do |
|
| 52 |
post( |
|
| 53 |
'/boards/1/topics.xml', |
|
| 54 |
:params => {
|
|
| 55 |
:message => {
|
|
| 56 |
:subject => 'API topic', |
|
| 57 |
:content => 'API topic body', |
|
| 58 |
:board_id => 2 |
|
| 59 |
} |
|
| 60 |
}, |
|
| 61 |
:headers => credentials('jsmith'))
|
|
| 62 |
end |
|
| 63 | ||
| 64 |
assert_response :created |
|
| 65 |
assert_equal Board.find(1), Message.order(id: :desc).first.board |
|
| 66 |
end |
|
| 67 | ||
| 68 |
test "POST /boards/:board_id/topics.xml with attachment should create a topic with attachment" do |
|
| 69 |
token = xml_upload('File content', credentials('jsmith'))
|
|
| 70 |
attachment = Attachment.find_by_token(token) |
|
| 71 | ||
| 72 |
assert_difference 'Message.count' do |
|
| 73 |
post( |
|
| 74 |
'/boards/1/topics.xml', |
|
| 75 |
:params => {
|
|
| 76 |
:message => {
|
|
| 77 |
:subject => 'API topic with attachment', |
|
| 78 |
:content => 'API topic body' |
|
| 79 |
}, |
|
| 80 |
:attachments => [ |
|
| 81 |
{
|
|
| 82 |
:token => token, |
|
| 83 |
:filename => 'message.txt', |
|
| 84 |
:content_type => 'text/plain' |
|
| 85 |
} |
|
| 86 |
] |
|
| 87 |
}, |
|
| 88 |
:headers => credentials('jsmith'))
|
|
| 89 |
end |
|
| 90 | ||
| 91 |
message = Message.order(id: :desc).first |
|
| 92 |
assert_response :created |
|
| 93 |
assert_equal attachment, message.attachments.first |
|
| 94 | ||
| 95 |
attachment.reload |
|
| 96 |
assert_equal 'message.txt', attachment.filename |
|
| 97 |
assert_equal 'text/plain', attachment.content_type |
|
| 98 |
assert_equal 2, attachment.author_id |
|
| 99 |
end |
|
| 100 | ||
| 101 |
test "POST /boards/:board_id/topics.xml with invalid parameters should return errors" do |
|
| 102 |
assert_no_difference 'Message.count' do |
|
| 103 |
post( |
|
| 104 |
'/boards/1/topics.xml', |
|
| 105 |
:params => {:message => {:subject => '', :content => 'API topic body'}},
|
|
| 106 |
:headers => credentials('jsmith'))
|
|
| 107 |
end |
|
| 108 | ||
| 109 |
assert_response :unprocessable_content |
|
| 110 |
assert_equal 'application/xml', @response.media_type |
|
| 111 |
assert_select 'errors error', :text => 'Subject cannot be blank' |
|
| 112 |
end |
|
| 113 | ||
| 114 |
test "GET /messages/:topic_id/replies.xml should return replies" do |
|
| 115 |
get '/messages/1/replies.xml', :headers => credentials('jsmith')
|
|
| 116 | ||
| 117 |
assert_response :success |
|
| 118 |
assert_equal 'application/xml', @response.media_type |
|
| 119 |
assert_select 'replies[type=array] reply id', :text => '2' |
|
| 120 |
assert_select 'replies[type=array] reply parent[id="1"][subject="First post"]' |
|
| 121 |
end |
|
| 122 | ||
| 123 |
test "POST /messages/:topic_id/replies.xml should create a reply" do |
|
| 124 |
assert_difference 'Message.count' do |
|
| 125 |
post( |
|
| 126 |
'/messages/1/replies.xml', |
|
| 127 |
:params => {:reply => {:content => 'API reply body'}},
|
|
| 128 |
:headers => credentials('jsmith'))
|
|
| 129 |
end |
|
| 130 | ||
| 131 |
reply = Message.order(id: :desc).first |
|
| 132 |
assert_response :created |
|
| 133 |
assert_equal 'application/xml', @response.media_type |
|
| 134 |
assert_equal Message.find(1), reply.parent |
|
| 135 |
assert_equal 'RE: First post', reply.subject |
|
| 136 |
assert_equal 'API reply body', reply.content |
|
| 137 |
assert_select 'message id', :text => reply.id.to_s |
|
| 138 |
end |
|
| 139 | ||
| 140 |
test "POST /messages/:topic_id/replies.xml should ignore board_id parameter" do |
|
| 141 |
assert_difference 'Message.count' do |
|
| 142 |
post( |
|
| 143 |
'/messages/1/replies.xml', |
|
| 144 |
:params => {:reply => {:content => 'API reply body', :board_id => 2}},
|
|
| 145 |
:headers => credentials('jsmith'))
|
|
| 146 |
end |
|
| 147 | ||
| 148 |
reply = Message.order(id: :desc).first |
|
| 149 |
assert_response :created |
|
| 150 |
assert_equal Message.find(1), reply.parent |
|
| 151 |
assert_equal Board.find(1), reply.board |
|
| 152 |
end |
|
| 153 | ||
| 154 |
test "POST /messages/:topic_id/replies.xml to locked topic should return errors" do |
|
| 155 |
Message.find(1).update!(:locked => true) |
|
| 156 | ||
| 157 |
assert_no_difference 'Message.count' do |
|
| 158 |
post( |
|
| 159 |
'/messages/1/replies.xml', |
|
| 160 |
:params => {:reply => {:content => 'API reply body'}},
|
|
| 161 |
:headers => credentials('jsmith'))
|
|
| 162 |
end |
|
| 163 | ||
| 164 |
assert_response :unprocessable_content |
|
| 165 |
assert_equal 'application/xml', @response.media_type |
|
| 166 |
assert_select 'errors error', :text => 'Topic is locked' |
|
| 167 |
end |
|
| 168 | ||
| 169 |
test "POST /messages/:topic_id/replies without API format should not create a reply" do |
|
| 170 |
assert_no_difference 'Message.count' do |
|
| 171 |
post( |
|
| 172 |
'/messages/1/replies', |
|
| 173 |
:params => {:reply => {:content => 'HTML reply body'}},
|
|
| 174 |
:headers => credentials('jsmith'))
|
|
| 175 |
end |
|
| 176 | ||
| 177 |
assert_response :not_found |
|
| 178 |
end |
|
| 179 | ||
| 180 |
test "GET /messages/:id.xml should return a topic by message id" do |
|
| 181 |
get '/messages/1.xml', :headers => credentials('jsmith')
|
|
| 182 | ||
| 183 |
assert_response :success |
|
| 184 |
assert_equal 'application/xml', @response.media_type |
|
| 185 |
assert_select 'message' do |
|
| 186 |
assert_select 'id', :text => '1' |
|
| 187 |
assert_select 'project[id="1"][name="eCookbook"]' |
|
| 188 |
assert_select 'board[id="1"][name="Help"]' |
|
| 189 |
assert_select 'root[id="1"][subject="First post"]' |
|
| 190 |
assert_select 'subject', :text => 'First post' |
|
| 191 |
assert_select 'replies_count', :text => '2' |
|
| 192 |
end |
|
| 193 |
end |
|
| 194 | ||
| 195 |
test "GET /messages/:id.xml should return a reply by message id" do |
|
| 196 |
get '/messages/2.xml', :headers => credentials('jsmith')
|
|
| 197 | ||
| 198 |
assert_response :success |
|
| 199 |
assert_equal 'application/xml', @response.media_type |
|
| 200 |
assert_select 'message' do |
|
| 201 |
assert_select 'id', :text => '2' |
|
| 202 |
assert_select 'parent[id="1"][subject="First post"]' |
|
| 203 |
assert_select 'root[id="1"][subject="First post"]' |
|
| 204 |
assert_select 'subject', :text => 'First reply' |
|
| 205 |
end |
|
| 206 |
end |
|
| 207 | ||
| 208 |
test "GET /messages/:id.xml without permission should return 404" do |
|
| 209 |
get '/messages/7.xml' |
|
| 210 | ||
| 211 |
assert_response :not_found |
|
| 212 |
end |
|
| 213 | ||
| 214 |
test "PUT /messages/:id.xml should update a message" do |
|
| 215 |
put( |
|
| 216 |
'/messages/1.xml', |
|
| 217 |
:params => {:message => {:subject => 'Updated topic', :content => 'Updated topic body'}},
|
|
| 218 |
:headers => credentials('jsmith'))
|
|
| 219 | ||
| 220 |
assert_response :no_content |
|
| 221 |
assert_equal '', @response.body |
|
| 222 |
assert_equal 'Updated topic', Message.find(1).subject |
|
| 223 |
assert_equal 'Updated topic body', Message.find(1).content |
|
| 224 |
end |
|
| 225 | ||
| 226 |
test "PUT /messages/:id.xml should allow moving a message to a board in the same project" do |
|
| 227 |
put( |
|
| 228 |
'/messages/1.xml', |
|
| 229 |
:params => {:message => {:board_id => 2}},
|
|
| 230 |
:headers => credentials('jsmith'))
|
|
| 231 | ||
| 232 |
assert_response :no_content |
|
| 233 |
assert_equal Board.find(2), Message.find(1).board |
|
| 234 |
end |
|
| 235 | ||
| 236 |
test "PUT /messages/:id.xml should not move a message to a board in another project" do |
|
| 237 |
put( |
|
| 238 |
'/messages/1.xml', |
|
| 239 |
:params => {:message => {:board_id => 3}},
|
|
| 240 |
:headers => credentials('jsmith'))
|
|
| 241 | ||
| 242 |
assert_response :unprocessable_content |
|
| 243 |
assert_equal Board.find(1), Message.find(1).board |
|
| 244 |
end |
|
| 245 | ||
| 246 |
test "DELETE /messages/:id.xml should destroy a message" do |
|
| 247 |
assert_difference 'Message.count', -1 do |
|
| 248 |
delete '/messages/2.xml', :headers => credentials('jsmith')
|
|
| 249 |
end |
|
| 250 | ||
| 251 |
assert_response :no_content |
|
| 252 |
assert_equal '', @response.body |
|
| 253 |
assert_nil Message.find_by_id(2) |
|
| 254 |
end |
|
| 255 | ||
| 256 |
test "DELETE /messages/:id without API format should not destroy a message" do |
|
| 257 |
assert_no_difference 'Message.count' do |
|
| 258 |
delete '/messages/2', :headers => credentials('jsmith')
|
|
| 259 |
end |
|
| 260 | ||
| 261 |
assert_response :not_found |
|
| 262 |
assert Message.exists?(2) |
|
| 263 |
end |
|
| 264 |
end |
|
- « Previous
- 1
- 2
- Next »