##// END OF EJS Templates
Add/remove issue watchers via the REST API (#6727)....
Jean-Philippe Lang -
r11060:a0158eff9643
parent child
Show More
@@ -1,95 +1,101
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class WatchersController < ApplicationController
18 class WatchersController < ApplicationController
19 before_filter :find_project
19 before_filter :find_project
20 before_filter :require_login, :check_project_privacy, :only => [:watch, :unwatch]
20 before_filter :require_login, :check_project_privacy, :only => [:watch, :unwatch]
21 before_filter :authorize, :only => [:new, :destroy]
21 before_filter :authorize, :only => [:new, :destroy]
22 accept_api_auth :create, :destroy
22
23
23 def watch
24 def watch
24 if @watched.respond_to?(:visible?) && !@watched.visible?(User.current)
25 if @watched.respond_to?(:visible?) && !@watched.visible?(User.current)
25 render_403
26 render_403
26 else
27 else
27 set_watcher(User.current, true)
28 set_watcher(User.current, true)
28 end
29 end
29 end
30 end
30
31
31 def unwatch
32 def unwatch
32 set_watcher(User.current, false)
33 set_watcher(User.current, false)
33 end
34 end
34
35
35 def new
36 def new
36 end
37 end
37
38
38 def create
39 def create
39 if params[:watcher].is_a?(Hash) && request.post?
40 user_ids = []
40 user_ids = params[:watcher][:user_ids] || [params[:watcher][:user_id]]
41 if params[:watcher].is_a?(Hash)
41 user_ids.each do |user_id|
42 user_ids << (params[:watcher][:user_ids] || params[:watcher][:user_id])
42 Watcher.create(:watchable => @watched, :user_id => user_id)
43 else
43 end
44 user_ids << params[:user_id]
45 end
46 user_ids.flatten.compact.uniq.each do |user_id|
47 Watcher.create(:watchable => @watched, :user_id => user_id)
44 end
48 end
45 respond_to do |format|
49 respond_to do |format|
46 format.html { redirect_to_referer_or {render :text => 'Watcher added.', :layout => true}}
50 format.html { redirect_to_referer_or {render :text => 'Watcher added.', :layout => true}}
47 format.js
51 format.js
52 format.api { render_api_ok }
48 end
53 end
49 end
54 end
50
55
51 def append
56 def append
52 if params[:watcher].is_a?(Hash)
57 if params[:watcher].is_a?(Hash)
53 user_ids = params[:watcher][:user_ids] || [params[:watcher][:user_id]]
58 user_ids = params[:watcher][:user_ids] || [params[:watcher][:user_id]]
54 @users = User.active.find_all_by_id(user_ids)
59 @users = User.active.find_all_by_id(user_ids)
55 end
60 end
56 end
61 end
57
62
58 def destroy
63 def destroy
59 @watched.set_watcher(User.find(params[:user_id]), false) if request.post?
64 @watched.set_watcher(User.find(params[:user_id]), false)
60 respond_to do |format|
65 respond_to do |format|
61 format.html { redirect_to :back }
66 format.html { redirect_to :back }
62 format.js
67 format.js
68 format.api { render_api_ok }
63 end
69 end
64 end
70 end
65
71
66 def autocomplete_for_user
72 def autocomplete_for_user
67 @users = User.active.sorted.like(params[:q]).limit(100).all
73 @users = User.active.sorted.like(params[:q]).limit(100).all
68 if @watched
74 if @watched
69 @users -= @watched.watcher_users
75 @users -= @watched.watcher_users
70 end
76 end
71 render :layout => false
77 render :layout => false
72 end
78 end
73
79
74 private
80 private
75 def find_project
81 def find_project
76 if params[:object_type] && params[:object_id]
82 if params[:object_type] && params[:object_id]
77 klass = Object.const_get(params[:object_type].camelcase)
83 klass = Object.const_get(params[:object_type].camelcase)
78 return false unless klass.respond_to?('watched_by')
84 return false unless klass.respond_to?('watched_by')
79 @watched = klass.find(params[:object_id])
85 @watched = klass.find(params[:object_id])
80 @project = @watched.project
86 @project = @watched.project
81 elsif params[:project_id]
87 elsif params[:project_id]
82 @project = Project.visible.find_by_param(params[:project_id])
88 @project = Project.visible.find_by_param(params[:project_id])
83 end
89 end
84 rescue
90 rescue
85 render_404
91 render_404
86 end
92 end
87
93
88 def set_watcher(user, watching)
94 def set_watcher(user, watching)
89 @watched.set_watcher(user, watching)
95 @watched.set_watcher(user, watching)
90 respond_to do |format|
96 respond_to do |format|
91 format.html { redirect_to_referer_or {render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true}}
97 format.html { redirect_to_referer_or {render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true}}
92 format.js { render :partial => 'set_watcher', :locals => {:user => user, :watched => @watched} }
98 format.js { render :partial => 'set_watcher', :locals => {:user => user, :watched => @watched} }
93 end
99 end
94 end
100 end
95 end
101 end
@@ -1,67 +1,73
1 api.issue do
1 api.issue do
2 api.id @issue.id
2 api.id @issue.id
3 api.project(:id => @issue.project_id, :name => @issue.project.name) unless @issue.project.nil?
3 api.project(:id => @issue.project_id, :name => @issue.project.name) unless @issue.project.nil?
4 api.tracker(:id => @issue.tracker_id, :name => @issue.tracker.name) unless @issue.tracker.nil?
4 api.tracker(:id => @issue.tracker_id, :name => @issue.tracker.name) unless @issue.tracker.nil?
5 api.status(:id => @issue.status_id, :name => @issue.status.name) unless @issue.status.nil?
5 api.status(:id => @issue.status_id, :name => @issue.status.name) unless @issue.status.nil?
6 api.priority(:id => @issue.priority_id, :name => @issue.priority.name) unless @issue.priority.nil?
6 api.priority(:id => @issue.priority_id, :name => @issue.priority.name) unless @issue.priority.nil?
7 api.author(:id => @issue.author_id, :name => @issue.author.name) unless @issue.author.nil?
7 api.author(:id => @issue.author_id, :name => @issue.author.name) unless @issue.author.nil?
8 api.assigned_to(:id => @issue.assigned_to_id, :name => @issue.assigned_to.name) unless @issue.assigned_to.nil?
8 api.assigned_to(:id => @issue.assigned_to_id, :name => @issue.assigned_to.name) unless @issue.assigned_to.nil?
9 api.category(:id => @issue.category_id, :name => @issue.category.name) unless @issue.category.nil?
9 api.category(:id => @issue.category_id, :name => @issue.category.name) unless @issue.category.nil?
10 api.fixed_version(:id => @issue.fixed_version_id, :name => @issue.fixed_version.name) unless @issue.fixed_version.nil?
10 api.fixed_version(:id => @issue.fixed_version_id, :name => @issue.fixed_version.name) unless @issue.fixed_version.nil?
11 api.parent(:id => @issue.parent_id) unless @issue.parent.nil?
11 api.parent(:id => @issue.parent_id) unless @issue.parent.nil?
12
12
13 api.subject @issue.subject
13 api.subject @issue.subject
14 api.description @issue.description
14 api.description @issue.description
15 api.start_date @issue.start_date
15 api.start_date @issue.start_date
16 api.due_date @issue.due_date
16 api.due_date @issue.due_date
17 api.done_ratio @issue.done_ratio
17 api.done_ratio @issue.done_ratio
18 api.estimated_hours @issue.estimated_hours
18 api.estimated_hours @issue.estimated_hours
19 api.spent_hours(@issue.spent_hours) if User.current.allowed_to?(:view_time_entries, @project)
19 api.spent_hours(@issue.spent_hours) if User.current.allowed_to?(:view_time_entries, @project)
20
20
21 render_api_custom_values @issue.custom_field_values, api
21 render_api_custom_values @issue.custom_field_values, api
22
22
23 api.created_on @issue.created_on
23 api.created_on @issue.created_on
24 api.updated_on @issue.updated_on
24 api.updated_on @issue.updated_on
25
25
26 render_api_issue_children(@issue, api) if include_in_api_response?('children')
26 render_api_issue_children(@issue, api) if include_in_api_response?('children')
27
27
28 api.array :attachments do
28 api.array :attachments do
29 @issue.attachments.each do |attachment|
29 @issue.attachments.each do |attachment|
30 render_api_attachment(attachment, api)
30 render_api_attachment(attachment, api)
31 end
31 end
32 end if include_in_api_response?('attachments')
32 end if include_in_api_response?('attachments')
33
33
34 api.array :relations do
34 api.array :relations do
35 @relations.each do |relation|
35 @relations.each do |relation|
36 api.relation(:id => relation.id, :issue_id => relation.issue_from_id, :issue_to_id => relation.issue_to_id, :relation_type => relation.relation_type, :delay => relation.delay)
36 api.relation(:id => relation.id, :issue_id => relation.issue_from_id, :issue_to_id => relation.issue_to_id, :relation_type => relation.relation_type, :delay => relation.delay)
37 end
37 end
38 end if include_in_api_response?('relations') && @relations.present?
38 end if include_in_api_response?('relations') && @relations.present?
39
39
40 api.array :changesets do
40 api.array :changesets do
41 @issue.changesets.each do |changeset|
41 @issue.changesets.each do |changeset|
42 api.changeset :revision => changeset.revision do
42 api.changeset :revision => changeset.revision do
43 api.user(:id => changeset.user_id, :name => changeset.user.name) unless changeset.user.nil?
43 api.user(:id => changeset.user_id, :name => changeset.user.name) unless changeset.user.nil?
44 api.comments changeset.comments
44 api.comments changeset.comments
45 api.committed_on changeset.committed_on
45 api.committed_on changeset.committed_on
46 end
46 end
47 end
47 end
48 end if include_in_api_response?('changesets') && User.current.allowed_to?(:view_changesets, @project)
48 end if include_in_api_response?('changesets') && User.current.allowed_to?(:view_changesets, @project)
49
49
50 api.array :journals do
50 api.array :journals do
51 @journals.each do |journal|
51 @journals.each do |journal|
52 api.journal :id => journal.id do
52 api.journal :id => journal.id do
53 api.user(:id => journal.user_id, :name => journal.user.name) unless journal.user.nil?
53 api.user(:id => journal.user_id, :name => journal.user.name) unless journal.user.nil?
54 api.notes journal.notes
54 api.notes journal.notes
55 api.created_on journal.created_on
55 api.created_on journal.created_on
56 api.array :details do
56 api.array :details do
57 journal.details.each do |detail|
57 journal.details.each do |detail|
58 api.detail :property => detail.property, :name => detail.prop_key do
58 api.detail :property => detail.property, :name => detail.prop_key do
59 api.old_value detail.old_value
59 api.old_value detail.old_value
60 api.new_value detail.value
60 api.new_value detail.value
61 end
61 end
62 end
62 end
63 end
63 end
64 end
64 end
65 end
65 end
66 end if include_in_api_response?('journals')
66 end if include_in_api_response?('journals')
67
68 api.array :watchers do
69 @issue.watcher_users.each do |user|
70 api.user :id => user.id, :name => user.name
71 end
72 end if include_in_api_response?('watchers') && User.current.allowed_to?(:view_issue_watchers, @issue.project)
67 end
73 end
@@ -1,344 +1,347
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 RedmineApp::Application.routes.draw do
18 RedmineApp::Application.routes.draw do
19 root :to => 'welcome#index', :as => 'home'
19 root :to => 'welcome#index', :as => 'home'
20
20
21 match 'login', :to => 'account#login', :as => 'signin', :via => [:get, :post]
21 match 'login', :to => 'account#login', :as => 'signin', :via => [:get, :post]
22 match 'logout', :to => 'account#logout', :as => 'signout', :via => [:get, :post]
22 match 'logout', :to => 'account#logout', :as => 'signout', :via => [:get, :post]
23 match 'account/register', :to => 'account#register', :via => [:get, :post], :as => 'register'
23 match 'account/register', :to => 'account#register', :via => [:get, :post], :as => 'register'
24 match 'account/lost_password', :to => 'account#lost_password', :via => [:get, :post], :as => 'lost_password'
24 match 'account/lost_password', :to => 'account#lost_password', :via => [:get, :post], :as => 'lost_password'
25 match 'account/activate', :to => 'account#activate', :via => :get
25 match 'account/activate', :to => 'account#activate', :via => :get
26
26
27 match '/news/preview', :controller => 'previews', :action => 'news', :as => 'preview_news', :via => [:get, :post, :put]
27 match '/news/preview', :controller => 'previews', :action => 'news', :as => 'preview_news', :via => [:get, :post, :put]
28 match '/issues/preview/new/:project_id', :to => 'previews#issue', :as => 'preview_new_issue', :via => [:get, :post, :put]
28 match '/issues/preview/new/:project_id', :to => 'previews#issue', :as => 'preview_new_issue', :via => [:get, :post, :put]
29 match '/issues/preview/edit/:id', :to => 'previews#issue', :as => 'preview_edit_issue', :via => [:get, :post, :put]
29 match '/issues/preview/edit/:id', :to => 'previews#issue', :as => 'preview_edit_issue', :via => [:get, :post, :put]
30 match '/issues/preview', :to => 'previews#issue', :as => 'preview_issue', :via => [:get, :post, :put]
30 match '/issues/preview', :to => 'previews#issue', :as => 'preview_issue', :via => [:get, :post, :put]
31
31
32 match 'projects/:id/wiki', :to => 'wikis#edit', :via => :post
32 match 'projects/:id/wiki', :to => 'wikis#edit', :via => :post
33 match 'projects/:id/wiki/destroy', :to => 'wikis#destroy', :via => [:get, :post]
33 match 'projects/:id/wiki/destroy', :to => 'wikis#destroy', :via => [:get, :post]
34
34
35 match 'boards/:board_id/topics/new', :to => 'messages#new', :via => [:get, :post], :as => 'new_board_message'
35 match 'boards/:board_id/topics/new', :to => 'messages#new', :via => [:get, :post], :as => 'new_board_message'
36 get 'boards/:board_id/topics/:id', :to => 'messages#show', :as => 'board_message'
36 get 'boards/:board_id/topics/:id', :to => 'messages#show', :as => 'board_message'
37 match 'boards/:board_id/topics/quote/:id', :to => 'messages#quote', :via => [:get, :post]
37 match 'boards/:board_id/topics/quote/:id', :to => 'messages#quote', :via => [:get, :post]
38 get 'boards/:board_id/topics/:id/edit', :to => 'messages#edit'
38 get 'boards/:board_id/topics/:id/edit', :to => 'messages#edit'
39
39
40 post 'boards/:board_id/topics/preview', :to => 'messages#preview', :as => 'preview_board_message'
40 post 'boards/:board_id/topics/preview', :to => 'messages#preview', :as => 'preview_board_message'
41 post 'boards/:board_id/topics/:id/replies', :to => 'messages#reply'
41 post 'boards/:board_id/topics/:id/replies', :to => 'messages#reply'
42 post 'boards/:board_id/topics/:id/edit', :to => 'messages#edit'
42 post 'boards/:board_id/topics/:id/edit', :to => 'messages#edit'
43 post 'boards/:board_id/topics/:id/destroy', :to => 'messages#destroy'
43 post 'boards/:board_id/topics/:id/destroy', :to => 'messages#destroy'
44
44
45 # Misc issue routes. TODO: move into resources
45 # Misc issue routes. TODO: move into resources
46 match '/issues/auto_complete', :to => 'auto_completes#issues', :via => :get, :as => 'auto_complete_issues'
46 match '/issues/auto_complete', :to => 'auto_completes#issues', :via => :get, :as => 'auto_complete_issues'
47 match '/issues/context_menu', :to => 'context_menus#issues', :as => 'issues_context_menu', :via => [:get, :post]
47 match '/issues/context_menu', :to => 'context_menus#issues', :as => 'issues_context_menu', :via => [:get, :post]
48 match '/issues/changes', :to => 'journals#index', :as => 'issue_changes', :via => :get
48 match '/issues/changes', :to => 'journals#index', :as => 'issue_changes', :via => :get
49 match '/issues/:id/quoted', :to => 'journals#new', :id => /\d+/, :via => :post, :as => 'quoted_issue'
49 match '/issues/:id/quoted', :to => 'journals#new', :id => /\d+/, :via => :post, :as => 'quoted_issue'
50
50
51 match '/journals/diff/:id', :to => 'journals#diff', :id => /\d+/, :via => :get
51 match '/journals/diff/:id', :to => 'journals#diff', :id => /\d+/, :via => :get
52 match '/journals/edit/:id', :to => 'journals#edit', :id => /\d+/, :via => [:get, :post]
52 match '/journals/edit/:id', :to => 'journals#edit', :id => /\d+/, :via => [:get, :post]
53
53
54 get '/projects/:project_id/issues/gantt', :to => 'gantts#show', :as => 'project_gantt'
54 get '/projects/:project_id/issues/gantt', :to => 'gantts#show', :as => 'project_gantt'
55 get '/issues/gantt', :to => 'gantts#show'
55 get '/issues/gantt', :to => 'gantts#show'
56
56
57 get '/projects/:project_id/issues/calendar', :to => 'calendars#show', :as => 'project_calendar'
57 get '/projects/:project_id/issues/calendar', :to => 'calendars#show', :as => 'project_calendar'
58 get '/issues/calendar', :to => 'calendars#show'
58 get '/issues/calendar', :to => 'calendars#show'
59
59
60 get 'projects/:id/issues/report', :to => 'reports#issue_report', :as => 'project_issues_report'
60 get 'projects/:id/issues/report', :to => 'reports#issue_report', :as => 'project_issues_report'
61 get 'projects/:id/issues/report/:detail', :to => 'reports#issue_report_details', :as => 'project_issues_report_details'
61 get 'projects/:id/issues/report/:detail', :to => 'reports#issue_report_details', :as => 'project_issues_report_details'
62
62
63 match 'my/account', :controller => 'my', :action => 'account', :via => [:get, :post]
63 match 'my/account', :controller => 'my', :action => 'account', :via => [:get, :post]
64 match 'my/account/destroy', :controller => 'my', :action => 'destroy', :via => [:get, :post]
64 match 'my/account/destroy', :controller => 'my', :action => 'destroy', :via => [:get, :post]
65 match 'my/page', :controller => 'my', :action => 'page', :via => :get
65 match 'my/page', :controller => 'my', :action => 'page', :via => :get
66 match 'my', :controller => 'my', :action => 'index', :via => :get # Redirects to my/page
66 match 'my', :controller => 'my', :action => 'index', :via => :get # Redirects to my/page
67 match 'my/reset_rss_key', :controller => 'my', :action => 'reset_rss_key', :via => :post
67 match 'my/reset_rss_key', :controller => 'my', :action => 'reset_rss_key', :via => :post
68 match 'my/reset_api_key', :controller => 'my', :action => 'reset_api_key', :via => :post
68 match 'my/reset_api_key', :controller => 'my', :action => 'reset_api_key', :via => :post
69 match 'my/password', :controller => 'my', :action => 'password', :via => [:get, :post]
69 match 'my/password', :controller => 'my', :action => 'password', :via => [:get, :post]
70 match 'my/page_layout', :controller => 'my', :action => 'page_layout', :via => :get
70 match 'my/page_layout', :controller => 'my', :action => 'page_layout', :via => :get
71 match 'my/add_block', :controller => 'my', :action => 'add_block', :via => :post
71 match 'my/add_block', :controller => 'my', :action => 'add_block', :via => :post
72 match 'my/remove_block', :controller => 'my', :action => 'remove_block', :via => :post
72 match 'my/remove_block', :controller => 'my', :action => 'remove_block', :via => :post
73 match 'my/order_blocks', :controller => 'my', :action => 'order_blocks', :via => :post
73 match 'my/order_blocks', :controller => 'my', :action => 'order_blocks', :via => :post
74
74
75 resources :users
75 resources :users
76 match 'users/:id/memberships/:membership_id', :to => 'users#edit_membership', :via => :put, :as => 'user_membership'
76 match 'users/:id/memberships/:membership_id', :to => 'users#edit_membership', :via => :put, :as => 'user_membership'
77 match 'users/:id/memberships/:membership_id', :to => 'users#destroy_membership', :via => :delete
77 match 'users/:id/memberships/:membership_id', :to => 'users#destroy_membership', :via => :delete
78 match 'users/:id/memberships', :to => 'users#edit_membership', :via => :post, :as => 'user_memberships'
78 match 'users/:id/memberships', :to => 'users#edit_membership', :via => :post, :as => 'user_memberships'
79
79
80 match 'watchers/new', :controller=> 'watchers', :action => 'new', :via => :get
80 match 'watchers/new', :controller=> 'watchers', :action => 'new', :via => :get
81 match 'watchers', :controller=> 'watchers', :action => 'create', :via => :post
81 match 'watchers', :controller=> 'watchers', :action => 'create', :via => :post
82 match 'watchers/append', :controller=> 'watchers', :action => 'append', :via => :post
82 match 'watchers/append', :controller=> 'watchers', :action => 'append', :via => :post
83 match 'watchers/destroy', :controller=> 'watchers', :action => 'destroy', :via => :post
83 match 'watchers/destroy', :controller=> 'watchers', :action => 'destroy', :via => :post
84 match 'watchers/watch', :controller=> 'watchers', :action => 'watch', :via => :post
84 match 'watchers/watch', :controller=> 'watchers', :action => 'watch', :via => :post
85 match 'watchers/unwatch', :controller=> 'watchers', :action => 'unwatch', :via => :post
85 match 'watchers/unwatch', :controller=> 'watchers', :action => 'unwatch', :via => :post
86 match 'watchers/autocomplete_for_user', :controller=> 'watchers', :action => 'autocomplete_for_user', :via => :get
86 match 'watchers/autocomplete_for_user', :controller=> 'watchers', :action => 'autocomplete_for_user', :via => :get
87 # Specific routes for issue watchers API
88 post 'issues/:object_id/watchers', :to => 'watchers#create', :object_type => 'issue'
89 delete 'issues/:object_id/watchers/:user_id' => 'watchers#destroy', :object_type => 'issue'
87
90
88 resources :projects do
91 resources :projects do
89 member do
92 member do
90 get 'settings(/:tab)', :action => 'settings', :as => 'settings'
93 get 'settings(/:tab)', :action => 'settings', :as => 'settings'
91 post 'modules'
94 post 'modules'
92 post 'archive'
95 post 'archive'
93 post 'unarchive'
96 post 'unarchive'
94 post 'close'
97 post 'close'
95 post 'reopen'
98 post 'reopen'
96 match 'copy', :via => [:get, :post]
99 match 'copy', :via => [:get, :post]
97 end
100 end
98
101
99 resources :memberships, :shallow => true, :controller => 'members', :only => [:index, :show, :new, :create, :update, :destroy] do
102 resources :memberships, :shallow => true, :controller => 'members', :only => [:index, :show, :new, :create, :update, :destroy] do
100 collection do
103 collection do
101 get 'autocomplete'
104 get 'autocomplete'
102 end
105 end
103 end
106 end
104
107
105 resource :enumerations, :controller => 'project_enumerations', :only => [:update, :destroy]
108 resource :enumerations, :controller => 'project_enumerations', :only => [:update, :destroy]
106
109
107 get 'issues/:copy_from/copy', :to => 'issues#new', :as => 'copy_issue'
110 get 'issues/:copy_from/copy', :to => 'issues#new', :as => 'copy_issue'
108 resources :issues, :only => [:index, :new, :create] do
111 resources :issues, :only => [:index, :new, :create] do
109 resources :time_entries, :controller => 'timelog' do
112 resources :time_entries, :controller => 'timelog' do
110 collection do
113 collection do
111 get 'report'
114 get 'report'
112 end
115 end
113 end
116 end
114 end
117 end
115 # issue form update
118 # issue form update
116 match 'issues/new', :controller => 'issues', :action => 'new', :via => [:put, :post], :as => 'issue_form'
119 match 'issues/new', :controller => 'issues', :action => 'new', :via => [:put, :post], :as => 'issue_form'
117
120
118 resources :files, :only => [:index, :new, :create]
121 resources :files, :only => [:index, :new, :create]
119
122
120 resources :versions, :except => [:index, :show, :edit, :update, :destroy] do
123 resources :versions, :except => [:index, :show, :edit, :update, :destroy] do
121 collection do
124 collection do
122 put 'close_completed'
125 put 'close_completed'
123 end
126 end
124 end
127 end
125 get 'versions.:format', :to => 'versions#index'
128 get 'versions.:format', :to => 'versions#index'
126 get 'roadmap', :to => 'versions#index', :format => false
129 get 'roadmap', :to => 'versions#index', :format => false
127 get 'versions', :to => 'versions#index'
130 get 'versions', :to => 'versions#index'
128
131
129 resources :news, :except => [:show, :edit, :update, :destroy]
132 resources :news, :except => [:show, :edit, :update, :destroy]
130 resources :time_entries, :controller => 'timelog' do
133 resources :time_entries, :controller => 'timelog' do
131 get 'report', :on => :collection
134 get 'report', :on => :collection
132 end
135 end
133 resources :queries, :only => [:new, :create]
136 resources :queries, :only => [:new, :create]
134 resources :issue_categories, :shallow => true
137 resources :issue_categories, :shallow => true
135 resources :documents, :except => [:show, :edit, :update, :destroy]
138 resources :documents, :except => [:show, :edit, :update, :destroy]
136 resources :boards
139 resources :boards
137 resources :repositories, :shallow => true, :except => [:index, :show] do
140 resources :repositories, :shallow => true, :except => [:index, :show] do
138 member do
141 member do
139 match 'committers', :via => [:get, :post]
142 match 'committers', :via => [:get, :post]
140 end
143 end
141 end
144 end
142
145
143 match 'wiki/index', :controller => 'wiki', :action => 'index', :via => :get
146 match 'wiki/index', :controller => 'wiki', :action => 'index', :via => :get
144 resources :wiki, :except => [:index, :new, :create], :as => 'wiki_page' do
147 resources :wiki, :except => [:index, :new, :create], :as => 'wiki_page' do
145 member do
148 member do
146 get 'rename'
149 get 'rename'
147 post 'rename'
150 post 'rename'
148 get 'history'
151 get 'history'
149 get 'diff'
152 get 'diff'
150 match 'preview', :via => [:post, :put]
153 match 'preview', :via => [:post, :put]
151 post 'protect'
154 post 'protect'
152 post 'add_attachment'
155 post 'add_attachment'
153 end
156 end
154 collection do
157 collection do
155 get 'export'
158 get 'export'
156 get 'date_index'
159 get 'date_index'
157 end
160 end
158 end
161 end
159 match 'wiki', :controller => 'wiki', :action => 'show', :via => :get
162 match 'wiki', :controller => 'wiki', :action => 'show', :via => :get
160 get 'wiki/:id/:version', :to => 'wiki#show', :constraints => {:version => /\d+/}
163 get 'wiki/:id/:version', :to => 'wiki#show', :constraints => {:version => /\d+/}
161 delete 'wiki/:id/:version', :to => 'wiki#destroy_version'
164 delete 'wiki/:id/:version', :to => 'wiki#destroy_version'
162 get 'wiki/:id/:version/annotate', :to => 'wiki#annotate'
165 get 'wiki/:id/:version/annotate', :to => 'wiki#annotate'
163 get 'wiki/:id/:version/diff', :to => 'wiki#diff'
166 get 'wiki/:id/:version/diff', :to => 'wiki#diff'
164 end
167 end
165
168
166 resources :issues do
169 resources :issues do
167 collection do
170 collection do
168 match 'bulk_edit', :via => [:get, :post]
171 match 'bulk_edit', :via => [:get, :post]
169 post 'bulk_update'
172 post 'bulk_update'
170 end
173 end
171 resources :time_entries, :controller => 'timelog' do
174 resources :time_entries, :controller => 'timelog' do
172 collection do
175 collection do
173 get 'report'
176 get 'report'
174 end
177 end
175 end
178 end
176 resources :relations, :shallow => true, :controller => 'issue_relations', :only => [:index, :show, :create, :destroy]
179 resources :relations, :shallow => true, :controller => 'issue_relations', :only => [:index, :show, :create, :destroy]
177 end
180 end
178 match '/issues', :controller => 'issues', :action => 'destroy', :via => :delete
181 match '/issues', :controller => 'issues', :action => 'destroy', :via => :delete
179
182
180 resources :queries, :except => [:show]
183 resources :queries, :except => [:show]
181
184
182 resources :news, :only => [:index, :show, :edit, :update, :destroy]
185 resources :news, :only => [:index, :show, :edit, :update, :destroy]
183 match '/news/:id/comments', :to => 'comments#create', :via => :post
186 match '/news/:id/comments', :to => 'comments#create', :via => :post
184 match '/news/:id/comments/:comment_id', :to => 'comments#destroy', :via => :delete
187 match '/news/:id/comments/:comment_id', :to => 'comments#destroy', :via => :delete
185
188
186 resources :versions, :only => [:show, :edit, :update, :destroy] do
189 resources :versions, :only => [:show, :edit, :update, :destroy] do
187 post 'status_by', :on => :member
190 post 'status_by', :on => :member
188 end
191 end
189
192
190 resources :documents, :only => [:show, :edit, :update, :destroy] do
193 resources :documents, :only => [:show, :edit, :update, :destroy] do
191 post 'add_attachment', :on => :member
194 post 'add_attachment', :on => :member
192 end
195 end
193
196
194 match '/time_entries/context_menu', :to => 'context_menus#time_entries', :as => :time_entries_context_menu, :via => [:get, :post]
197 match '/time_entries/context_menu', :to => 'context_menus#time_entries', :as => :time_entries_context_menu, :via => [:get, :post]
195
198
196 resources :time_entries, :controller => 'timelog', :except => :destroy do
199 resources :time_entries, :controller => 'timelog', :except => :destroy do
197 collection do
200 collection do
198 get 'report'
201 get 'report'
199 get 'bulk_edit'
202 get 'bulk_edit'
200 post 'bulk_update'
203 post 'bulk_update'
201 end
204 end
202 end
205 end
203 match '/time_entries/:id', :to => 'timelog#destroy', :via => :delete, :id => /\d+/
206 match '/time_entries/:id', :to => 'timelog#destroy', :via => :delete, :id => /\d+/
204 # TODO: delete /time_entries for bulk deletion
207 # TODO: delete /time_entries for bulk deletion
205 match '/time_entries/destroy', :to => 'timelog#destroy', :via => :delete
208 match '/time_entries/destroy', :to => 'timelog#destroy', :via => :delete
206
209
207 get 'projects/:id/activity', :to => 'activities#index'
210 get 'projects/:id/activity', :to => 'activities#index'
208 get 'projects/:id/activity.:format', :to => 'activities#index'
211 get 'projects/:id/activity.:format', :to => 'activities#index'
209 get 'activity', :to => 'activities#index'
212 get 'activity', :to => 'activities#index'
210
213
211 # repositories routes
214 # repositories routes
212 get 'projects/:id/repository/:repository_id/statistics', :to => 'repositories#stats'
215 get 'projects/:id/repository/:repository_id/statistics', :to => 'repositories#stats'
213 get 'projects/:id/repository/:repository_id/graph', :to => 'repositories#graph'
216 get 'projects/:id/repository/:repository_id/graph', :to => 'repositories#graph'
214
217
215 get 'projects/:id/repository/:repository_id/changes(/*path(.:ext))',
218 get 'projects/:id/repository/:repository_id/changes(/*path(.:ext))',
216 :to => 'repositories#changes'
219 :to => 'repositories#changes'
217
220
218 get 'projects/:id/repository/:repository_id/revisions/:rev', :to => 'repositories#revision'
221 get 'projects/:id/repository/:repository_id/revisions/:rev', :to => 'repositories#revision'
219 get 'projects/:id/repository/:repository_id/revision', :to => 'repositories#revision'
222 get 'projects/:id/repository/:repository_id/revision', :to => 'repositories#revision'
220 post 'projects/:id/repository/:repository_id/revisions/:rev/issues', :to => 'repositories#add_related_issue'
223 post 'projects/:id/repository/:repository_id/revisions/:rev/issues', :to => 'repositories#add_related_issue'
221 delete 'projects/:id/repository/:repository_id/revisions/:rev/issues/:issue_id', :to => 'repositories#remove_related_issue'
224 delete 'projects/:id/repository/:repository_id/revisions/:rev/issues/:issue_id', :to => 'repositories#remove_related_issue'
222 get 'projects/:id/repository/:repository_id/revisions', :to => 'repositories#revisions'
225 get 'projects/:id/repository/:repository_id/revisions', :to => 'repositories#revisions'
223 get 'projects/:id/repository/:repository_id/revisions/:rev/:action(/*path(.:ext))',
226 get 'projects/:id/repository/:repository_id/revisions/:rev/:action(/*path(.:ext))',
224 :controller => 'repositories',
227 :controller => 'repositories',
225 :format => false,
228 :format => false,
226 :constraints => {
229 :constraints => {
227 :action => /(browse|show|entry|raw|annotate|diff)/,
230 :action => /(browse|show|entry|raw|annotate|diff)/,
228 :rev => /[a-z0-9\.\-_]+/
231 :rev => /[a-z0-9\.\-_]+/
229 }
232 }
230
233
231 get 'projects/:id/repository/statistics', :to => 'repositories#stats'
234 get 'projects/:id/repository/statistics', :to => 'repositories#stats'
232 get 'projects/:id/repository/graph', :to => 'repositories#graph'
235 get 'projects/:id/repository/graph', :to => 'repositories#graph'
233
236
234 get 'projects/:id/repository/changes(/*path(.:ext))',
237 get 'projects/:id/repository/changes(/*path(.:ext))',
235 :to => 'repositories#changes'
238 :to => 'repositories#changes'
236
239
237 get 'projects/:id/repository/revisions', :to => 'repositories#revisions'
240 get 'projects/:id/repository/revisions', :to => 'repositories#revisions'
238 get 'projects/:id/repository/revisions/:rev', :to => 'repositories#revision'
241 get 'projects/:id/repository/revisions/:rev', :to => 'repositories#revision'
239 get 'projects/:id/repository/revision', :to => 'repositories#revision'
242 get 'projects/:id/repository/revision', :to => 'repositories#revision'
240 post 'projects/:id/repository/revisions/:rev/issues', :to => 'repositories#add_related_issue'
243 post 'projects/:id/repository/revisions/:rev/issues', :to => 'repositories#add_related_issue'
241 delete 'projects/:id/repository/revisions/:rev/issues/:issue_id', :to => 'repositories#remove_related_issue'
244 delete 'projects/:id/repository/revisions/:rev/issues/:issue_id', :to => 'repositories#remove_related_issue'
242 get 'projects/:id/repository/revisions/:rev/:action(/*path(.:ext))',
245 get 'projects/:id/repository/revisions/:rev/:action(/*path(.:ext))',
243 :controller => 'repositories',
246 :controller => 'repositories',
244 :format => false,
247 :format => false,
245 :constraints => {
248 :constraints => {
246 :action => /(browse|show|entry|raw|annotate|diff)/,
249 :action => /(browse|show|entry|raw|annotate|diff)/,
247 :rev => /[a-z0-9\.\-_]+/
250 :rev => /[a-z0-9\.\-_]+/
248 }
251 }
249 get 'projects/:id/repository/:repository_id/:action(/*path(.:ext))',
252 get 'projects/:id/repository/:repository_id/:action(/*path(.:ext))',
250 :controller => 'repositories',
253 :controller => 'repositories',
251 :action => /(browse|show|entry|raw|changes|annotate|diff)/
254 :action => /(browse|show|entry|raw|changes|annotate|diff)/
252 get 'projects/:id/repository/:action(/*path(.:ext))',
255 get 'projects/:id/repository/:action(/*path(.:ext))',
253 :controller => 'repositories',
256 :controller => 'repositories',
254 :action => /(browse|show|entry|raw|changes|annotate|diff)/
257 :action => /(browse|show|entry|raw|changes|annotate|diff)/
255
258
256 get 'projects/:id/repository/:repository_id', :to => 'repositories#show', :path => nil
259 get 'projects/:id/repository/:repository_id', :to => 'repositories#show', :path => nil
257 get 'projects/:id/repository', :to => 'repositories#show', :path => nil
260 get 'projects/:id/repository', :to => 'repositories#show', :path => nil
258
261
259 # additional routes for having the file name at the end of url
262 # additional routes for having the file name at the end of url
260 get 'attachments/:id/:filename', :to => 'attachments#show', :id => /\d+/, :filename => /.*/, :as => 'named_attachment'
263 get 'attachments/:id/:filename', :to => 'attachments#show', :id => /\d+/, :filename => /.*/, :as => 'named_attachment'
261 get 'attachments/download/:id/:filename', :to => 'attachments#download', :id => /\d+/, :filename => /.*/, :as => 'download_named_attachment'
264 get 'attachments/download/:id/:filename', :to => 'attachments#download', :id => /\d+/, :filename => /.*/, :as => 'download_named_attachment'
262 get 'attachments/download/:id', :to => 'attachments#download', :id => /\d+/
265 get 'attachments/download/:id', :to => 'attachments#download', :id => /\d+/
263 get 'attachments/thumbnail/:id(/:size)', :to => 'attachments#thumbnail', :id => /\d+/, :size => /\d+/, :as => 'thumbnail'
266 get 'attachments/thumbnail/:id(/:size)', :to => 'attachments#thumbnail', :id => /\d+/, :size => /\d+/, :as => 'thumbnail'
264 resources :attachments, :only => [:show, :destroy]
267 resources :attachments, :only => [:show, :destroy]
265
268
266 resources :groups do
269 resources :groups do
267 member do
270 member do
268 get 'autocomplete_for_user'
271 get 'autocomplete_for_user'
269 end
272 end
270 end
273 end
271
274
272 match 'groups/:id/users', :controller => 'groups', :action => 'add_users', :id => /\d+/, :via => :post, :as => 'group_users'
275 match 'groups/:id/users', :controller => 'groups', :action => 'add_users', :id => /\d+/, :via => :post, :as => 'group_users'
273 match 'groups/:id/users/:user_id', :controller => 'groups', :action => 'remove_user', :id => /\d+/, :via => :delete, :as => 'group_user'
276 match 'groups/:id/users/:user_id', :controller => 'groups', :action => 'remove_user', :id => /\d+/, :via => :delete, :as => 'group_user'
274 match 'groups/destroy_membership/:id', :controller => 'groups', :action => 'destroy_membership', :id => /\d+/, :via => :post
277 match 'groups/destroy_membership/:id', :controller => 'groups', :action => 'destroy_membership', :id => /\d+/, :via => :post
275 match 'groups/edit_membership/:id', :controller => 'groups', :action => 'edit_membership', :id => /\d+/, :via => :post
278 match 'groups/edit_membership/:id', :controller => 'groups', :action => 'edit_membership', :id => /\d+/, :via => :post
276
279
277 resources :trackers, :except => :show do
280 resources :trackers, :except => :show do
278 collection do
281 collection do
279 match 'fields', :via => [:get, :post]
282 match 'fields', :via => [:get, :post]
280 end
283 end
281 end
284 end
282 resources :issue_statuses, :except => :show do
285 resources :issue_statuses, :except => :show do
283 collection do
286 collection do
284 post 'update_issue_done_ratio'
287 post 'update_issue_done_ratio'
285 end
288 end
286 end
289 end
287 resources :custom_fields, :except => :show
290 resources :custom_fields, :except => :show
288 resources :roles do
291 resources :roles do
289 collection do
292 collection do
290 match 'permissions', :via => [:get, :post]
293 match 'permissions', :via => [:get, :post]
291 end
294 end
292 end
295 end
293 resources :enumerations, :except => :show
296 resources :enumerations, :except => :show
294 match 'enumerations/:type', :to => 'enumerations#index', :via => :get
297 match 'enumerations/:type', :to => 'enumerations#index', :via => :get
295
298
296 get 'projects/:id/search', :controller => 'search', :action => 'index'
299 get 'projects/:id/search', :controller => 'search', :action => 'index'
297 get 'search', :controller => 'search', :action => 'index'
300 get 'search', :controller => 'search', :action => 'index'
298
301
299 match 'mail_handler', :controller => 'mail_handler', :action => 'index', :via => :post
302 match 'mail_handler', :controller => 'mail_handler', :action => 'index', :via => :post
300
303
301 match 'admin', :controller => 'admin', :action => 'index', :via => :get
304 match 'admin', :controller => 'admin', :action => 'index', :via => :get
302 match 'admin/projects', :controller => 'admin', :action => 'projects', :via => :get
305 match 'admin/projects', :controller => 'admin', :action => 'projects', :via => :get
303 match 'admin/plugins', :controller => 'admin', :action => 'plugins', :via => :get
306 match 'admin/plugins', :controller => 'admin', :action => 'plugins', :via => :get
304 match 'admin/info', :controller => 'admin', :action => 'info', :via => :get
307 match 'admin/info', :controller => 'admin', :action => 'info', :via => :get
305 match 'admin/test_email', :controller => 'admin', :action => 'test_email', :via => :get
308 match 'admin/test_email', :controller => 'admin', :action => 'test_email', :via => :get
306 match 'admin/default_configuration', :controller => 'admin', :action => 'default_configuration', :via => :post
309 match 'admin/default_configuration', :controller => 'admin', :action => 'default_configuration', :via => :post
307
310
308 resources :auth_sources do
311 resources :auth_sources do
309 member do
312 member do
310 get 'test_connection', :as => 'try_connection'
313 get 'test_connection', :as => 'try_connection'
311 end
314 end
312 collection do
315 collection do
313 get 'autocomplete_for_new_user'
316 get 'autocomplete_for_new_user'
314 end
317 end
315 end
318 end
316
319
317 match 'workflows', :controller => 'workflows', :action => 'index', :via => :get
320 match 'workflows', :controller => 'workflows', :action => 'index', :via => :get
318 match 'workflows/edit', :controller => 'workflows', :action => 'edit', :via => [:get, :post]
321 match 'workflows/edit', :controller => 'workflows', :action => 'edit', :via => [:get, :post]
319 match 'workflows/permissions', :controller => 'workflows', :action => 'permissions', :via => [:get, :post]
322 match 'workflows/permissions', :controller => 'workflows', :action => 'permissions', :via => [:get, :post]
320 match 'workflows/copy', :controller => 'workflows', :action => 'copy', :via => [:get, :post]
323 match 'workflows/copy', :controller => 'workflows', :action => 'copy', :via => [:get, :post]
321 match 'settings', :controller => 'settings', :action => 'index', :via => :get
324 match 'settings', :controller => 'settings', :action => 'index', :via => :get
322 match 'settings/edit', :controller => 'settings', :action => 'edit', :via => [:get, :post]
325 match 'settings/edit', :controller => 'settings', :action => 'edit', :via => [:get, :post]
323 match 'settings/plugin/:id', :controller => 'settings', :action => 'plugin', :via => [:get, :post], :as => 'plugin_settings'
326 match 'settings/plugin/:id', :controller => 'settings', :action => 'plugin', :via => [:get, :post], :as => 'plugin_settings'
324
327
325 match 'sys/projects', :to => 'sys#projects', :via => :get
328 match 'sys/projects', :to => 'sys#projects', :via => :get
326 match 'sys/projects/:id/repository', :to => 'sys#create_project_repository', :via => :post
329 match 'sys/projects/:id/repository', :to => 'sys#create_project_repository', :via => :post
327 match 'sys/fetch_changesets', :to => 'sys#fetch_changesets', :via => :get
330 match 'sys/fetch_changesets', :to => 'sys#fetch_changesets', :via => :get
328
331
329 match 'uploads', :to => 'attachments#upload', :via => :post
332 match 'uploads', :to => 'attachments#upload', :via => :post
330
333
331 get 'robots.txt', :to => 'welcome#robots'
334 get 'robots.txt', :to => 'welcome#robots'
332
335
333 Dir.glob File.expand_path("plugins/*", Rails.root) do |plugin_dir|
336 Dir.glob File.expand_path("plugins/*", Rails.root) do |plugin_dir|
334 file = File.join(plugin_dir, "config/routes.rb")
337 file = File.join(plugin_dir, "config/routes.rb")
335 if File.exists?(file)
338 if File.exists?(file)
336 begin
339 begin
337 instance_eval File.read(file)
340 instance_eval File.read(file)
338 rescue Exception => e
341 rescue Exception => e
339 puts "An error occurred while loading the routes definition of #{File.basename(plugin_dir)} plugin (#{file}): #{e.message}."
342 puts "An error occurred while loading the routes definition of #{File.basename(plugin_dir)} plugin (#{file}): #{e.message}."
340 exit 1
343 exit 1
341 end
344 end
342 end
345 end
343 end
346 end
344 end
347 end
@@ -1,795 +1,846
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base
20 class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base
21 fixtures :projects,
21 fixtures :projects,
22 :users,
22 :users,
23 :roles,
23 :roles,
24 :members,
24 :members,
25 :member_roles,
25 :member_roles,
26 :issues,
26 :issues,
27 :issue_statuses,
27 :issue_statuses,
28 :issue_relations,
28 :issue_relations,
29 :versions,
29 :versions,
30 :trackers,
30 :trackers,
31 :projects_trackers,
31 :projects_trackers,
32 :issue_categories,
32 :issue_categories,
33 :enabled_modules,
33 :enabled_modules,
34 :enumerations,
34 :enumerations,
35 :attachments,
35 :attachments,
36 :workflows,
36 :workflows,
37 :custom_fields,
37 :custom_fields,
38 :custom_values,
38 :custom_values,
39 :custom_fields_projects,
39 :custom_fields_projects,
40 :custom_fields_trackers,
40 :custom_fields_trackers,
41 :time_entries,
41 :time_entries,
42 :journals,
42 :journals,
43 :journal_details,
43 :journal_details,
44 :queries,
44 :queries,
45 :attachments
45 :attachments
46
46
47 def setup
47 def setup
48 Setting.rest_api_enabled = '1'
48 Setting.rest_api_enabled = '1'
49 end
49 end
50
50
51 context "/issues" do
51 context "/issues" do
52 # Use a private project to make sure auth is really working and not just
52 # Use a private project to make sure auth is really working and not just
53 # only showing public issues.
53 # only showing public issues.
54 should_allow_api_authentication(:get, "/projects/private-child/issues.xml")
54 should_allow_api_authentication(:get, "/projects/private-child/issues.xml")
55
55
56 should "contain metadata" do
56 should "contain metadata" do
57 get '/issues.xml'
57 get '/issues.xml'
58
58
59 assert_tag :tag => 'issues',
59 assert_tag :tag => 'issues',
60 :attributes => {
60 :attributes => {
61 :type => 'array',
61 :type => 'array',
62 :total_count => assigns(:issue_count),
62 :total_count => assigns(:issue_count),
63 :limit => 25,
63 :limit => 25,
64 :offset => 0
64 :offset => 0
65 }
65 }
66 end
66 end
67
67
68 context "with offset and limit" do
68 context "with offset and limit" do
69 should "use the params" do
69 should "use the params" do
70 get '/issues.xml?offset=2&limit=3'
70 get '/issues.xml?offset=2&limit=3'
71
71
72 assert_equal 3, assigns(:limit)
72 assert_equal 3, assigns(:limit)
73 assert_equal 2, assigns(:offset)
73 assert_equal 2, assigns(:offset)
74 assert_tag :tag => 'issues', :children => {:count => 3, :only => {:tag => 'issue'}}
74 assert_tag :tag => 'issues', :children => {:count => 3, :only => {:tag => 'issue'}}
75 end
75 end
76 end
76 end
77
77
78 context "with nometa param" do
78 context "with nometa param" do
79 should "not contain metadata" do
79 should "not contain metadata" do
80 get '/issues.xml?nometa=1'
80 get '/issues.xml?nometa=1'
81
81
82 assert_tag :tag => 'issues',
82 assert_tag :tag => 'issues',
83 :attributes => {
83 :attributes => {
84 :type => 'array',
84 :type => 'array',
85 :total_count => nil,
85 :total_count => nil,
86 :limit => nil,
86 :limit => nil,
87 :offset => nil
87 :offset => nil
88 }
88 }
89 end
89 end
90 end
90 end
91
91
92 context "with nometa header" do
92 context "with nometa header" do
93 should "not contain metadata" do
93 should "not contain metadata" do
94 get '/issues.xml', {}, {'X-Redmine-Nometa' => '1'}
94 get '/issues.xml', {}, {'X-Redmine-Nometa' => '1'}
95
95
96 assert_tag :tag => 'issues',
96 assert_tag :tag => 'issues',
97 :attributes => {
97 :attributes => {
98 :type => 'array',
98 :type => 'array',
99 :total_count => nil,
99 :total_count => nil,
100 :limit => nil,
100 :limit => nil,
101 :offset => nil
101 :offset => nil
102 }
102 }
103 end
103 end
104 end
104 end
105
105
106 context "with relations" do
106 context "with relations" do
107 should "display relations" do
107 should "display relations" do
108 get '/issues.xml?include=relations'
108 get '/issues.xml?include=relations'
109
109
110 assert_response :success
110 assert_response :success
111 assert_equal 'application/xml', @response.content_type
111 assert_equal 'application/xml', @response.content_type
112 assert_tag 'relations',
112 assert_tag 'relations',
113 :parent => {:tag => 'issue', :child => {:tag => 'id', :content => '3'}},
113 :parent => {:tag => 'issue', :child => {:tag => 'id', :content => '3'}},
114 :children => {:count => 1},
114 :children => {:count => 1},
115 :child => {
115 :child => {
116 :tag => 'relation',
116 :tag => 'relation',
117 :attributes => {:id => '2', :issue_id => '2', :issue_to_id => '3',
117 :attributes => {:id => '2', :issue_id => '2', :issue_to_id => '3',
118 :relation_type => 'relates'}
118 :relation_type => 'relates'}
119 }
119 }
120 assert_tag 'relations',
120 assert_tag 'relations',
121 :parent => {:tag => 'issue', :child => {:tag => 'id', :content => '1'}},
121 :parent => {:tag => 'issue', :child => {:tag => 'id', :content => '1'}},
122 :children => {:count => 0}
122 :children => {:count => 0}
123 end
123 end
124 end
124 end
125
125
126 context "with invalid query params" do
126 context "with invalid query params" do
127 should "return errors" do
127 should "return errors" do
128 get '/issues.xml', {:f => ['start_date'], :op => {:start_date => '='}}
128 get '/issues.xml', {:f => ['start_date'], :op => {:start_date => '='}}
129
129
130 assert_response :unprocessable_entity
130 assert_response :unprocessable_entity
131 assert_equal 'application/xml', @response.content_type
131 assert_equal 'application/xml', @response.content_type
132 assert_tag 'errors', :child => {:tag => 'error', :content => "Start date can't be blank"}
132 assert_tag 'errors', :child => {:tag => 'error', :content => "Start date can't be blank"}
133 end
133 end
134 end
134 end
135
135
136 context "with custom field filter" do
136 context "with custom field filter" do
137 should "show only issues with the custom field value" do
137 should "show only issues with the custom field value" do
138 get '/issues.xml',
138 get '/issues.xml',
139 {:set_filter => 1, :f => ['cf_1'], :op => {:cf_1 => '='},
139 {:set_filter => 1, :f => ['cf_1'], :op => {:cf_1 => '='},
140 :v => {:cf_1 => ['MySQL']}}
140 :v => {:cf_1 => ['MySQL']}}
141 expected_ids = Issue.visible.all(
141 expected_ids = Issue.visible.all(
142 :include => :custom_values,
142 :include => :custom_values,
143 :conditions => {:custom_values => {:custom_field_id => 1, :value => 'MySQL'}}).map(&:id)
143 :conditions => {:custom_values => {:custom_field_id => 1, :value => 'MySQL'}}).map(&:id)
144 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
144 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
145 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
145 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
146 end
146 end
147 end
147 end
148 end
148 end
149
149
150 context "with custom field filter (shorthand method)" do
150 context "with custom field filter (shorthand method)" do
151 should "show only issues with the custom field value" do
151 should "show only issues with the custom field value" do
152 get '/issues.xml', { :cf_1 => 'MySQL' }
152 get '/issues.xml', { :cf_1 => 'MySQL' }
153
153
154 expected_ids = Issue.visible.all(
154 expected_ids = Issue.visible.all(
155 :include => :custom_values,
155 :include => :custom_values,
156 :conditions => {:custom_values => {:custom_field_id => 1, :value => 'MySQL'}}).map(&:id)
156 :conditions => {:custom_values => {:custom_field_id => 1, :value => 'MySQL'}}).map(&:id)
157
157
158 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
158 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
159 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
159 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
160 end
160 end
161 end
161 end
162 end
162 end
163 end
163 end
164
164
165 context "/index.json" do
165 context "/index.json" do
166 should_allow_api_authentication(:get, "/projects/private-child/issues.json")
166 should_allow_api_authentication(:get, "/projects/private-child/issues.json")
167 end
167 end
168
168
169 context "/index.xml with filter" do
169 context "/index.xml with filter" do
170 should "show only issues with the status_id" do
170 should "show only issues with the status_id" do
171 get '/issues.xml?status_id=5'
171 get '/issues.xml?status_id=5'
172
172
173 expected_ids = Issue.visible.all(:conditions => {:status_id => 5}).map(&:id)
173 expected_ids = Issue.visible.all(:conditions => {:status_id => 5}).map(&:id)
174
174
175 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
175 assert_select 'issues > issue > id', :count => expected_ids.count do |ids|
176 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
176 ids.each { |id| assert expected_ids.delete(id.children.first.content.to_i) }
177 end
177 end
178 end
178 end
179 end
179 end
180
180
181 context "/index.json with filter" do
181 context "/index.json with filter" do
182 should "show only issues with the status_id" do
182 should "show only issues with the status_id" do
183 get '/issues.json?status_id=5'
183 get '/issues.json?status_id=5'
184
184
185 json = ActiveSupport::JSON.decode(response.body)
185 json = ActiveSupport::JSON.decode(response.body)
186 status_ids_used = json['issues'].collect {|j| j['status']['id'] }
186 status_ids_used = json['issues'].collect {|j| j['status']['id'] }
187 assert_equal 3, status_ids_used.length
187 assert_equal 3, status_ids_used.length
188 assert status_ids_used.all? {|id| id == 5 }
188 assert status_ids_used.all? {|id| id == 5 }
189 end
189 end
190
190
191 end
191 end
192
192
193 # Issue 6 is on a private project
193 # Issue 6 is on a private project
194 context "/issues/6.xml" do
194 context "/issues/6.xml" do
195 should_allow_api_authentication(:get, "/issues/6.xml")
195 should_allow_api_authentication(:get, "/issues/6.xml")
196 end
196 end
197
197
198 context "/issues/6.json" do
198 context "/issues/6.json" do
199 should_allow_api_authentication(:get, "/issues/6.json")
199 should_allow_api_authentication(:get, "/issues/6.json")
200 end
200 end
201
201
202 context "GET /issues/:id" do
202 context "GET /issues/:id" do
203 context "with journals" do
203 context "with journals" do
204 context ".xml" do
204 context ".xml" do
205 should "display journals" do
205 should "display journals" do
206 get '/issues/1.xml?include=journals'
206 get '/issues/1.xml?include=journals'
207
207
208 assert_tag :tag => 'issue',
208 assert_tag :tag => 'issue',
209 :child => {
209 :child => {
210 :tag => 'journals',
210 :tag => 'journals',
211 :attributes => { :type => 'array' },
211 :attributes => { :type => 'array' },
212 :child => {
212 :child => {
213 :tag => 'journal',
213 :tag => 'journal',
214 :attributes => { :id => '1'},
214 :attributes => { :id => '1'},
215 :child => {
215 :child => {
216 :tag => 'details',
216 :tag => 'details',
217 :attributes => { :type => 'array' },
217 :attributes => { :type => 'array' },
218 :child => {
218 :child => {
219 :tag => 'detail',
219 :tag => 'detail',
220 :attributes => { :name => 'status_id' },
220 :attributes => { :name => 'status_id' },
221 :child => {
221 :child => {
222 :tag => 'old_value',
222 :tag => 'old_value',
223 :content => '1',
223 :content => '1',
224 :sibling => {
224 :sibling => {
225 :tag => 'new_value',
225 :tag => 'new_value',
226 :content => '2'
226 :content => '2'
227 }
227 }
228 }
228 }
229 }
229 }
230 }
230 }
231 }
231 }
232 }
232 }
233 end
233 end
234 end
234 end
235 end
235 end
236
236
237 context "with custom fields" do
237 context "with custom fields" do
238 context ".xml" do
238 context ".xml" do
239 should "display custom fields" do
239 should "display custom fields" do
240 get '/issues/3.xml'
240 get '/issues/3.xml'
241
241
242 assert_tag :tag => 'issue',
242 assert_tag :tag => 'issue',
243 :child => {
243 :child => {
244 :tag => 'custom_fields',
244 :tag => 'custom_fields',
245 :attributes => { :type => 'array' },
245 :attributes => { :type => 'array' },
246 :child => {
246 :child => {
247 :tag => 'custom_field',
247 :tag => 'custom_field',
248 :attributes => { :id => '1'},
248 :attributes => { :id => '1'},
249 :child => {
249 :child => {
250 :tag => 'value',
250 :tag => 'value',
251 :content => 'MySQL'
251 :content => 'MySQL'
252 }
252 }
253 }
253 }
254 }
254 }
255
255
256 assert_nothing_raised do
256 assert_nothing_raised do
257 Hash.from_xml(response.body).to_xml
257 Hash.from_xml(response.body).to_xml
258 end
258 end
259 end
259 end
260 end
260 end
261 end
261 end
262
262
263 context "with multi custom fields" do
263 context "with multi custom fields" do
264 setup do
264 setup do
265 field = CustomField.find(1)
265 field = CustomField.find(1)
266 field.update_attribute :multiple, true
266 field.update_attribute :multiple, true
267 issue = Issue.find(3)
267 issue = Issue.find(3)
268 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
268 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
269 issue.save!
269 issue.save!
270 end
270 end
271
271
272 context ".xml" do
272 context ".xml" do
273 should "display custom fields" do
273 should "display custom fields" do
274 get '/issues/3.xml'
274 get '/issues/3.xml'
275 assert_response :success
275 assert_response :success
276 assert_tag :tag => 'issue',
276 assert_tag :tag => 'issue',
277 :child => {
277 :child => {
278 :tag => 'custom_fields',
278 :tag => 'custom_fields',
279 :attributes => { :type => 'array' },
279 :attributes => { :type => 'array' },
280 :child => {
280 :child => {
281 :tag => 'custom_field',
281 :tag => 'custom_field',
282 :attributes => { :id => '1'},
282 :attributes => { :id => '1'},
283 :child => {
283 :child => {
284 :tag => 'value',
284 :tag => 'value',
285 :attributes => { :type => 'array' },
285 :attributes => { :type => 'array' },
286 :children => { :count => 2 }
286 :children => { :count => 2 }
287 }
287 }
288 }
288 }
289 }
289 }
290
290
291 xml = Hash.from_xml(response.body)
291 xml = Hash.from_xml(response.body)
292 custom_fields = xml['issue']['custom_fields']
292 custom_fields = xml['issue']['custom_fields']
293 assert_kind_of Array, custom_fields
293 assert_kind_of Array, custom_fields
294 field = custom_fields.detect {|f| f['id'] == '1'}
294 field = custom_fields.detect {|f| f['id'] == '1'}
295 assert_kind_of Hash, field
295 assert_kind_of Hash, field
296 assert_equal ['MySQL', 'Oracle'], field['value'].sort
296 assert_equal ['MySQL', 'Oracle'], field['value'].sort
297 end
297 end
298 end
298 end
299
299
300 context ".json" do
300 context ".json" do
301 should "display custom fields" do
301 should "display custom fields" do
302 get '/issues/3.json'
302 get '/issues/3.json'
303 assert_response :success
303 assert_response :success
304 json = ActiveSupport::JSON.decode(response.body)
304 json = ActiveSupport::JSON.decode(response.body)
305 custom_fields = json['issue']['custom_fields']
305 custom_fields = json['issue']['custom_fields']
306 assert_kind_of Array, custom_fields
306 assert_kind_of Array, custom_fields
307 field = custom_fields.detect {|f| f['id'] == 1}
307 field = custom_fields.detect {|f| f['id'] == 1}
308 assert_kind_of Hash, field
308 assert_kind_of Hash, field
309 assert_equal ['MySQL', 'Oracle'], field['value'].sort
309 assert_equal ['MySQL', 'Oracle'], field['value'].sort
310 end
310 end
311 end
311 end
312 end
312 end
313
313
314 context "with empty value for multi custom field" do
314 context "with empty value for multi custom field" do
315 setup do
315 setup do
316 field = CustomField.find(1)
316 field = CustomField.find(1)
317 field.update_attribute :multiple, true
317 field.update_attribute :multiple, true
318 issue = Issue.find(3)
318 issue = Issue.find(3)
319 issue.custom_field_values = {1 => ['']}
319 issue.custom_field_values = {1 => ['']}
320 issue.save!
320 issue.save!
321 end
321 end
322
322
323 context ".xml" do
323 context ".xml" do
324 should "display custom fields" do
324 should "display custom fields" do
325 get '/issues/3.xml'
325 get '/issues/3.xml'
326 assert_response :success
326 assert_response :success
327 assert_tag :tag => 'issue',
327 assert_tag :tag => 'issue',
328 :child => {
328 :child => {
329 :tag => 'custom_fields',
329 :tag => 'custom_fields',
330 :attributes => { :type => 'array' },
330 :attributes => { :type => 'array' },
331 :child => {
331 :child => {
332 :tag => 'custom_field',
332 :tag => 'custom_field',
333 :attributes => { :id => '1'},
333 :attributes => { :id => '1'},
334 :child => {
334 :child => {
335 :tag => 'value',
335 :tag => 'value',
336 :attributes => { :type => 'array' },
336 :attributes => { :type => 'array' },
337 :children => { :count => 0 }
337 :children => { :count => 0 }
338 }
338 }
339 }
339 }
340 }
340 }
341
341
342 xml = Hash.from_xml(response.body)
342 xml = Hash.from_xml(response.body)
343 custom_fields = xml['issue']['custom_fields']
343 custom_fields = xml['issue']['custom_fields']
344 assert_kind_of Array, custom_fields
344 assert_kind_of Array, custom_fields
345 field = custom_fields.detect {|f| f['id'] == '1'}
345 field = custom_fields.detect {|f| f['id'] == '1'}
346 assert_kind_of Hash, field
346 assert_kind_of Hash, field
347 assert_equal [], field['value']
347 assert_equal [], field['value']
348 end
348 end
349 end
349 end
350
350
351 context ".json" do
351 context ".json" do
352 should "display custom fields" do
352 should "display custom fields" do
353 get '/issues/3.json'
353 get '/issues/3.json'
354 assert_response :success
354 assert_response :success
355 json = ActiveSupport::JSON.decode(response.body)
355 json = ActiveSupport::JSON.decode(response.body)
356 custom_fields = json['issue']['custom_fields']
356 custom_fields = json['issue']['custom_fields']
357 assert_kind_of Array, custom_fields
357 assert_kind_of Array, custom_fields
358 field = custom_fields.detect {|f| f['id'] == 1}
358 field = custom_fields.detect {|f| f['id'] == 1}
359 assert_kind_of Hash, field
359 assert_kind_of Hash, field
360 assert_equal [], field['value'].sort
360 assert_equal [], field['value'].sort
361 end
361 end
362 end
362 end
363 end
363 end
364
364
365 context "with attachments" do
365 context "with attachments" do
366 context ".xml" do
366 context ".xml" do
367 should "display attachments" do
367 should "display attachments" do
368 get '/issues/3.xml?include=attachments'
368 get '/issues/3.xml?include=attachments'
369
369
370 assert_tag :tag => 'issue',
370 assert_tag :tag => 'issue',
371 :child => {
371 :child => {
372 :tag => 'attachments',
372 :tag => 'attachments',
373 :children => {:count => 5},
373 :children => {:count => 5},
374 :child => {
374 :child => {
375 :tag => 'attachment',
375 :tag => 'attachment',
376 :child => {
376 :child => {
377 :tag => 'filename',
377 :tag => 'filename',
378 :content => 'source.rb',
378 :content => 'source.rb',
379 :sibling => {
379 :sibling => {
380 :tag => 'content_url',
380 :tag => 'content_url',
381 :content => 'http://www.example.com/attachments/download/4/source.rb'
381 :content => 'http://www.example.com/attachments/download/4/source.rb'
382 }
382 }
383 }
383 }
384 }
384 }
385 }
385 }
386 end
386 end
387 end
387 end
388 end
388 end
389
389
390 context "with subtasks" do
390 context "with subtasks" do
391 setup do
391 setup do
392 @c1 = Issue.create!(
392 @c1 = Issue.create!(
393 :status_id => 1, :subject => "child c1",
393 :status_id => 1, :subject => "child c1",
394 :tracker_id => 1, :project_id => 1, :author_id => 1,
394 :tracker_id => 1, :project_id => 1, :author_id => 1,
395 :parent_issue_id => 1
395 :parent_issue_id => 1
396 )
396 )
397 @c2 = Issue.create!(
397 @c2 = Issue.create!(
398 :status_id => 1, :subject => "child c2",
398 :status_id => 1, :subject => "child c2",
399 :tracker_id => 1, :project_id => 1, :author_id => 1,
399 :tracker_id => 1, :project_id => 1, :author_id => 1,
400 :parent_issue_id => 1
400 :parent_issue_id => 1
401 )
401 )
402 @c3 = Issue.create!(
402 @c3 = Issue.create!(
403 :status_id => 1, :subject => "child c3",
403 :status_id => 1, :subject => "child c3",
404 :tracker_id => 1, :project_id => 1, :author_id => 1,
404 :tracker_id => 1, :project_id => 1, :author_id => 1,
405 :parent_issue_id => @c1.id
405 :parent_issue_id => @c1.id
406 )
406 )
407 end
407 end
408
408
409 context ".xml" do
409 context ".xml" do
410 should "display children" do
410 should "display children" do
411 get '/issues/1.xml?include=children'
411 get '/issues/1.xml?include=children'
412
412
413 assert_tag :tag => 'issue',
413 assert_tag :tag => 'issue',
414 :child => {
414 :child => {
415 :tag => 'children',
415 :tag => 'children',
416 :children => {:count => 2},
416 :children => {:count => 2},
417 :child => {
417 :child => {
418 :tag => 'issue',
418 :tag => 'issue',
419 :attributes => {:id => @c1.id.to_s},
419 :attributes => {:id => @c1.id.to_s},
420 :child => {
420 :child => {
421 :tag => 'subject',
421 :tag => 'subject',
422 :content => 'child c1',
422 :content => 'child c1',
423 :sibling => {
423 :sibling => {
424 :tag => 'children',
424 :tag => 'children',
425 :children => {:count => 1},
425 :children => {:count => 1},
426 :child => {
426 :child => {
427 :tag => 'issue',
427 :tag => 'issue',
428 :attributes => {:id => @c3.id.to_s}
428 :attributes => {:id => @c3.id.to_s}
429 }
429 }
430 }
430 }
431 }
431 }
432 }
432 }
433 }
433 }
434 end
434 end
435
435
436 context ".json" do
436 context ".json" do
437 should "display children" do
437 should "display children" do
438 get '/issues/1.json?include=children'
438 get '/issues/1.json?include=children'
439
439
440 json = ActiveSupport::JSON.decode(response.body)
440 json = ActiveSupport::JSON.decode(response.body)
441 assert_equal([
441 assert_equal([
442 {
442 {
443 'id' => @c1.id, 'subject' => 'child c1', 'tracker' => {'id' => 1, 'name' => 'Bug'},
443 'id' => @c1.id, 'subject' => 'child c1', 'tracker' => {'id' => 1, 'name' => 'Bug'},
444 'children' => [{'id' => @c3.id, 'subject' => 'child c3',
444 'children' => [{'id' => @c3.id, 'subject' => 'child c3',
445 'tracker' => {'id' => 1, 'name' => 'Bug'} }]
445 'tracker' => {'id' => 1, 'name' => 'Bug'} }]
446 },
446 },
447 { 'id' => @c2.id, 'subject' => 'child c2', 'tracker' => {'id' => 1, 'name' => 'Bug'} }
447 { 'id' => @c2.id, 'subject' => 'child c2', 'tracker' => {'id' => 1, 'name' => 'Bug'} }
448 ],
448 ],
449 json['issue']['children'])
449 json['issue']['children'])
450 end
450 end
451 end
451 end
452 end
452 end
453 end
453 end
454 end
454 end
455
455
456 test "GET /issues/:id.xml?include=watchers should include watchers" do
457 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
458
459 get '/issues/1.xml?include=watchers', {}, credentials('jsmith')
460
461 assert_response :ok
462 assert_equal 'application/xml', response.content_type
463 assert_select 'issue' do
464 assert_select 'watchers', Issue.find(1).watchers.count
465 assert_select 'watchers' do
466 assert_select 'user[id=3]'
467 end
468 end
469 end
470
456 context "POST /issues.xml" do
471 context "POST /issues.xml" do
457 should_allow_api_authentication(
472 should_allow_api_authentication(
458 :post,
473 :post,
459 '/issues.xml',
474 '/issues.xml',
460 {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}},
475 {:issue => {:project_id => 1, :subject => 'API test', :tracker_id => 2, :status_id => 3}},
461 {:success_code => :created}
476 {:success_code => :created}
462 )
477 )
463 should "create an issue with the attributes" do
478 should "create an issue with the attributes" do
464 assert_difference('Issue.count') do
479 assert_difference('Issue.count') do
465 post '/issues.xml',
480 post '/issues.xml',
466 {:issue => {:project_id => 1, :subject => 'API test',
481 {:issue => {:project_id => 1, :subject => 'API test',
467 :tracker_id => 2, :status_id => 3}}, credentials('jsmith')
482 :tracker_id => 2, :status_id => 3}}, credentials('jsmith')
468 end
483 end
469 issue = Issue.first(:order => 'id DESC')
484 issue = Issue.first(:order => 'id DESC')
470 assert_equal 1, issue.project_id
485 assert_equal 1, issue.project_id
471 assert_equal 2, issue.tracker_id
486 assert_equal 2, issue.tracker_id
472 assert_equal 3, issue.status_id
487 assert_equal 3, issue.status_id
473 assert_equal 'API test', issue.subject
488 assert_equal 'API test', issue.subject
474
489
475 assert_response :created
490 assert_response :created
476 assert_equal 'application/xml', @response.content_type
491 assert_equal 'application/xml', @response.content_type
477 assert_tag 'issue', :child => {:tag => 'id', :content => issue.id.to_s}
492 assert_tag 'issue', :child => {:tag => 'id', :content => issue.id.to_s}
478 end
493 end
479 end
494 end
480
495
496 test "POST /issues.xml with watcher_user_ids should create issue with watchers" do
497 assert_difference('Issue.count') do
498 post '/issues.xml',
499 {:issue => {:project_id => 1, :subject => 'Watchers',
500 :tracker_id => 2, :status_id => 3, :watcher_user_ids => [3, 1]}}, credentials('jsmith')
501 assert_response :created
502 end
503 issue = Issue.order('id desc').first
504 assert_equal 2, issue.watchers.size
505 assert_equal [1, 3], issue.watcher_user_ids.sort
506 end
507
481 context "POST /issues.xml with failure" do
508 context "POST /issues.xml with failure" do
482 should "have an errors tag" do
509 should "have an errors tag" do
483 assert_no_difference('Issue.count') do
510 assert_no_difference('Issue.count') do
484 post '/issues.xml', {:issue => {:project_id => 1}}, credentials('jsmith')
511 post '/issues.xml', {:issue => {:project_id => 1}}, credentials('jsmith')
485 end
512 end
486
513
487 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
514 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
488 end
515 end
489 end
516 end
490
517
491 context "POST /issues.json" do
518 context "POST /issues.json" do
492 should_allow_api_authentication(:post,
519 should_allow_api_authentication(:post,
493 '/issues.json',
520 '/issues.json',
494 {:issue => {:project_id => 1, :subject => 'API test',
521 {:issue => {:project_id => 1, :subject => 'API test',
495 :tracker_id => 2, :status_id => 3}},
522 :tracker_id => 2, :status_id => 3}},
496 {:success_code => :created})
523 {:success_code => :created})
497
524
498 should "create an issue with the attributes" do
525 should "create an issue with the attributes" do
499 assert_difference('Issue.count') do
526 assert_difference('Issue.count') do
500 post '/issues.json',
527 post '/issues.json',
501 {:issue => {:project_id => 1, :subject => 'API test',
528 {:issue => {:project_id => 1, :subject => 'API test',
502 :tracker_id => 2, :status_id => 3}},
529 :tracker_id => 2, :status_id => 3}},
503 credentials('jsmith')
530 credentials('jsmith')
504 end
531 end
505
532
506 issue = Issue.first(:order => 'id DESC')
533 issue = Issue.first(:order => 'id DESC')
507 assert_equal 1, issue.project_id
534 assert_equal 1, issue.project_id
508 assert_equal 2, issue.tracker_id
535 assert_equal 2, issue.tracker_id
509 assert_equal 3, issue.status_id
536 assert_equal 3, issue.status_id
510 assert_equal 'API test', issue.subject
537 assert_equal 'API test', issue.subject
511 end
538 end
512
539
513 end
540 end
514
541
515 context "POST /issues.json with failure" do
542 context "POST /issues.json with failure" do
516 should "have an errors element" do
543 should "have an errors element" do
517 assert_no_difference('Issue.count') do
544 assert_no_difference('Issue.count') do
518 post '/issues.json', {:issue => {:project_id => 1}}, credentials('jsmith')
545 post '/issues.json', {:issue => {:project_id => 1}}, credentials('jsmith')
519 end
546 end
520
547
521 json = ActiveSupport::JSON.decode(response.body)
548 json = ActiveSupport::JSON.decode(response.body)
522 assert json['errors'].include?("Subject can't be blank")
549 assert json['errors'].include?("Subject can't be blank")
523 end
550 end
524 end
551 end
525
552
526 # Issue 6 is on a private project
553 # Issue 6 is on a private project
527 context "PUT /issues/6.xml" do
554 context "PUT /issues/6.xml" do
528 setup do
555 setup do
529 @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
556 @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
530 end
557 end
531
558
532 should_allow_api_authentication(:put,
559 should_allow_api_authentication(:put,
533 '/issues/6.xml',
560 '/issues/6.xml',
534 {:issue => {:subject => 'API update', :notes => 'A new note'}},
561 {:issue => {:subject => 'API update', :notes => 'A new note'}},
535 {:success_code => :ok})
562 {:success_code => :ok})
536
563
537 should "not create a new issue" do
564 should "not create a new issue" do
538 assert_no_difference('Issue.count') do
565 assert_no_difference('Issue.count') do
539 put '/issues/6.xml', @parameters, credentials('jsmith')
566 put '/issues/6.xml', @parameters, credentials('jsmith')
540 end
567 end
541 end
568 end
542
569
543 should "create a new journal" do
570 should "create a new journal" do
544 assert_difference('Journal.count') do
571 assert_difference('Journal.count') do
545 put '/issues/6.xml', @parameters, credentials('jsmith')
572 put '/issues/6.xml', @parameters, credentials('jsmith')
546 end
573 end
547 end
574 end
548
575
549 should "add the note to the journal" do
576 should "add the note to the journal" do
550 put '/issues/6.xml', @parameters, credentials('jsmith')
577 put '/issues/6.xml', @parameters, credentials('jsmith')
551
578
552 journal = Journal.last
579 journal = Journal.last
553 assert_equal "A new note", journal.notes
580 assert_equal "A new note", journal.notes
554 end
581 end
555
582
556 should "update the issue" do
583 should "update the issue" do
557 put '/issues/6.xml', @parameters, credentials('jsmith')
584 put '/issues/6.xml', @parameters, credentials('jsmith')
558
585
559 issue = Issue.find(6)
586 issue = Issue.find(6)
560 assert_equal "API update", issue.subject
587 assert_equal "API update", issue.subject
561 end
588 end
562
589
563 end
590 end
564
591
565 context "PUT /issues/3.xml with custom fields" do
592 context "PUT /issues/3.xml with custom fields" do
566 setup do
593 setup do
567 @parameters = {
594 @parameters = {
568 :issue => {:custom_fields => [{'id' => '1', 'value' => 'PostgreSQL' },
595 :issue => {:custom_fields => [{'id' => '1', 'value' => 'PostgreSQL' },
569 {'id' => '2', 'value' => '150'}]}
596 {'id' => '2', 'value' => '150'}]}
570 }
597 }
571 end
598 end
572
599
573 should "update custom fields" do
600 should "update custom fields" do
574 assert_no_difference('Issue.count') do
601 assert_no_difference('Issue.count') do
575 put '/issues/3.xml', @parameters, credentials('jsmith')
602 put '/issues/3.xml', @parameters, credentials('jsmith')
576 end
603 end
577
604
578 issue = Issue.find(3)
605 issue = Issue.find(3)
579 assert_equal '150', issue.custom_value_for(2).value
606 assert_equal '150', issue.custom_value_for(2).value
580 assert_equal 'PostgreSQL', issue.custom_value_for(1).value
607 assert_equal 'PostgreSQL', issue.custom_value_for(1).value
581 end
608 end
582 end
609 end
583
610
584 context "PUT /issues/3.xml with multi custom fields" do
611 context "PUT /issues/3.xml with multi custom fields" do
585 setup do
612 setup do
586 field = CustomField.find(1)
613 field = CustomField.find(1)
587 field.update_attribute :multiple, true
614 field.update_attribute :multiple, true
588 @parameters = {
615 @parameters = {
589 :issue => {:custom_fields => [{'id' => '1', 'value' => ['MySQL', 'PostgreSQL'] },
616 :issue => {:custom_fields => [{'id' => '1', 'value' => ['MySQL', 'PostgreSQL'] },
590 {'id' => '2', 'value' => '150'}]}
617 {'id' => '2', 'value' => '150'}]}
591 }
618 }
592 end
619 end
593
620
594 should "update custom fields" do
621 should "update custom fields" do
595 assert_no_difference('Issue.count') do
622 assert_no_difference('Issue.count') do
596 put '/issues/3.xml', @parameters, credentials('jsmith')
623 put '/issues/3.xml', @parameters, credentials('jsmith')
597 end
624 end
598
625
599 issue = Issue.find(3)
626 issue = Issue.find(3)
600 assert_equal '150', issue.custom_value_for(2).value
627 assert_equal '150', issue.custom_value_for(2).value
601 assert_equal ['MySQL', 'PostgreSQL'], issue.custom_field_value(1).sort
628 assert_equal ['MySQL', 'PostgreSQL'], issue.custom_field_value(1).sort
602 end
629 end
603 end
630 end
604
631
605 context "PUT /issues/3.xml with project change" do
632 context "PUT /issues/3.xml with project change" do
606 setup do
633 setup do
607 @parameters = {:issue => {:project_id => 2, :subject => 'Project changed'}}
634 @parameters = {:issue => {:project_id => 2, :subject => 'Project changed'}}
608 end
635 end
609
636
610 should "update project" do
637 should "update project" do
611 assert_no_difference('Issue.count') do
638 assert_no_difference('Issue.count') do
612 put '/issues/3.xml', @parameters, credentials('jsmith')
639 put '/issues/3.xml', @parameters, credentials('jsmith')
613 end
640 end
614
641
615 issue = Issue.find(3)
642 issue = Issue.find(3)
616 assert_equal 2, issue.project_id
643 assert_equal 2, issue.project_id
617 assert_equal 'Project changed', issue.subject
644 assert_equal 'Project changed', issue.subject
618 end
645 end
619 end
646 end
620
647
621 context "PUT /issues/6.xml with failed update" do
648 context "PUT /issues/6.xml with failed update" do
622 setup do
649 setup do
623 @parameters = {:issue => {:subject => ''}}
650 @parameters = {:issue => {:subject => ''}}
624 end
651 end
625
652
626 should "not create a new issue" do
653 should "not create a new issue" do
627 assert_no_difference('Issue.count') do
654 assert_no_difference('Issue.count') do
628 put '/issues/6.xml', @parameters, credentials('jsmith')
655 put '/issues/6.xml', @parameters, credentials('jsmith')
629 end
656 end
630 end
657 end
631
658
632 should "not create a new journal" do
659 should "not create a new journal" do
633 assert_no_difference('Journal.count') do
660 assert_no_difference('Journal.count') do
634 put '/issues/6.xml', @parameters, credentials('jsmith')
661 put '/issues/6.xml', @parameters, credentials('jsmith')
635 end
662 end
636 end
663 end
637
664
638 should "have an errors tag" do
665 should "have an errors tag" do
639 put '/issues/6.xml', @parameters, credentials('jsmith')
666 put '/issues/6.xml', @parameters, credentials('jsmith')
640
667
641 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
668 assert_tag :errors, :child => {:tag => 'error', :content => "Subject can't be blank"}
642 end
669 end
643 end
670 end
644
671
645 context "PUT /issues/6.json" do
672 context "PUT /issues/6.json" do
646 setup do
673 setup do
647 @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
674 @parameters = {:issue => {:subject => 'API update', :notes => 'A new note'}}
648 end
675 end
649
676
650 should_allow_api_authentication(:put,
677 should_allow_api_authentication(:put,
651 '/issues/6.json',
678 '/issues/6.json',
652 {:issue => {:subject => 'API update', :notes => 'A new note'}},
679 {:issue => {:subject => 'API update', :notes => 'A new note'}},
653 {:success_code => :ok})
680 {:success_code => :ok})
654
681
655 should "update the issue" do
682 should "update the issue" do
656 assert_no_difference('Issue.count') do
683 assert_no_difference('Issue.count') do
657 assert_difference('Journal.count') do
684 assert_difference('Journal.count') do
658 put '/issues/6.json', @parameters, credentials('jsmith')
685 put '/issues/6.json', @parameters, credentials('jsmith')
659
686
660 assert_response :ok
687 assert_response :ok
661 assert_equal '', response.body
688 assert_equal '', response.body
662 end
689 end
663 end
690 end
664
691
665 issue = Issue.find(6)
692 issue = Issue.find(6)
666 assert_equal "API update", issue.subject
693 assert_equal "API update", issue.subject
667 journal = Journal.last
694 journal = Journal.last
668 assert_equal "A new note", journal.notes
695 assert_equal "A new note", journal.notes
669 end
696 end
670 end
697 end
671
698
672 context "PUT /issues/6.json with failed update" do
699 context "PUT /issues/6.json with failed update" do
673 should "return errors" do
700 should "return errors" do
674 assert_no_difference('Issue.count') do
701 assert_no_difference('Issue.count') do
675 assert_no_difference('Journal.count') do
702 assert_no_difference('Journal.count') do
676 put '/issues/6.json', {:issue => {:subject => ''}}, credentials('jsmith')
703 put '/issues/6.json', {:issue => {:subject => ''}}, credentials('jsmith')
677
704
678 assert_response :unprocessable_entity
705 assert_response :unprocessable_entity
679 end
706 end
680 end
707 end
681
708
682 json = ActiveSupport::JSON.decode(response.body)
709 json = ActiveSupport::JSON.decode(response.body)
683 assert json['errors'].include?("Subject can't be blank")
710 assert json['errors'].include?("Subject can't be blank")
684 end
711 end
685 end
712 end
686
713
687 context "DELETE /issues/1.xml" do
714 context "DELETE /issues/1.xml" do
688 should_allow_api_authentication(:delete,
715 should_allow_api_authentication(:delete,
689 '/issues/6.xml',
716 '/issues/6.xml',
690 {},
717 {},
691 {:success_code => :ok})
718 {:success_code => :ok})
692
719
693 should "delete the issue" do
720 should "delete the issue" do
694 assert_difference('Issue.count', -1) do
721 assert_difference('Issue.count', -1) do
695 delete '/issues/6.xml', {}, credentials('jsmith')
722 delete '/issues/6.xml', {}, credentials('jsmith')
696
723
697 assert_response :ok
724 assert_response :ok
698 assert_equal '', response.body
725 assert_equal '', response.body
699 end
726 end
700
727
701 assert_nil Issue.find_by_id(6)
728 assert_nil Issue.find_by_id(6)
702 end
729 end
703 end
730 end
704
731
705 context "DELETE /issues/1.json" do
732 context "DELETE /issues/1.json" do
706 should_allow_api_authentication(:delete,
733 should_allow_api_authentication(:delete,
707 '/issues/6.json',
734 '/issues/6.json',
708 {},
735 {},
709 {:success_code => :ok})
736 {:success_code => :ok})
710
737
711 should "delete the issue" do
738 should "delete the issue" do
712 assert_difference('Issue.count', -1) do
739 assert_difference('Issue.count', -1) do
713 delete '/issues/6.json', {}, credentials('jsmith')
740 delete '/issues/6.json', {}, credentials('jsmith')
714
741
715 assert_response :ok
742 assert_response :ok
716 assert_equal '', response.body
743 assert_equal '', response.body
717 end
744 end
718
745
719 assert_nil Issue.find_by_id(6)
746 assert_nil Issue.find_by_id(6)
720 end
747 end
721 end
748 end
722
749
750 test "POST /issues/:id/watchers.xml should add watcher" do
751 assert_difference 'Watcher.count' do
752 post '/issues/1/watchers.xml', {:user_id => 3}, credentials('jsmith')
753
754 assert_response :ok
755 assert_equal '', response.body
756 end
757 watcher = Watcher.order('id desc').first
758 assert_equal Issue.find(1), watcher.watchable
759 assert_equal User.find(3), watcher.user
760 end
761
762 test "DELETE /issues/:id/watchers/:user_id.xml should remove watcher" do
763 Watcher.create!(:user_id => 3, :watchable => Issue.find(1))
764
765 assert_difference 'Watcher.count', -1 do
766 delete '/issues/1/watchers/3.xml', {}, credentials('jsmith')
767
768 assert_response :ok
769 assert_equal '', response.body
770 end
771 assert_equal false, Issue.find(1).watched_by?(User.find(3))
772 end
773
723 def test_create_issue_with_uploaded_file
774 def test_create_issue_with_uploaded_file
724 set_tmp_attachments_directory
775 set_tmp_attachments_directory
725 # upload the file
776 # upload the file
726 assert_difference 'Attachment.count' do
777 assert_difference 'Attachment.count' do
727 post '/uploads.xml', 'test_create_with_upload',
778 post '/uploads.xml', 'test_create_with_upload',
728 {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
779 {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
729 assert_response :created
780 assert_response :created
730 end
781 end
731 xml = Hash.from_xml(response.body)
782 xml = Hash.from_xml(response.body)
732 token = xml['upload']['token']
783 token = xml['upload']['token']
733 attachment = Attachment.first(:order => 'id DESC')
784 attachment = Attachment.first(:order => 'id DESC')
734
785
735 # create the issue with the upload's token
786 # create the issue with the upload's token
736 assert_difference 'Issue.count' do
787 assert_difference 'Issue.count' do
737 post '/issues.xml',
788 post '/issues.xml',
738 {:issue => {:project_id => 1, :subject => 'Uploaded file',
789 {:issue => {:project_id => 1, :subject => 'Uploaded file',
739 :uploads => [{:token => token, :filename => 'test.txt',
790 :uploads => [{:token => token, :filename => 'test.txt',
740 :content_type => 'text/plain'}]}},
791 :content_type => 'text/plain'}]}},
741 credentials('jsmith')
792 credentials('jsmith')
742 assert_response :created
793 assert_response :created
743 end
794 end
744 issue = Issue.first(:order => 'id DESC')
795 issue = Issue.first(:order => 'id DESC')
745 assert_equal 1, issue.attachments.count
796 assert_equal 1, issue.attachments.count
746 assert_equal attachment, issue.attachments.first
797 assert_equal attachment, issue.attachments.first
747
798
748 attachment.reload
799 attachment.reload
749 assert_equal 'test.txt', attachment.filename
800 assert_equal 'test.txt', attachment.filename
750 assert_equal 'text/plain', attachment.content_type
801 assert_equal 'text/plain', attachment.content_type
751 assert_equal 'test_create_with_upload'.size, attachment.filesize
802 assert_equal 'test_create_with_upload'.size, attachment.filesize
752 assert_equal 2, attachment.author_id
803 assert_equal 2, attachment.author_id
753
804
754 # get the issue with its attachments
805 # get the issue with its attachments
755 get "/issues/#{issue.id}.xml", :include => 'attachments'
806 get "/issues/#{issue.id}.xml", :include => 'attachments'
756 assert_response :success
807 assert_response :success
757 xml = Hash.from_xml(response.body)
808 xml = Hash.from_xml(response.body)
758 attachments = xml['issue']['attachments']
809 attachments = xml['issue']['attachments']
759 assert_kind_of Array, attachments
810 assert_kind_of Array, attachments
760 assert_equal 1, attachments.size
811 assert_equal 1, attachments.size
761 url = attachments.first['content_url']
812 url = attachments.first['content_url']
762 assert_not_nil url
813 assert_not_nil url
763
814
764 # download the attachment
815 # download the attachment
765 get url
816 get url
766 assert_response :success
817 assert_response :success
767 end
818 end
768
819
769 def test_update_issue_with_uploaded_file
820 def test_update_issue_with_uploaded_file
770 set_tmp_attachments_directory
821 set_tmp_attachments_directory
771 # upload the file
822 # upload the file
772 assert_difference 'Attachment.count' do
823 assert_difference 'Attachment.count' do
773 post '/uploads.xml', 'test_upload_with_upload',
824 post '/uploads.xml', 'test_upload_with_upload',
774 {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
825 {"CONTENT_TYPE" => 'application/octet-stream'}.merge(credentials('jsmith'))
775 assert_response :created
826 assert_response :created
776 end
827 end
777 xml = Hash.from_xml(response.body)
828 xml = Hash.from_xml(response.body)
778 token = xml['upload']['token']
829 token = xml['upload']['token']
779 attachment = Attachment.first(:order => 'id DESC')
830 attachment = Attachment.first(:order => 'id DESC')
780
831
781 # update the issue with the upload's token
832 # update the issue with the upload's token
782 assert_difference 'Journal.count' do
833 assert_difference 'Journal.count' do
783 put '/issues/1.xml',
834 put '/issues/1.xml',
784 {:issue => {:notes => 'Attachment added',
835 {:issue => {:notes => 'Attachment added',
785 :uploads => [{:token => token, :filename => 'test.txt',
836 :uploads => [{:token => token, :filename => 'test.txt',
786 :content_type => 'text/plain'}]}},
837 :content_type => 'text/plain'}]}},
787 credentials('jsmith')
838 credentials('jsmith')
788 assert_response :ok
839 assert_response :ok
789 assert_equal '', @response.body
840 assert_equal '', @response.body
790 end
841 end
791
842
792 issue = Issue.find(1)
843 issue = Issue.find(1)
793 assert_include attachment, issue.attachments
844 assert_include attachment, issue.attachments
794 end
845 end
795 end
846 end
@@ -1,51 +1,61
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../../test_helper', __FILE__)
18 require File.expand_path('../../../test_helper', __FILE__)
19
19
20 class RoutingWatchersTest < ActionController::IntegrationTest
20 class RoutingWatchersTest < ActionController::IntegrationTest
21 def test_watchers
21 def test_watchers
22 assert_routing(
22 assert_routing(
23 { :method => 'get', :path => "/watchers/new" },
23 { :method => 'get', :path => "/watchers/new" },
24 { :controller => 'watchers', :action => 'new' }
24 { :controller => 'watchers', :action => 'new' }
25 )
25 )
26 assert_routing(
26 assert_routing(
27 { :method => 'post', :path => "/watchers/append" },
27 { :method => 'post', :path => "/watchers/append" },
28 { :controller => 'watchers', :action => 'append' }
28 { :controller => 'watchers', :action => 'append' }
29 )
29 )
30 assert_routing(
30 assert_routing(
31 { :method => 'post', :path => "/watchers" },
31 { :method => 'post', :path => "/watchers" },
32 { :controller => 'watchers', :action => 'create' }
32 { :controller => 'watchers', :action => 'create' }
33 )
33 )
34 assert_routing(
34 assert_routing(
35 { :method => 'post', :path => "/watchers/destroy" },
35 { :method => 'post', :path => "/watchers/destroy" },
36 { :controller => 'watchers', :action => 'destroy' }
36 { :controller => 'watchers', :action => 'destroy' }
37 )
37 )
38 assert_routing(
38 assert_routing(
39 { :method => 'get', :path => "/watchers/autocomplete_for_user" },
39 { :method => 'get', :path => "/watchers/autocomplete_for_user" },
40 { :controller => 'watchers', :action => 'autocomplete_for_user' }
40 { :controller => 'watchers', :action => 'autocomplete_for_user' }
41 )
41 )
42 assert_routing(
42 assert_routing(
43 { :method => 'post', :path => "/watchers/watch" },
43 { :method => 'post', :path => "/watchers/watch" },
44 { :controller => 'watchers', :action => 'watch' }
44 { :controller => 'watchers', :action => 'watch' }
45 )
45 )
46 assert_routing(
46 assert_routing(
47 { :method => 'post', :path => "/watchers/unwatch" },
47 { :method => 'post', :path => "/watchers/unwatch" },
48 { :controller => 'watchers', :action => 'unwatch' }
48 { :controller => 'watchers', :action => 'unwatch' }
49 )
49 )
50 assert_routing(
51 { :method => 'post', :path => "/issues/12/watchers.xml" },
52 { :controller => 'watchers', :action => 'create',
53 :object_type => 'issue', :object_id => '12', :format => 'xml' }
54 )
55 assert_routing(
56 { :method => 'delete', :path => "/issues/12/watchers/3.xml" },
57 { :controller => 'watchers', :action => 'destroy',
58 :object_type => 'issue', :object_id => '12', :user_id => '3', :format => 'xml'}
59 )
50 end
60 end
51 end
61 end
General Comments 0
You need to be logged in to leave comments. Login now