Commit bd17fa780fb7f38eb201c96fc4a87f9f8e597a55

Authored by Nick Recobra
2 parents d95174a2 dd383c8f
Exists in master and in 1 other branch production

Merge branch 'feature/redmine'

@@ -7,6 +7,7 @@ gem 'haml' @@ -7,6 +7,7 @@ gem 'haml'
7 gem 'will_paginate' 7 gem 'will_paginate'
8 gem 'devise', '~> 1.1.8' 8 gem 'devise', '~> 1.1.8'
9 gem 'lighthouse-api' 9 gem 'lighthouse-api'
  10 +gem 'redmine_client', :git => "git://github.com/oruen/redmine_client.git"
10 11
11 platform :ruby do 12 platform :ruby do
12 gem 'bson_ext', '~> 1.2' 13 gem 'bson_ext', '~> 1.2'
  1 +GIT
  2 + remote: git://github.com/oruen/redmine_client.git
  3 + revision: 0df20a8b695869b03cfa129560b938a0af346add
  4 + specs:
  5 + redmine_client (0.0.1)
  6 + activeresource (>= 2.3.0)
  7 +
1 GEM 8 GEM
2 remote: http://rubygems.org/ 9 remote: http://rubygems.org/
3 specs: 10 specs:
@@ -122,6 +129,7 @@ DEPENDENCIES @@ -122,6 +129,7 @@ DEPENDENCIES
122 mongoid (~> 2.0.0.rc.7) 129 mongoid (~> 2.0.0.rc.7)
123 nokogiri 130 nokogiri
124 rails (= 3.0.5) 131 rails (= 3.0.5)
  132 + redmine_client!
