Commit bd17fa780fb7f38eb201c96fc4a87f9f8e597a55
Exists in
master
and in
1 other branch
Merge branch 'feature/redmine'
Showing
20 changed files
with
257 additions
and
78 deletions
Show diff stats
Gemfile
| @@ -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' |
Gemfile.lock
| 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 |
README.md
| @@ -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
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 |
| @@ -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 %> |
| @@ -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 |
1.57 KB
1.61 KB
1.47 KB
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 > * { | @@ -578,6 +579,22 @@ table.errs tr.resolved td > * { | ||
| 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 => IssueTracker do |e| | @@ -4,4 +4,9 @@ Factory.define :lighthouseapp_tracker, :class => 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 |