##// END OF EJS Templates
Adds REST API for versions (#7403)....
Jean-Philippe Lang -
r6060:b86a748b1d3a
parent child
Show More
@@ -0,0 +1,18
1 api.array :versions, api_meta(:total_count => @versions.size) do
2 @versions.each do |version|
3 api.version do
4 api.id version.id
5 api.project(:id => version.project_id, :name => version.project.name) unless version.project.nil?
6
7 api.name version.name
8 api.description version.description
9 api.status version.status
10 api.due_date version.effective_date
11
12 render_api_custom_values version.custom_field_values, api
13
14 api.created_on version.created_on
15 api.updated_on version.updated_on
16 end
17 end
18 end
@@ -0,0 +1,14
1 api.version do
2 api.id @version.id
3 api.project(:id => @version.project_id, :name => @version.project.name) unless @version.project.nil?
4
5 api.name @version.name
6 api.description @version.description
7 api.status @version.status
8 api.due_date @version.effective_date
9
10 render_api_custom_values @version.custom_field_values, api
11
12 api.created_on @version.created_on
13 api.updated_on @version.updated_on
14 end
@@ -0,0 +1,120
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require File.expand_path('../../../test_helper', __FILE__)
19
20 class ApiTest::VersionsTest < ActionController::IntegrationTest
21 fixtures :all
22
23 def setup
24 Setting.rest_api_enabled = '1'
25 end
26
27 context "/projects/:project_id/versions" do
28 context "GET" do
29 should "return project versions" do
30 get '/projects/1/versions.xml'
31
32 assert_response :success
33 assert_equal 'application/xml', @response.content_type
34 assert_tag :tag => 'versions',
35 :attributes => {:type => 'array'},
36 :child => {
37 :tag => 'version',
38 :child => {
39 :tag => 'id',
40 :content => '2',
41 :sibling => {
42 :tag => 'name',
43 :content => '1.0'
44 }
45 }
46 }
47 end
48 end
49
50 context "POST" do
51 should "create the version" do
52 assert_difference 'Version.count' do
53 post '/projects/1/versions.xml', {:version => {:name => 'API test'}}, :authorization => credentials('jsmith')
54 end
55
56 version = Version.first(:order => 'id DESC')
57 assert_equal 'API test', version.name
58
59 assert_response :created
60 assert_equal 'application/xml', @response.content_type
61 assert_tag 'version', :child => {:tag => 'id', :content => version.id.to_s}
62 end
63
64 context "with failure" do
65 should "return the errors" do
66 assert_no_difference('Version.count') do
67 post '/projects/1/versions.xml', {:version => {:name => ''}}, :authorization => credentials('jsmith')
68 end
69
70 assert_response :unprocessable_entity
71 assert_tag :errors, :child => {:tag => 'error', :content => "Name can't be blank"}
72 end
73 end
74 end
75 end
76
77 context "/projects/:project_id/versions/:id" do
78 context "GET" do
79 should "return the version" do
80 get '/projects/1/versions/2.xml'
81
82 assert_response :success
83 assert_equal 'application/xml', @response.content_type
84 assert_tag 'version',
85 :child => {
86 :tag => 'id',
87 :content => '2',
88 :sibling => {
89 :tag => 'name',
90 :content => '1.0'
91 }
92 }
93 end
94 end
95
96 context "PUT" do
97 should "update the version" do
98 put '/projects/1/versions/2.xml', {:version => {:name => 'API update'}}, :authorization => credentials('jsmith')
99
100 assert_response :ok
101 assert_equal 'API update', Version.find(2).name
102 end
103 end
104
105 context "DELETE" do
106 should "destroy the version" do
107 assert_difference 'Version.count', -1 do
108 delete '/projects/1/versions/3.xml', {}, :authorization => credentials('jsmith')
109 end
110
111 assert_response :ok
112 assert_nil Version.find_by_id(3)
113 end
114 end
115 end
116
117 def credentials(user, password=nil)
118 ActionController::HttpAuthentication::Basic.encode_credentials(user, password || user)
119 end
120 end
@@ -1,5 +1,5
1 # redMine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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
@@ -23,37 +23,51 class VersionsController < ApplicationController
23 before_filter :find_project, :only => [:index, :new, :create, :close_completed]
23 before_filter :find_project, :only => [:index, :new, :create, :close_completed]
24 before_filter :authorize
24 before_filter :authorize
25
25
26 accept_key_auth :index, :create, :update, :destroy
27
26 helper :custom_fields
28 helper :custom_fields
27 helper :projects
29 helper :projects
28
30
29 def index
31 def index
30 @trackers = @project.trackers.find(:all, :order => 'position')
32 respond_to do |format|
31 retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
33 format.html {
32 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
34 @trackers = @project.trackers.find(:all, :order => 'position')
33 project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
35 retrieve_selected_tracker_ids(@trackers, @trackers.select {|t| t.is_in_roadmap?})
34
36 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
35 @versions = @project.shared_versions || []
37 project_ids = @with_subprojects ? @project.self_and_descendants.collect(&:id) : [@project.id]
36 @versions += @project.rolled_up_versions.visible if @with_subprojects
38
37 @versions = @versions.uniq.sort
39 @versions = @project.shared_versions || []
38 @versions.reject! {|version| version.closed? || version.completed? } unless params[:completed]
40 @versions += @project.rolled_up_versions.visible if @with_subprojects
39
41 @versions = @versions.uniq.sort
40 @issues_by_version = {}
42 @versions.reject! {|version| version.closed? || version.completed? } unless params[:completed]
41 unless @selected_tracker_ids.empty?
43
42 @versions.each do |version|
44 @issues_by_version = {}
43 issues = version.fixed_issues.visible.find(:all,
45 unless @selected_tracker_ids.empty?
44 :include => [:project, :status, :tracker, :priority],
46 @versions.each do |version|
45 :conditions => {:tracker_id => @selected_tracker_ids, :project_id => project_ids},
47 issues = version.fixed_issues.visible.find(:all,
46 :order => "#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id")
48 :include => [:project, :status, :tracker, :priority],
47 @issues_by_version[version] = issues
49 :conditions => {:tracker_id => @selected_tracker_ids, :project_id => project_ids},
48 end
50 :order => "#{Project.table_name}.lft, #{Tracker.table_name}.position, #{Issue.table_name}.id")
51 @issues_by_version[version] = issues
52 end
53 end
54 @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
55 }
56 format.api {
57 @versions = @project.shared_versions.all
58 }
49 end
59 end
50 @versions.reject! {|version| !project_ids.include?(version.project_id) && @issues_by_version[version].blank?}
51 end
60 end
52
61
53 def show
62 def show
54 @issues = @version.fixed_issues.visible.find(:all,
63 respond_to do |format|
55 :include => [:status, :tracker, :priority],
64 format.html {
56 :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id")
65 @issues = @version.fixed_issues.visible.find(:all,
66 :include => [:status, :tracker, :priority],
67 :order => "#{Tracker.table_name}.position, #{Issue.table_name}.id")
68 }
69 format.api
70 end
57 end
71 end
58
72
59 def new
73 def new
@@ -87,6 +101,9 class VersionsController < ApplicationController
87 content_tag('select', '<option></option>' + version_options_for_select(@project.shared_versions.open, @version), :id => 'issue_fixed_version_id', :name => 'issue[fixed_version_id]')
101 content_tag('select', '<option></option>' + version_options_for_select(@project.shared_versions.open, @version), :id => 'issue_fixed_version_id', :name => 'issue[fixed_version_id]')
88 }
102 }
89 end
103 end
104 format.api do
105 render :action => 'show', :status => :created, :location => project_version_url(@project, @version)
106 end
90 end
107 end
91 else
108 else
92 respond_to do |format|
109 respond_to do |format|
@@ -94,6 +111,7 class VersionsController < ApplicationController
94 format.js do
111 format.js do
95 render(:update) {|page| page.alert(@version.errors.full_messages.join('\n')) }
112 render(:update) {|page| page.alert(@version.errors.full_messages.join('\n')) }
96 end
113 end
114 format.api { render_validation_errors(@version) }
97 end
115 end
98 end
116 end
99 end
117 end
@@ -107,11 +125,17 class VersionsController < ApplicationController
107 attributes = params[:version].dup
125 attributes = params[:version].dup
108 attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing'])
126 attributes.delete('sharing') unless @version.allowed_sharings.include?(attributes['sharing'])
109 if @version.update_attributes(attributes)
127 if @version.update_attributes(attributes)
110 flash[:notice] = l(:notice_successful_update)
128 respond_to do |format|
111 redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
129 format.html {
130 flash[:notice] = l(:notice_successful_update)
131 redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
132 }
133 format.api { head :ok }
134 end
112 else
135 else
113 respond_to do |format|
136 respond_to do |format|
114 format.html { render :action => 'edit' }
137 format.html { render :action => 'edit' }
138 format.api { render_validation_errors(@version) }
115 end
139 end
116 end
140 end
117 end
141 end
@@ -124,13 +148,22 class VersionsController < ApplicationController
124 redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
148 redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
125 end
149 end
126
150
151 verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
127 def destroy
152 def destroy
128 if @version.fixed_issues.empty?
153 if @version.fixed_issues.empty?
129 @version.destroy
154 @version.destroy
130 redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
155 respond_to do |format|
156 format.html { redirect_back_or_default :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project }
157 format.api { head :ok }
158 end
131 else
159 else
132 flash[:error] = l(:notice_unable_delete_version)
160 respond_to do |format|
133 redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
161 format.html {
162 flash[:error] = l(:notice_unable_delete_version)
163 redirect_to :controller => 'projects', :action => 'settings', :tab => 'versions', :id => @project
164 }
165 format.api { head :unprocessable_entity }
166 end
134 end
167 end
135 end
168 end
136
169
@@ -19,9 +19,9
19 <td class="sharing"><%=h format_version_sharing(version.sharing) %></td>
19 <td class="sharing"><%=h format_version_sharing(version.sharing) %></td>
20 <td><%= link_to_if_authorized(h(version.wiki_page_title), {:controller => 'wiki', :action => 'show', :project_id => version.project, :id => Wiki.titleize(version.wiki_page_title)}) || h(version.wiki_page_title) unless version.wiki_page_title.blank? || version.project.wiki.nil? %></td>
20 <td><%= link_to_if_authorized(h(version.wiki_page_title), {:controller => 'wiki', :action => 'show', :project_id => version.project, :id => Wiki.titleize(version.wiki_page_title)}) || h(version.wiki_page_title) unless version.wiki_page_title.blank? || version.project.wiki.nil? %></td>
21 <td class="buttons">
21 <td class="buttons">
22 <% if version.project == @project %>
22 <% if version.project == @project && User.current.allowed_to?(:manage_versions, @project) %>
23 <%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => version }, :class => 'icon icon-edit' %>
23 <%= link_to l(:button_edit), edit_project_version_path(@project, version), :class => 'icon icon-edit' %>
24 <%= link_to_if_authorized l(:button_delete), {:controller => 'versions', :action => 'destroy', :id => version}, :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-del' %>
24 <%= link_to l(:button_delete), project_version_path(@project, version), :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-del' %>
25 <% end %>
25 <% end %>
26 </td>
26 </td>
27 </tr>
27 </tr>
@@ -1,8 +1,8
1 <div class="contextual">
1 <div class="contextual">
2 <%= link_to_if_authorized l(:button_edit), {:controller => 'versions', :action => 'edit', :id => @version}, :class => 'icon icon-edit' %>
2 <%= link_to(l(:button_edit), edit_project_version_path(@version.project, @version), :class => 'icon icon-edit') if User.current.allowed_to?(:manage_versions, @version.project) %>
3 <%= link_to_if_authorized(l(:button_edit_associated_wikipage, :page_title => @version.wiki_page_title), {:controller => 'wiki', :action => 'edit', :project_id => @version.project, :id => Wiki.titleize(@version.wiki_page_title)}, :class => 'icon icon-edit') unless @version.wiki_page_title.blank? || @version.project.wiki.nil? %>
3 <%= link_to_if_authorized(l(:button_edit_associated_wikipage, :page_title => @version.wiki_page_title), {:controller => 'wiki', :action => 'edit', :project_id => @version.project, :id => Wiki.titleize(@version.wiki_page_title)}, :class => 'icon icon-edit') unless @version.wiki_page_title.blank? || @version.project.wiki.nil? %>
4 <%= link_to_if_authorized l(:button_delete), {:controller => 'versions', :action => 'destroy', :id => @version, :back_url => url_for(:controller => 'versions', :action => 'index', :project_id => @version.project)},
4 <%= link_to(l(:button_delete), project_version_path(@version.project, @version, :back_url => url_for(:controller => 'versions', :action => 'index', :project_id => @version.project)),
5 :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-del' %>
5 :confirm => l(:text_are_you_sure), :method => :delete, :class => 'icon icon-del') if User.current.allowed_to?(:manage_versions, @version.project) %>
6 <%= call_hook(:view_versions_show_contextual, { :version => @version, :project => @project }) %>
6 <%= call_hook(:view_versions_show_contextual, { :version => @version, :project => @project }) %>
7 </div>
7 </div>
8
8
@@ -1,5 +1,5
1 # redMine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2010 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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
@@ -326,16 +326,33 class RoutingTest < ActionController::IntegrationTest
326 should_route :delete, "/users/44.xml", :controller => 'users', :action => 'destroy', :id => '44', :format => 'xml'
326 should_route :delete, "/users/44.xml", :controller => 'users', :action => 'destroy', :id => '44', :format => 'xml'
327 end
327 end
328
328
329 # TODO: should they all be scoped under /projects/:project_id ?
330 context "versions" do
329 context "versions" do
330 # /projects/foo/versions is /projects/foo/roadmap
331 should_route :get, "/projects/foo/versions.xml", :controller => 'versions', :action => 'index', :project_id => 'foo', :format => 'xml'
332 should_route :get, "/projects/foo/versions.json", :controller => 'versions', :action => 'index', :project_id => 'foo', :format => 'json'
333
331 should_route :get, "/projects/foo/versions/new", :controller => 'versions', :action => 'new', :project_id => 'foo'
334 should_route :get, "/projects/foo/versions/new", :controller => 'versions', :action => 'new', :project_id => 'foo'
332 should_route :get, "/versions/show/1", :controller => 'versions', :action => 'show', :id => '1'
335
333 should_route :get, "/versions/edit/1", :controller => 'versions', :action => 'edit', :id => '1'
334
335 should_route :post, "/projects/foo/versions", :controller => 'versions', :action => 'create', :project_id => 'foo'
336 should_route :post, "/projects/foo/versions", :controller => 'versions', :action => 'create', :project_id => 'foo'
336 should_route :post, "/versions/update/1", :controller => 'versions', :action => 'update', :id => '1'
337 should_route :post, "/projects/foo/versions.xml", :controller => 'versions', :action => 'create', :project_id => 'foo', :format => 'xml'
337
338 should_route :post, "/projects/foo/versions.json", :controller => 'versions', :action => 'create', :project_id => 'foo', :format => 'json'
338 should_route :delete, "/versions/destroy/1", :controller => 'versions', :action => 'destroy', :id => '1'
339
340 should_route :get, "/projects/foo/versions/1", :controller => 'versions', :action => 'show', :project_id => 'foo', :id => '1'
341 should_route :get, "/projects/foo/versions/1.xml", :controller => 'versions', :action => 'show', :project_id => 'foo', :id => '1', :format => 'xml'
342 should_route :get, "/projects/foo/versions/1.json", :controller => 'versions', :action => 'show', :project_id => 'foo', :id => '1', :format => 'json'
343
344 should_route :get, "/projects/foo/versions/1/edit", :controller => 'versions', :action => 'edit', :project_id => 'foo', :id => '1'
345
346 should_route :put, "/projects/foo/versions/1", :controller => 'versions', :action => 'update', :project_id => 'foo', :id => '1'
347 should_route :put, "/projects/foo/versions/1.xml", :controller => 'versions', :action => 'update', :project_id => 'foo', :id => '1', :format => 'xml'
348 should_route :put, "/projects/foo/versions/1.json", :controller => 'versions', :action => 'update', :project_id => 'foo', :id => '1', :format => 'json'
349
350 should_route :delete, "/projects/foo/versions/1", :controller => 'versions', :action => 'destroy', :project_id => 'foo', :id => '1'
351 should_route :delete, "/projects/foo/versions/1.xml", :controller => 'versions', :action => 'destroy', :project_id => 'foo', :id => '1', :format => 'xml'
352 should_route :delete, "/projects/foo/versions/1.json", :controller => 'versions', :action => 'destroy', :project_id => 'foo', :id => '1', :format => 'json'
353
354 should_route :put, "/projects/foo/versions/close_completed", :controller => 'versions', :action => 'close_completed', :project_id => 'foo'
355 should_route :post, "/projects/foo/versions/1/status_by", :controller => 'versions', :action => 'status_by', :project_id => 'foo', :id => '1'
339 end
356 end
340
357
341 context "wiki (singular, project's pages)" do
358 context "wiki (singular, project's pages)" do
@@ -1,5 +1,5
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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
@@ -27,6 +27,7 class VersionTest < ActiveSupport::TestCase
27 v = Version.new(:project => Project.find(1), :name => '1.1', :effective_date => '2011-03-25')
27 v = Version.new(:project => Project.find(1), :name => '1.1', :effective_date => '2011-03-25')
28 assert v.save
28 assert v.save
29 assert_equal 'open', v.status
29 assert_equal 'open', v.status
30 assert_equal 'none', v.sharing
30 end
31 end
31
32
32 def test_invalid_effective_date_validation
33 def test_invalid_effective_date_validation
General Comments 0
You need to be logged in to leave comments. Login now