125 rspec (~> 2.5) 133 rspec (~> 2.5)
126 rspec-rails (~> 2.5) 134 rspec-rails (~> 2.5)
127 webmock 135 webmock
@@ -94,7 +94,14 @@ Lighthouseapp integration @@ -94,7 +94,14 @@ Lighthouseapp integration
94 94
95 * Account is the name of your subdomain, i.e. **litcafe** for project at http://litcafe.lighthouseapp.com/projects/73466-face/overview 95 * Account is the name of your subdomain, i.e. **litcafe** for project at http://litcafe.lighthouseapp.com/projects/73466-face/overview
96 * Errbit uses token-based authentication. Get your API Token or visit [http://help.lighthouseapp.com/kb/api/how-do-i-get-an-api-token](http://help.lighthouseapp.com/kb/api/how-do-i-get-an-api-token) to learn how to get it. 96 * Errbit uses token-based authentication. Get your API Token or visit [http://help.lighthouseapp.com/kb/api/how-do-i-get-an-api-token](http://help.lighthouseapp.com/kb/api/how-do-i-get-an-api-token) to learn how to get it.
97 -* Project id is number identifier of your project, i.e. **73466** for project at http://litcafe.lighthouseapp.com/projects/73466-face/overview 97 +* Project id is number identifier of your project, i.e. **73466** for project at http://litcafe.lighthouseapp.com/projects/73466-face/overview
  98 +
  99 +Redmine integration
  100 +-------------------------
  101 +
  102 +* Account is the host of your redmine installation, i.e. **http://redmine.org**
  103 +* Errbit uses token-based authentication. Get your API Key or visit [http://www.redmine.org/projects/redmine/wiki/Rest_api#Authentication](http://www.redmine.org/projects/redmine/wiki/Rest_api#Authentication) to learn how to get it.
  104 +* Project id is an identifier of your project, i.e. **chilliproject** for project at http://www.redmine.org/projects/chilliproject
98 105
99 TODO 106 TODO
100 ---- 107 ----
app/controllers/errs_controller.rb
@@ -36,7 +36,8 @@ class ErrsController < ApplicationController @@ -36,7 +36,8 @@ class ErrsController < ApplicationController
36 flash[:error] = "This up has no issue tracker setup." 36 flash[:error] = "This up has no issue tracker setup."
37 end 37 end
38 redirect_to app_err_path(@app, @err) 38 redirect_to app_err_path(@app, @err)
39 - rescue ActiveResource::ConnectionError 39 + rescue ActiveResource::ConnectionError => e
  40 + Rails.logger.error e.to_s
40 flash[:error] = "There was an error during issue creation. Check your tracker settings or try again later." 41 flash[:error] = "There was an error during issue creation. Check your tracker settings or try again later."
41 redirect_to app_err_path(@app, @err) 42 redirect_to app_err_path(@app, @err)
42 end 43 end
app/helpers/application_helper.rb
1 module ApplicationHelper 1 module ApplicationHelper
  2 + def lighthouse_tracker? object
  3 + object.issue_tracker_type == "lighthouseapp"
  4 + end
2 end 5 end
app/models/app.rb
@@ -33,7 +33,7 @@ class App @@ -33,7 +33,7 @@ class App
33 accepts_nested_attributes_for :watchers, :allow_destroy => true, 33 accepts_nested_attributes_for :watchers, :allow_destroy => true,
34 :reject_if => proc { |attrs| attrs[:user_id].blank? && attrs[:email].blank? } 34 :reject_if => proc { |attrs| attrs[:user_id].blank? && attrs[:email].blank? }
35 accepts_nested_attributes_for :issue_tracker, :allow_destroy => true, 35 accepts_nested_attributes_for :issue_tracker, :allow_destroy => true,
36 - :reject_if => proc { |attrs| !%w( lighthouseapp ).include?(attrs[:issue_tracker_type]) } 36 + :reject_if => proc { |attrs| !%w( lighthouseapp redmine ).include?(attrs[:issue_tracker_type]) }
37 37
38 # Mongoid Bug: find(id) on association proxies returns an Enumerator 38 # Mongoid Bug: find(id) on association proxies returns an Enumerator
39 def self.find_by_id!(app_id) 39 def self.find_by_id!(app_id)
app/models/issue_tracker.rb
@@ -5,7 +5,7 @@ class IssueTracker @@ -5,7 +5,7 @@ class IssueTracker
5 include Rails.application.routes.url_helpers 5 include Rails.application.routes.url_helpers
6 default_url_options[:host] = Errbit::Application.config.action_mailer.default_url_options[:host] 6 default_url_options[:host] = Errbit::Application.config.action_mailer.default_url_options[:host]
7 7
8 - validate :check_lighthouseapp_params 8 + validate :check_params
9 9
10 embedded_in :app, :inverse_of => :issue_tracker 10 embedded_in :app, :inverse_of => :issue_tracker
11 11
@@ -15,6 +15,26 @@ class IssueTracker @@ -15,6 +15,26 @@ class IssueTracker
15 field :issue_tracker_type, :type => String, :default => 'lighthouseapp' 15 field :issue_tracker_type, :type => String, :default => 'lighthouseapp'
16 16
17 def create_issue err 17 def create_issue err
  18 + return create_lighthouseapp_issue err if issue_tracker_type == 'lighthouseapp'
  19 + create_redmine_issue err if issue_tracker_type == 'redmine'
  20 + end
  21 +
  22 + protected
  23 + def create_redmine_issue err
  24 + token = api_token
  25 + acc = account
  26 + RedmineClient::Base.configure do
  27 + self.token = token
  28 + self.site = acc
  29 + end
  30 + issue = RedmineClient::Issue.new(:project_id => project_id)
  31 + issue.subject = issue_title err
  32 + issue.description = self.class.redmine_body_template.result(binding)
  33 + issue.save!
  34 + err.update_attribute :issue_link, "#{RedmineClient::Issue.site.to_s.sub(/#{RedmineClient::Issue.site.path}$/, '')}#{RedmineClient::Issue.element_path(issue.id, :project_id => project_id)}".sub(/\.xml\?project_id=#{project_id}$/, "\?project_id=#{project_id}")
  35 + end
  36 +
  37 + def create_lighthouseapp_issue err
18 Lighthouse.account = account 38 Lighthouse.account = account
19 Lighthouse.token = api_token 39 Lighthouse.token = api_token
20 40
@@ -22,75 +42,38 @@ class IssueTracker @@ -22,75 +42,38 @@ class IssueTracker
22 Lighthouse::Ticket.site 42 Lighthouse::Ticket.site
23 43
24 ticket = Lighthouse::Ticket.new(:project_id => project_id) 44 ticket = Lighthouse::Ticket.new(:project_id => project_id)
25 - ticket.title = "[#{ err.environment }][#{ err.where }] #{err.message.to_s.truncate(100)}" 45 + ticket.title = issue_title err
26 46
27 - ticket.body = ""  
28 - ticket.body += "[See this exception on Errbit](#{ app_err_url err.app, err } \"See this exception on Errbit\")"  
29 - ticket.body += "\n"  
30 - if notice = err.notices.first  
31 - ticket.body += "# #{notice.message} #"  
32 - ticket.body += "\n"  
33 - ticket.body += "## Summary ##"  
34 - ticket.body += "\n"  
35 - if notice.request['url'].present?  
36 - ticket.body += "### URL ###"  
37 - ticket.body += "\n"  
38 - ticket.body += "[#{notice.request['url']}](#{notice.request['url']})"  
39 - ticket.body += "\n"  
40 - end  
41 - ticket.body += "### Where ###"  
42 - ticket.body += "\n"  
43 - ticket.body += notice.err.where  
44 - ticket.body += "\n"  
45 -  
46 - ticket.body += "### Occured ###"  
47 - ticket.body += "\n"  
48 - ticket.body += notice.created_at.to_s(:micro)  
49 - ticket.body += "\n"  
50 -  
51 - ticket.body += "### Similar ###"  
52 - ticket.body += "\n"  
53 - ticket.body += (notice.err.notices.count - 1).to_s  
54 - ticket.body += "\n"  
55 -  
56 - ticket.body += "## Params ##"  
57 - ticket.body += "\n"  
58 - ticket.body += "<code>#{pretty_hash(notice.params)}</code>"  
59 - ticket.body += "\n"  
60 -  
61 - ticket.body += "## Session ##"  
62 - ticket.body += "\n"  
63 - ticket.body += "<code>#{pretty_hash(notice.session)}</code>"  
64 - ticket.body += "\n"  
65 -  
66 - ticket.body += "## Backtrace ##"  
67 - ticket.body += "\n"  
68 - ticket.body += "<code>"  
69 - for line in notice.backtrace  
70 - ticket.body += "#{line['number']}: #{line['file'].sub(/^\[PROJECT_ROOT\]/, '')} -> **#{line['method']}**"  
71 - ticket.body += "\n"  
72 - end  
73 - ticket.body += "</code>"  
74 - ticket.body += "\n"  
75 -  
76 - ticket.body += "## Environment ##"  
77 - ticket.body += "\n"  
78 - for key, val in notice.env_vars  
79 - ticket.body += "#{key}: #{val}"  
80 - end  
81 - ticket.body += "\n"  
82 - end 47 + ticket.body = self.class.lighthouseapp_body_template.result(binding)
83 48
84 ticket.tags << "errbit" 49 ticket.tags << "errbit"
85 ticket.save! 50 ticket.save!
86 err.update_attribute :issue_link, "#{Lighthouse::Ticket.site.to_s.sub(/#{Lighthouse::Ticket.site.path}$/, '')}#{Lighthouse::Ticket.element_path(ticket.id, :project_id => project_id)}".sub(/\.xml$/, '') 51 err.update_attribute :issue_link, "#{Lighthouse::Ticket.site.to_s.sub(/#{Lighthouse::Ticket.site.path}$/, '')}#{Lighthouse::Ticket.element_path(ticket.id, :project_id => project_id)}".sub(/\.xml$/, '')
87 end 52 end
88 53
89 - protected  
90 - def check_lighthouseapp_params 54 + def issue_title err
  55 + "[#{ err.environment }][#{ err.where }] #{err.message.to_s.truncate(100)}"
  56 + end
  57 +
  58 + def check_params
91 blank_flags = %w( api_token project_id account ).map {|m| self[m].blank? } 59 blank_flags = %w( api_token project_id account ).map {|m| self[m].blank? }
92 if blank_flags.any? && !blank_flags.all? 60 if blank_flags.any? && !blank_flags.all?
93 - errors.add(:base, "You must specify your Lighthouseapp account, token and project id") 61 + message = if issue_tracker_type == 'lighthouseapp'
  62 + "You must specify your Lighthouseapp account, api token and project id"
  63 + else
  64 + "You must specify your Redmine url, api token and project id"
  65 + end
  66 + errors.add(:base, message)
  67 + end
  68 + end
  69 +
  70 + class << self
  71 + def lighthouseapp_body_template
  72 + @@lighthouseapp_body_template ||= ERB.new(File.read(Rails.root + "app/views/errs/lighthouseapp_body.txt.erb").gsub(/^\s*/, ''))
  73 + end
  74 +
  75 + def redmine_body_template
  76 + @@redmine_body_template ||= ERB.new(File.read(Rails.root + "app/views/errs/redmine_body.txt.erb"))
94 end 77 end
95 end 78 end
96 end 79 end
app/views/apps/_fields.html.haml
@@ -33,15 +33,24 @@ @@ -33,15 +33,24 @@
33 %fieldset 33 %fieldset
34 %legend Issue tracker 34 %legend Issue tracker
35 = f.fields_for :issue_tracker do |w| 35 = f.fields_for :issue_tracker do |w|
36 - %div.watcher.nested 36 + %div.issue_tracker.nested
37 %div.choose 37 %div.choose
38 = w.radio_button :issue_tracker_type, :lighthouseapp 38 = w.radio_button :issue_tracker_type, :lighthouseapp
39 = label_tag :issue_tracker_type_lighthouseapp, 'Lighthouse', :for => label_for_attr(w, 'issue_tracker_type_lighthouseapp') 39 = label_tag :issue_tracker_type_lighthouseapp, 'Lighthouse', :for => label_for_attr(w, 'issue_tracker_type_lighthouseapp')
40 - %div.lighthouseapp{:class => 'choosen'} 40 + = w.radio_button :issue_tracker_type, :redmine
  41 + = label_tag :issue_tracker_type_redmine, 'Redmine', :for => label_for_attr(w, 'issue_tracker_type_redmine')
  42 + %div.tracker_params{:class => lighthouse_tracker?(w.object) ? 'choosen' : nil}
41 = w.label :account, "Account" 43 = w.label :account, "Account"
42 - = w.text_field :account 44 + = w.text_field :account, :placeholder => "abc from abc.lighthouseapp.com"
  45 + = w.label :api_token, "API token"
  46 + = w.text_field :api_token, :placeholder => "API Token for your account"
  47 + = w.label :project_id, "Project ID"
  48 + = w.text_field :project_id, :placeholder => "123 from abc from abc.lighthouseapp.com/projects/123"
  49 + %div.tracker_params{:class => lighthouse_tracker?(w.object) ? nil : 'choosen'}
  50 + = w.label :account, "Redmine URL"
  51 + = w.text_field :account, :placeholder => "like http://www.redmine.org/"
43 = w.label :api_token, "API token" 52 = w.label :api_token, "API token"
44 - = w.text_field :api_token 53 + = w.text_field :api_token, :placeholder => "API Token for your account"
45 = w.label :project_id, "Project ID" 54 = w.label :project_id, "Project ID"
46 = w.text_field :project_id 55 = w.text_field :project_id
47 56
app/views/errs/lighthouseapp_body.txt.erb 0 → 100644
@@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
  1 +[See this exception on Errbit](<%= app_err_url err.app, err %> "See this exception on Errbit")
  2 +<% if notice = err.notices.first %>
  3 + # <%= notice.message %> #
  4 + ## Summary ##
  5 + <% if notice.request['url'].present? %>
  6 + ### URL ###
  7 + [<%= notice.request['url'] %>](<%= notice.request['url'] %>)"
  8 + <% end %>
  9 + ### Where ###
  10 + <%= notice.err.where %>
  11 +
  12 + ### Occured ###
  13 + <%= notice.created_at.to_s(:micro) %>
  14 +
  15 + ### Similar ###
  16 + <%= (notice.err.notices.count - 1).to_s %>
  17 +
  18 + ## Params ##
  19 + <code><%= pretty_hash(notice.params) %></code>
  20 +
  21 + ## Session ##
  22 + <code><%= pretty_hash(notice.session) %></code>
  23 +
  24 + ## Backtrace ##
  25 + <code>
  26 + <% for line in notice.backtrace %><%= line['number'] %>: <%= line['file'].sub(/^\[PROJECT_ROOT\]/, '') %> -> **<%= line['method'] %>**
  27 + <% end %>
  28 + </code>
  29 +
  30 + ## Environment ##
  31 + <% for key, val in notice.env_vars %>
  32 + <%= key %>: <%= val %>
  33 + <% end %>
  34 +<% end %>
app/views/errs/redmine_body.txt.erb 0 → 100644
@@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
  1 +"See this exception on Errbit":<%= app_err_url err.app, err %>
  2 +<% if notice = err.notices.first %>
  3 +h1. <%= notice.message %>
  4 +
  5 +h2. Summary
  6 +<% if notice.request['url'].present? %>
  7 +h3. URL
  8 +
  9 +"<%= notice.request['url'] %>":<%= notice.request['url'] %>
  10 +<% end %>
  11 +h3. Where
  12 +
  13 +<%= notice.err.where %>
  14 +
  15 +h3. Occured
  16 +
  17 +<%= notice.created_at.to_s(:micro) %>
  18 +
  19 +h3. Similar
  20 +
  21 +<%= (notice.err.notices.count - 1).to_s %>
  22 +
  23 +h2. Params
  24 +
  25 +<pre><%= pretty_hash(notice.params) %></pre>
  26 +
  27 +h2. Session
  28 +
  29 +<pre><%= pretty_hash(notice.session) %></pre>
  30 +
  31 +h2. Backtrace
  32 +
  33 +<pre>
  34 +<% for line in notice.backtrace %><%= line['number'] %>: <%= line['file'].sub(/^\[PROJECT_ROOT\]/, '') %> -> *<%= line['method'] %>*
  35 +<% end %>
  36 +</pre>
  37 +
  38 +h2. Environment
  39 +
  40 +<pre>
  41 +<% for key, val in notice.env_vars %>
  42 +<%= key %>: <%= val %>
  43 +<% end %>
  44 +</pre>
  45 +<% end %>
app/views/errs/show.html.haml
@@ -12,10 +12,10 @@ @@ -12,10 +12,10 @@
12 - content_for :action_bar do 12 - content_for :action_bar do
13 - if @err.app.issue_tracker 13 - if @err.app.issue_tracker
14 - if @err.issue_link.blank? 14 - if @err.issue_link.blank?
15 - %span= link_to 'create issue', create_issue_app_err_path(@app, @err), :method => :post, :class => 'create-issue' 15 + %span= link_to 'create issue', create_issue_app_err_path(@app, @err), :method => :post, :class => "#{@app.issue_tracker.issue_tracker_type}_create create-issue"
16 - else 16 - else
17 - %span= link_to 'go to issue', @err.issue_link, :class => 'goto-issue'  
18 - = link_to 'clear issue', clear_issue_app_err_path(@app, @err), :method => :delete, :confirm => "Clear err issues?", :class => 'clear-issue' 17 + %span= link_to 'go to issue', @err.issue_link, :class => "#{@app.issue_tracker.issue_tracker_type}_create goto-issue"
  18 + = link_to 'clear issue', clear_issue_app_err_path(@app, @err), :method => :delete, :confirm => "Clear err issues?", :class => "clear-issue"
19 - if @err.unresolved? 19 - if @err.unresolved?
20 %span= link_to 'resolve', resolve_app_err_path(@app, @err), :method => :put, :confirm => err_confirm, :class => 'resolve' 20 %span= link_to 'resolve', resolve_app_err_path(@app, @err), :method => :put, :confirm => err_confirm, :class => 'resolve'
21 21
public/images/lighthouseapp_create.png 0 → 100644

1.57 KB

public/images/lighthousehouseapp_goto.png 0 → 100644

1.61 KB

public/images/redmine_create.png 0 → 100644

1.47 KB

public/images/redmine_goto.png 0 → 100644

1.52 KB

public/javascripts/form.js
@@ -3,6 +3,9 @@ $(function(){ @@ -3,6 +3,9 @@ $(function(){
3 3
4 if($('div.watcher.nested').length) 4 if($('div.watcher.nested').length)
5 activateWatcherTypeSelector(); 5 activateWatcherTypeSelector();
  6 +
  7 + if($('div.issue_tracker.nested').length)
  8 + activateIssueTrackerTypeSelector();
6 }); 9 });
7 10
8 function activateNestedForms() { 11 function activateNestedForms() {
@@ -67,4 +70,20 @@ function activateWatcherTypeSelector() { @@ -67,4 +70,20 @@ function activateWatcherTypeSelector() {
67 wrapper.find('div.choosen').removeClass('choosen'); 70 wrapper.find('div.choosen').removeClass('choosen');
68 wrapper.find('div.'+choosen).addClass('choosen'); 71 wrapper.find('div.'+choosen).addClass('choosen');
69 }); 72 });
  73 +}
  74 +
  75 +function activateIssueTrackerTypeSelector() {
  76 + var not_choosen = $("div.tracker_params").filter(function () {
  77 + return !$(this).hasClass("choosen");
  78 + });
  79 + window.hiddenTracker = not_choosen.html();
  80 + not_choosen.remove();
  81 + $('div.issue_tracker input[name*=issue_tracker_type]').live('click', function(){
  82 + var choosen = $(this).val();
  83 + var wrapper = $(this).closest('.nested');
  84 + var tmp;
  85 + tmp = wrapper.find('div.choosen').html();
  86 + wrapper.find('div.choosen').html(window.hiddenTracker);
  87 + window.hiddenTracker = tmp;
  88 + });
70 } 89 }
71 \ No newline at end of file 90 \ No newline at end of file
public/stylesheets/application.css
@@ -501,14 +501,15 @@ a.button.active { @@ -501,14 +501,15 @@ a.button.active {
501 margin-right: 14px; 501 margin-right: 14px;
502 } 502 }
503 503
504 -/* Watchers Form */  
505 -div.nested.watcher .user, div.nested.watcher .email { 504 +/* Watchers and Issue Tracker Forms */
  505 +div.nested.watcher .user, div.nested.watcher .email, div.issue_tracker.nested .lighthouseapp, div.issue_tracker.nested .redmine {
506 display: none; 506 display: none;
507 } 507 }
508 -div.nested.watcher .choosen { 508 +div.nested.watcher .choosen, div.nested.issue_tracker .choosen {
509 display: block; 509 display: block;
510 } 510 }
511 -div.nested.watcher .choose { 511 +
  512 +div.nested.watcher .choose, div.nested.issue_tracker .choose {
512 margin-bottom: 0.5em; 513 margin-bottom: 0.5em;
513 } 514 }
514 515
@@ -578,6 +579,22 @@ table.errs tr.resolved td &gt; * { @@ -578,6 +579,22 @@ table.errs tr.resolved td &gt; * {
578 background: transparent url(images/icons/thumbs-up.png) 6px 5px no-repeat; 579 background: transparent url(images/icons/thumbs-up.png) 6px 5px no-repeat;
579 } 580 }
580 581
  582 +#action-bar a.lighthouseapp_create {
  583 + background: transparent url(/images/lighthouseapp_create.png) 6px 5px no-repeat;
  584 +}
  585 +
  586 +#action-bar a.redmine_create {
  587 + background: transparent url(/images/redmine_create.png) 6px 5px no-repeat;
  588 +}
  589 +
  590 +#action-bar a.lighthouseapp_goto {
  591 + background: transparent url(/images/lighthouseapp_goto.png) 6px 5px no-repeat;
  592 +}
  593 +
  594 +#action-bar a.redmine_goto {
  595 + background: transparent url(/images/redmine_goto.png) 6px 5px no-repeat;
  596 +}
  597 +
581 /* Notices Pagination */ 598 /* Notices Pagination */
582 .notice-pagination { 599 .notice-pagination {
583 float: left; 600 float: left;
spec/controllers/apps_controller_spec.rb
@@ -211,17 +211,32 @@ describe AppsController do @@ -211,17 +211,32 @@ describe AppsController do
211 @app.reload 211 @app.reload
212 212
213 @app.issue_tracker.should be_nil 213 @app.issue_tracker.should be_nil
214 - response.body.should match(/You must specify your Lighthouseapp account, token and project id/) 214 + response.body.should match(/You must specify your Lighthouseapp account, api token and project id/)
  215 + end
  216 + end
  217 +
  218 + context "redmine" do
  219 + it "should save tracker params" do
  220 + put :update, :id => @app.id, :app => { :issue_tracker_attributes => {
  221 + :issue_tracker_type => 'redmine', :project_id => '1234', :api_token => '123123', :account => 'http://myapp.com'
  222 + } }
  223 + @app.reload
  224 +
  225 + tracker = @app.issue_tracker
  226 + tracker.issue_tracker_type.should == 'redmine'
  227 + tracker.project_id.should == '1234'
  228 + tracker.api_token.should == '123123'
  229 + tracker.account.should == 'http://myapp.com'
215 end 230 end
216 231
217 it "should show validation notice when sufficient params are not present" do 232 it "should show validation notice when sufficient params are not present" do
218 put :update, :id => @app.id, :app => { :issue_tracker_attributes => { 233 put :update, :id => @app.id, :app => { :issue_tracker_attributes => {
219 - :issue_tracker_type => 'lighthouseapp', :project_id => '1234', :api_token => '123123' 234 + :issue_tracker_type => 'redmine', :project_id => '1234', :api_token => '123123'
220 } } 235 } }
221 @app.reload 236 @app.reload
222 237
223 @app.issue_tracker.should be_nil 238 @app.issue_tracker.should be_nil
224 - response.body.should match(/You must specify your Lighthouseapp account, token and project id/) 239 + response.body.should match(/You must specify your Redmine url, api token and project id/)
225 end 240 end
226 end 241 end
227 end 242 end
spec/controllers/errs_controller_spec.rb
@@ -253,6 +253,38 @@ describe ErrsController do @@ -253,6 +253,38 @@ describe ErrsController do
253 err.issue_link.should == @issue_link.sub(/\.xml$/, '') 253 err.issue_link.should == @issue_link.sub(/\.xml$/, '')
254 end 254 end
255 end 255 end
  256 +
  257 + context "redmine tracker" do
  258 + let(:notice) { Factory :notice }
  259 + let(:tracker) { Factory :redmine_tracker, :app => notice.err.app }
  260 + let(:err) { notice.err }
  261 +
  262 + before(:each) do
  263 + number = 5
  264 + @issue_link = "#{tracker.account}/issues/#{number}.xml?project_id=#{tracker.project_id}"
  265 + body = "<issue><subject>my subject</subject><id>#{number}</id></issue>"
  266 + stub_request(:post, "#{tracker.account}/issues.xml").to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body )
  267 +
  268 + post :create_issue, :app_id => err.app.id, :id => err.id
  269 + err.reload
  270 + end
  271 +
  272 + it "should make request to Redmine with err params" do
  273 + requested = have_requested(:post, "#{tracker.account}/issues.xml")
  274 + WebMock.should requested.with(:headers => {'X-Redmine-API-Key' => tracker.api_token})
  275 + WebMock.should requested.with(:body => /<project-id>#{tracker.project_id}<\/project-id>/)
  276 + WebMock.should requested.with(:body => /<subject>\[#{ err.environment }\]\[#{err.where}\] #{err.message.to_s.truncate(100)}<\/subject>/)
  277 + WebMock.should requested.with(:body => /<description>.+<\/description>/m)
  278 + end
  279 +
  280 + it "should redirect to err page" do
  281 + response.should redirect_to( app_err_path(err.app, err) )
  282 + end
  283 +
  284 + it "should create issue link for err" do
  285 + err.issue_link.should == @issue_link.sub(/\.xml/, '')
  286 + end
  287 + end
256 end 288 end
257 289
258 context "absent issue tracker" do 290 context "absent issue tracker" do
spec/factories/issue_tracker_factories.rb
@@ -4,4 +4,9 @@ Factory.define :lighthouseapp_tracker, :class =&gt; IssueTracker do |e| @@ -4,4 +4,9 @@ Factory.define :lighthouseapp_tracker, :class =&gt; IssueTracker do |e|
4 e.api_token { Factory.next :word } 4 e.api_token { Factory.next :word }
5 e.project_id { Factory.next :word } 5 e.project_id { Factory.next :word }
6 e.association :app, :factory => :app 6 e.association :app, :factory => :app
  7 +end
  8 +
  9 +Factory.define :redmine_tracker, :parent => :lighthouseapp_tracker do |e|
  10 + e.issue_tracker_type 'redmine'
  11 + e.account { "http://#{Factory.next(:word)}.com" }
7 end 12 end
8 \ No newline at end of file 13 \ No newline at end of file