Commit ba17fba9c5abc4a3d73b2c0a5c4bdb5b45b36271
Exists in
master
and in
1 other branch
Merge branch 'feature/lighthouse'
Showing
21 changed files
with
400 additions
and
11 deletions
Show diff stats
Gemfile
| @@ -6,6 +6,7 @@ gem 'mongoid', '~> 2.0.0.rc.7' | @@ -6,6 +6,7 @@ gem 'mongoid', '~> 2.0.0.rc.7' | ||
| 6 | gem 'haml' | 6 | 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 | 10 | ||
| 10 | platform :ruby do | 11 | platform :ruby do |
| 11 | gem 'bson_ext', '~> 1.2' | 12 | gem 'bson_ext', '~> 1.2' |
| @@ -13,6 +14,7 @@ end | @@ -13,6 +14,7 @@ end | ||
| 13 | 14 | ||
| 14 | group :development, :test do | 15 | group :development, :test do |
| 15 | gem 'rspec-rails', '~> 2.5' | 16 | gem 'rspec-rails', '~> 2.5' |
| 17 | + gem 'webmock', :require => false | ||
| 16 | end | 18 | end |
| 17 | 19 | ||
| 18 | group :test do | 20 | group :test do |
Gemfile.lock
| @@ -28,11 +28,13 @@ GEM | @@ -28,11 +28,13 @@ GEM | ||
| 28 | activemodel (= 3.0.5) | 28 | activemodel (= 3.0.5) |
| 29 | activesupport (= 3.0.5) | 29 | activesupport (= 3.0.5) |
| 30 | activesupport (3.0.5) | 30 | activesupport (3.0.5) |
| 31 | + addressable (2.2.4) | ||
| 31 | arel (2.0.9) | 32 | arel (2.0.9) |
| 32 | bcrypt-ruby (2.1.4) | 33 | bcrypt-ruby (2.1.4) |
| 33 | bson (1.2.4) | 34 | bson (1.2.4) |
| 34 | bson_ext (1.2.4) | 35 | bson_ext (1.2.4) |
| 35 | builder (2.1.2) | 36 | builder (2.1.2) |
| 37 | + crack (0.1.8) | ||
| 36 | database_cleaner (0.6.5) | 38 | database_cleaner (0.6.5) |
| 37 | devise (1.1.8) | 39 | devise (1.1.8) |
| 38 | bcrypt-ruby (~> 2.1.2) | 40 | bcrypt-ruby (~> 2.1.2) |
| @@ -46,6 +48,9 @@ GEM | @@ -46,6 +48,9 @@ GEM | ||
| 46 | railties (>= 3.0.0) | 48 | railties (>= 3.0.0) |
| 47 | haml (3.0.25) | 49 | haml (3.0.25) |
| 48 | i18n (0.5.0) | 50 | i18n (0.5.0) |
| 51 | + lighthouse-api (2.0) | ||
| 52 | + activeresource (>= 3.0.0) | ||
| 53 | + activesupport (>= 3.0.0) | ||
| 49 | mail (2.2.15) | 54 | mail (2.2.15) |
| 50 | activesupport (>= 2.3.6) | 55 | activesupport (>= 2.3.6) |
| 51 | i18n (>= 0.4.0) | 56 | i18n (>= 0.4.0) |
| @@ -99,6 +104,9 @@ GEM | @@ -99,6 +104,9 @@ GEM | ||
| 99 | tzinfo (0.3.25) | 104 | tzinfo (0.3.25) |
| 100 | warden (1.0.3) | 105 | warden (1.0.3) |
| 101 | rack (>= 1.0.0) | 106 | rack (>= 1.0.0) |
| 107 | + webmock (1.6.2) | ||
| 108 | + addressable (>= 2.2.2) | ||
| 109 | + crack (>= 0.1.7) | ||
| 102 | will_paginate (3.0.pre2) | 110 | will_paginate (3.0.pre2) |
| 103 | 111 | ||
| 104 | PLATFORMS | 112 | PLATFORMS |
| @@ -110,9 +118,11 @@ DEPENDENCIES | @@ -110,9 +118,11 @@ DEPENDENCIES | ||
| 110 | devise (~> 1.1.8) | 118 | devise (~> 1.1.8) |
| 111 | factory_girl_rails | 119 | factory_girl_rails |
| 112 | haml | 120 | haml |
| 121 | + lighthouse-api | ||
| 113 | mongoid (~> 2.0.0.rc.7) | 122 | mongoid (~> 2.0.0.rc.7) |
| 114 | nokogiri | 123 | nokogiri |
| 115 | rails (= 3.0.5) | 124 | rails (= 3.0.5) |
| 116 | rspec (~> 2.5) | 125 | rspec (~> 2.5) |
| 117 | rspec-rails (~> 2.5) | 126 | rspec-rails (~> 2.5) |
| 127 | + webmock | ||
| 118 | will_paginate | 128 | will_paginate |
README.md
| @@ -89,6 +89,13 @@ for you. Checkout [Hoptoad](http://hoptoadapp.com) from the guys over at | @@ -89,6 +89,13 @@ for you. Checkout [Hoptoad](http://hoptoadapp.com) from the guys over at | ||
| 89 | 89 | ||
| 90 | 4. Enjoy! | 90 | 4. Enjoy! |
| 91 | 91 | ||
| 92 | +Lighthouseapp integration | ||
| 93 | +------------------------- | ||
| 94 | + | ||
| 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. | ||
| 97 | +* Project id is number identifier of your project, i.e. **73466** for project at http://litcafe.lighthouseapp.com/projects/73466-face/overview | ||
| 98 | + | ||
| 92 | TODO | 99 | TODO |
| 93 | ---- | 100 | ---- |
| 94 | 101 |
app/controllers/apps_controller.rb
| @@ -22,10 +22,12 @@ class AppsController < ApplicationController | @@ -22,10 +22,12 @@ class AppsController < ApplicationController | ||
| 22 | def new | 22 | def new |
| 23 | @app = App.new | 23 | @app = App.new |
| 24 | @app.watchers.build | 24 | @app.watchers.build |
| 25 | + @app.issue_tracker = IssueTracker.new | ||
| 25 | end | 26 | end |
| 26 | 27 | ||
| 27 | def edit | 28 | def edit |
| 28 | @app.watchers.build if @app.watchers.none? | 29 | @app.watchers.build if @app.watchers.none? |
| 30 | + @app.issue_tracker = IssueTracker.new if @app.issue_tracker.nil? | ||
| 29 | end | 31 | end |
| 30 | 32 | ||
| 31 | def create | 33 | def create |
app/controllers/errs_controller.rb
| 1 | class ErrsController < ApplicationController | 1 | class ErrsController < ApplicationController |
| 2 | 2 | ||
| 3 | before_filter :find_app, :except => [:index, :all] | 3 | before_filter :find_app, :except => [:index, :all] |
| 4 | + before_filter :find_err, :except => [:index, :all] | ||
| 4 | 5 | ||
| 5 | def index | 6 | def index |
| 6 | app_scope = current_user.admin? ? App.all : current_user.apps | 7 | app_scope = current_user.admin? ? App.all : current_user.apps |
| @@ -20,16 +21,32 @@ class ErrsController < ApplicationController | @@ -20,16 +21,32 @@ class ErrsController < ApplicationController | ||
| 20 | end | 21 | end |
| 21 | 22 | ||
| 22 | def show | 23 | def show |
| 23 | - @err = @app.errs.find(params[:id]) | ||
| 24 | page = (params[:notice] || @err.notices.count) | 24 | page = (params[:notice] || @err.notices.count) |
| 25 | page = 1 if page.to_i.zero? | 25 | page = 1 if page.to_i.zero? |
| 26 | @notices = @err.notices.ordered.paginate(:page => page, :per_page => 1) | 26 | @notices = @err.notices.ordered.paginate(:page => page, :per_page => 1) |
| 27 | @notice = @notices.first | 27 | @notice = @notices.first |
| 28 | end | 28 | end |
| 29 | + | ||
| 30 | + def create_issue | ||
| 31 | + set_tracker_params | ||
| 32 | + | ||
| 33 | + if @app.issue_tracker | ||
| 34 | + @app.issue_tracker.create_issue @err | ||
| 35 | + else | ||
| 36 | + flash[:error] = "This up has no issue tracker setup." | ||
| 37 | + end | ||
| 38 | + redirect_to app_err_path(@app, @err) | ||
| 39 | + rescue ActiveResource::ConnectionError | ||
| 40 | + 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 | + end | ||
| 43 | + | ||
| 44 | + def clear_issue | ||
| 45 | + @err.update_attribute :issue_link, nil | ||
| 46 | + redirect_to app_err_path(@app, @err) | ||
| 47 | + end | ||
| 29 | 48 | ||
| 30 | def resolve | 49 | def resolve |
| 31 | - @err = @app.errs.find(params[:id]) | ||
| 32 | - | ||
| 33 | # Deal with bug in mogoid where find is returning an Enumberable obj | 50 | # Deal with bug in mogoid where find is returning an Enumberable obj |
| 34 | @err = @err.first if @err.respond_to?(:first) | 51 | @err = @err.first if @err.respond_to?(:first) |
| 35 | 52 | ||
| @@ -51,5 +68,15 @@ class ErrsController < ApplicationController | @@ -51,5 +68,15 @@ class ErrsController < ApplicationController | ||
| 51 | # apparently finding by 'watchers.email' and 'id' is broken | 68 | # apparently finding by 'watchers.email' and 'id' is broken |
| 52 | raise(Mongoid::Errors::DocumentNotFound.new(App,@app.id)) unless current_user.admin? || current_user.watching?(@app) | 69 | raise(Mongoid::Errors::DocumentNotFound.new(App,@app.id)) unless current_user.admin? || current_user.watching?(@app) |
| 53 | end | 70 | end |
| 71 | + | ||
| 72 | + def find_err | ||
| 73 | + @err = @app.errs.find(params[:id]) | ||
| 74 | + end | ||
| 75 | + | ||
| 76 | + def set_tracker_params | ||
| 77 | + IssueTracker.default_url_options[:host] = request.host | ||
| 78 | + IssueTracker.default_url_options[:port] = request.port | ||
| 79 | + IssueTracker.default_url_options[:protocol] = request.scheme | ||
| 80 | + end | ||
| 54 | 81 | ||
| 55 | end | 82 | end |
app/helpers/form_helper.rb
| @@ -6,7 +6,7 @@ module FormHelper | @@ -6,7 +6,7 @@ module FormHelper | ||
| 6 | content_tag(:div, :class => 'error-messages') do | 6 | content_tag(:div, :class => 'error-messages') do |
| 7 | body = content_tag(:h2, 'Dang. The following errors are keeping this from being a success.') | 7 | body = content_tag(:h2, 'Dang. The following errors are keeping this from being a success.') |
| 8 | body += content_tag(:ul) do | 8 | body += content_tag(:ul) do |
| 9 | - document.errors.full_messages.inject('') {|errs, msg| errs += content_tag(:li, msg) } | 9 | + document.errors.full_messages.inject('') {|errs, msg| errs += content_tag(:li, h(msg)) }.html_safe |
| 10 | end | 10 | end |
| 11 | end | 11 | end |
| 12 | end | 12 | end |
app/models/app.rb
| @@ -16,6 +16,7 @@ class App | @@ -16,6 +16,7 @@ class App | ||
| 16 | 16 | ||
| 17 | embeds_many :watchers | 17 | embeds_many :watchers |
| 18 | embeds_many :deploys | 18 | embeds_many :deploys |
| 19 | + embeds_one :issue_tracker | ||
| 19 | references_many :errs, :dependent => :destroy | 20 | references_many :errs, :dependent => :destroy |
| 20 | 21 | ||
| 21 | before_validation :generate_api_key, :on => :create | 22 | before_validation :generate_api_key, :on => :create |
| @@ -24,9 +25,12 @@ class App | @@ -24,9 +25,12 @@ class App | ||
| 24 | validates_uniqueness_of :name, :allow_blank => true | 25 | validates_uniqueness_of :name, :allow_blank => true |
| 25 | validates_uniqueness_of :api_key, :allow_blank => true | 26 | validates_uniqueness_of :api_key, :allow_blank => true |
| 26 | validates_associated :watchers | 27 | validates_associated :watchers |
| 28 | + validate :check_issue_tracker | ||
| 27 | 29 | ||
| 28 | accepts_nested_attributes_for :watchers, :allow_destroy => true, | 30 | accepts_nested_attributes_for :watchers, :allow_destroy => true, |
| 29 | :reject_if => proc { |attrs| attrs[:user_id].blank? && attrs[:email].blank? } | 31 | :reject_if => proc { |attrs| attrs[:user_id].blank? && attrs[:email].blank? } |
| 32 | + accepts_nested_attributes_for :issue_tracker, :allow_destroy => true, | ||
| 33 | + :reject_if => proc { |attrs| !%w( lighthouseapp ).include?(attrs[:issue_tracker_type]) } | ||
| 30 | 34 | ||
| 31 | # Mongoid Bug: find(id) on association proxies returns an Enumerator | 35 | # Mongoid Bug: find(id) on association proxies returns an Enumerator |
| 32 | def self.find_by_id!(app_id) | 36 | def self.find_by_id!(app_id) |
| @@ -46,5 +50,13 @@ class App | @@ -46,5 +50,13 @@ class App | ||
| 46 | def generate_api_key | 50 | def generate_api_key |
| 47 | self.api_key ||= ActiveSupport::SecureRandom.hex | 51 | self.api_key ||= ActiveSupport::SecureRandom.hex |
| 48 | end | 52 | end |
| 49 | - | 53 | + |
| 54 | + def check_issue_tracker | ||
| 55 | + if issue_tracker.present? | ||
| 56 | + issue_tracker.valid? | ||
| 57 | + issue_tracker.errors.full_messages.each do |error| | ||
| 58 | + errors[:base] << error | ||
| 59 | + end if issue_tracker.errors | ||
| 60 | + end | ||
| 61 | + end | ||
| 50 | end | 62 | end |
app/models/err.rb
| @@ -9,6 +9,7 @@ class Err | @@ -9,6 +9,7 @@ class Err | ||
| 9 | field :fingerprint | 9 | field :fingerprint |
| 10 | field :last_notice_at, :type => DateTime | 10 | field :last_notice_at, :type => DateTime |
| 11 | field :resolved, :type => Boolean, :default => false | 11 | field :resolved, :type => Boolean, :default => false |
| 12 | + field :issue_link, :type => String | ||
| 12 | 13 | ||
| 13 | index :last_notice_at | 14 | index :last_notice_at |
| 14 | 15 |
| @@ -0,0 +1,96 @@ | @@ -0,0 +1,96 @@ | ||
| 1 | +class IssueTracker | ||
| 2 | + include Mongoid::Document | ||
| 3 | + include Mongoid::Timestamps | ||
| 4 | + include HashHelper | ||
| 5 | + include Rails.application.routes.url_helpers | ||
| 6 | + default_url_options[:host] = Errbit::Application.config.action_mailer.default_url_options[:host] | ||
| 7 | + | ||
| 8 | + validate :check_lighthouseapp_params | ||
| 9 | + | ||
| 10 | + embedded_in :app, :inverse_of => :issue_tracker | ||
| 11 | + | ||
| 12 | + field :account, :type => String | ||
| 13 | + field :api_token, :type => String | ||
| 14 | + field :project_id, :type => String | ||
| 15 | + field :issue_tracker_type, :type => String, :default => 'lighthouseapp' | ||
| 16 | + | ||
| 17 | + def create_issue err | ||
| 18 | + Lighthouse.account = account | ||
| 19 | + Lighthouse.token = api_token | ||
| 20 | + | ||
| 21 | + # updating lighthouse account | ||
| 22 | + Lighthouse::Ticket.site | ||
| 23 | + | ||
| 24 | + ticket = Lighthouse::Ticket.new(:project_id => project_id) | ||
| 25 | + ticket.title = "[#{ err.environment }][#{ err.where }] #{err.message.to_s.truncate(100)}" | ||
| 26 | + | ||
| 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 | ||
| 83 | + | ||
| 84 | + ticket.tags << "errbit" | ||
| 85 | + 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$/, '') | ||
| 87 | + end | ||
| 88 | + | ||
| 89 | + protected | ||
| 90 | + def check_lighthouseapp_params | ||
| 91 | + blank_flags = %w( api_token project_id account ).map {|m| self[m].blank? } | ||
| 92 | + if blank_flags.any? && !blank_flags.all? | ||
| 93 | + errors.add(:base, "You must specify your Lighthouseapp account, token and project id") | ||
| 94 | + end | ||
| 95 | + end | ||
| 96 | +end |
app/views/apps/_fields.html.haml
| @@ -21,3 +21,20 @@ | @@ -21,3 +21,20 @@ | ||
| 21 | = w.select :user_id, User.all.map{|u| [u.name,u.id.to_s]}, :include_blank => '-- Select a User --' | 21 | = w.select :user_id, User.all.map{|u| [u.name,u.id.to_s]}, :include_blank => '-- Select a User --' |
| 22 | %div.email{:class => w.object.email.present? ? 'choosen' : nil} | 22 | %div.email{:class => w.object.email.present? ? 'choosen' : nil} |
| 23 | = w.text_field :email | 23 | = w.text_field :email |
| 24 | + | ||
| 25 | +%fieldset | ||
| 26 | + %legend Issue tracker | ||
| 27 | + = f.fields_for :issue_tracker do |w| | ||
| 28 | + %div.watcher.nested | ||
| 29 | + %div.choose | ||
| 30 | + = w.radio_button :issue_tracker_type, :lighthouseapp | ||
| 31 | + = label_tag :issue_tracker_type_lighthouseapp, 'Lighthouse', :for => label_for_attr(w, 'issue_tracker_type_lighthouseapp') | ||
| 32 | + %div.lighthouseapp{:class => 'choosen'} | ||
| 33 | + = w.label :account, "Account" | ||
| 34 | + = w.text_field :account | ||
| 35 | + = w.label :api_token, "API token" | ||
| 36 | + = w.text_field :api_token | ||
| 37 | + = w.label :project_id, "Project ID" | ||
| 38 | + = w.text_field :project_id | ||
| 39 | + | ||
| 40 | + |
app/views/errs/show.html.haml
| @@ -10,7 +10,14 @@ | @@ -10,7 +10,14 @@ | ||
| 10 | %strong Last Notice: | 10 | %strong Last Notice: |
| 11 | = last_notice_at(@err).to_s(:micro) | 11 | = last_notice_at(@err).to_s(:micro) |
| 12 | - content_for :action_bar do | 12 | - content_for :action_bar do |
| 13 | - %span= link_to 'resolve', resolve_app_err_path(@app, @err), :method => :put, :confirm => err_confirm, :class => 'resolve' if @err.unresolved? | 13 | + - if @err.app.issue_tracker |
| 14 | + - if @err.issue_link.blank? | ||
| 15 | + %span= link_to 'create issue', create_issue_app_err_path(@app, @err), :method => :post, :class => 'create-issue' | ||
| 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' | ||
| 19 | + - if @err.unresolved? | ||
| 20 | + %span= link_to 'resolve', resolve_app_err_path(@app, @err), :method => :put, :confirm => err_confirm, :class => 'resolve' | ||
| 14 | 21 | ||
| 15 | %h4= @notice.try(:message) | 22 | %h4= @notice.try(:message) |
| 16 | 23 |
config/environments/development.rb
| @@ -16,6 +16,7 @@ Errbit::Application.configure do | @@ -16,6 +16,7 @@ Errbit::Application.configure do | ||
| 16 | 16 | ||
| 17 | # Don't care if the mailer can't send | 17 | # Don't care if the mailer can't send |
| 18 | config.action_mailer.raise_delivery_errors = false | 18 | config.action_mailer.raise_delivery_errors = false |
| 19 | + config.action_mailer.default_url_options = { :host => 'localhost:3000' } | ||
| 19 | 20 | ||
| 20 | # Print deprecation notices to the Rails logger | 21 | # Print deprecation notices to the Rails logger |
| 21 | config.active_support.deprecation = :log | 22 | config.active_support.deprecation = :log |
config/environments/test.rb
| @@ -24,6 +24,7 @@ Errbit::Application.configure do | @@ -24,6 +24,7 @@ Errbit::Application.configure do | ||
| 24 | # The :test delivery method accumulates sent emails in the | 24 | # The :test delivery method accumulates sent emails in the |
| 25 | # ActionMailer::Base.deliveries array. | 25 | # ActionMailer::Base.deliveries array. |
| 26 | config.action_mailer.delivery_method = :test | 26 | config.action_mailer.delivery_method = :test |
| 27 | + config.action_mailer.default_url_options = { :host => 'test.host' } | ||
| 27 | 28 | ||
| 28 | # Use SQL instead of Active Record's schema dumper when creating the test database. | 29 | # Use SQL instead of Active Record's schema dumper when creating the test database. |
| 29 | # This is necessary if your schema can't be completely dumped by the schema dumper, | 30 | # This is necessary if your schema can't be completely dumped by the schema dumper, |
config/routes.rb
spec/controllers/apps_controller_spec.rb
| @@ -147,7 +147,6 @@ describe AppsController do | @@ -147,7 +147,6 @@ describe AppsController do | ||
| 147 | describe "PUT /apps/:id" do | 147 | describe "PUT /apps/:id" do |
| 148 | before do | 148 | before do |
| 149 | @app = Factory(:app) | 149 | @app = Factory(:app) |
| 150 | - App.stub(:find).with(@app.id).and_return(@app) | ||
| 151 | end | 150 | end |
| 152 | 151 | ||
| 153 | context "when the update is successful" do | 152 | context "when the update is successful" do |
| @@ -172,11 +171,60 @@ describe AppsController do | @@ -172,11 +171,60 @@ describe AppsController do | ||
| 172 | 171 | ||
| 173 | context "when the update is unsuccessful" do | 172 | context "when the update is unsuccessful" do |
| 174 | it "should render the edit page" do | 173 | it "should render the edit page" do |
| 175 | - @app.should_receive(:update_attributes).and_return(false) | ||
| 176 | - put :update, :id => @app.id, :app => {} | 174 | + put :update, :id => @app.id, :app => { :name => '' } |
| 177 | response.should render_template(:edit) | 175 | response.should render_template(:edit) |
| 178 | end | 176 | end |
| 179 | end | 177 | end |
| 178 | + | ||
| 179 | + context "setting up issue tracker", :cur => true do | ||
| 180 | + context "unknown tracker type" do | ||
| 181 | + before(:each) do | ||
| 182 | + put :update, :id => @app.id, :app => { :issue_tracker_attributes => { | ||
| 183 | + :issue_tracker_type => 'unknown', :project_id => '1234', :api_token => '123123', :account => 'myapp' | ||
| 184 | + } } | ||
| 185 | + @app.reload | ||
| 186 | + end | ||
| 187 | + | ||
| 188 | + it "should not create issue tracker" do | ||
| 189 | + @app.issue_tracker.should be_nil | ||
| 190 | + end | ||
| 191 | + end | ||
| 192 | + | ||
| 193 | + context "lighthouseapp" do | ||
| 194 | + it "should save tracker params" do | ||
| 195 | + put :update, :id => @app.id, :app => { :issue_tracker_attributes => { | ||
| 196 | + :issue_tracker_type => 'lighthouseapp', :project_id => '1234', :api_token => '123123', :account => 'myapp' | ||
| 197 | + } } | ||
| 198 | + @app.reload | ||
| 199 | + | ||
| 200 | + tracker = @app.issue_tracker | ||
| 201 | + tracker.issue_tracker_type.should == 'lighthouseapp' | ||
| 202 | + tracker.project_id.should == '1234' | ||
| 203 | + tracker.api_token.should == '123123' | ||
| 204 | + tracker.account.should == 'myapp' | ||
| 205 | + end | ||
| 206 | + | ||
| 207 | + it "should show validation notice when sufficient params are not present" do | ||
| 208 | + put :update, :id => @app.id, :app => { :issue_tracker_attributes => { | ||
| 209 | + :issue_tracker_type => 'lighthouseapp', :project_id => '1234', :api_token => '123123' | ||
| 210 | + } } | ||
| 211 | + @app.reload | ||
| 212 | + | ||
| 213 | + @app.issue_tracker.should be_nil | ||
| 214 | + response.body.should match(/You must specify your Lighthouseapp account, token and project id/) | ||
| 215 | + end | ||
| 216 | + | ||
| 217 | + it "should show validation notice when sufficient params are not present" do | ||
| 218 | + put :update, :id => @app.id, :app => { :issue_tracker_attributes => { | ||
| 219 | + :issue_tracker_type => 'lighthouseapp', :project_id => '1234', :api_token => '123123' | ||
| 220 | + } } | ||
| 221 | + @app.reload | ||
| 222 | + | ||
| 223 | + @app.issue_tracker.should be_nil | ||
| 224 | + response.body.should match(/You must specify your Lighthouseapp account, token and project id/) | ||
| 225 | + end | ||
| 226 | + end | ||
| 227 | + end | ||
| 180 | end | 228 | end |
| 181 | 229 | ||
| 182 | describe "DELETE /apps/:id" do | 230 | describe "DELETE /apps/:id" do |
spec/controllers/errs_controller_spec.rb
| @@ -123,6 +123,33 @@ describe ErrsController do | @@ -123,6 +123,33 @@ describe ErrsController do | ||
| 123 | get :show, :app_id => app.id, :id => err.id | 123 | get :show, :app_id => app.id, :id => err.id |
| 124 | response.should be_success | 124 | response.should be_success |
| 125 | end | 125 | end |
| 126 | + | ||
| 127 | + context "create issue button" do | ||
| 128 | + let(:button_matcher) { match(/create issue/) } | ||
| 129 | + | ||
| 130 | + it "should not exist for err's app without issue tracker" do | ||
| 131 | + err = Factory :err | ||
| 132 | + get :show, :app_id => err.app.id, :id => err.id | ||
| 133 | + | ||
| 134 | + response.body.should_not button_matcher | ||
| 135 | + end | ||
| 136 | + | ||
| 137 | + it "should exist for err's app with issue tracker" do | ||
| 138 | + tracker = Factory(:lighthouseapp_tracker) | ||
| 139 | + err = Factory(:err, :app => tracker.app) | ||
| 140 | + get :show, :app_id => err.app.id, :id => err.id | ||
| 141 | + | ||
| 142 | + response.body.should button_matcher | ||
| 143 | + end | ||
| 144 | + | ||
| 145 | + it "should not exist for err with issue_link" do | ||
| 146 | + tracker = Factory(:lighthouseapp_tracker) | ||
| 147 | + err = Factory(:err, :app => tracker.app, :issue_link => "http://some.host") | ||
| 148 | + get :show, :app_id => err.app.id, :id => err.id | ||
| 149 | + | ||
| 150 | + response.body.should_not button_matcher | ||
| 151 | + end | ||
| 152 | + end | ||
| 126 | end | 153 | end |
| 127 | 154 | ||
| 128 | context 'when logged in as a user' do | 155 | context 'when logged in as a user' do |
| @@ -186,5 +213,119 @@ describe ErrsController do | @@ -186,5 +213,119 @@ describe ErrsController do | ||
| 186 | response.should redirect_to(errs_path) | 213 | response.should redirect_to(errs_path) |
| 187 | end | 214 | end |
| 188 | end | 215 | end |
| 189 | - | 216 | + |
| 217 | + describe "POST /apps/:app_id/errs/:id/create_issue" do | ||
| 218 | + render_views | ||
| 219 | + | ||
| 220 | + before(:each) do | ||
| 221 | + sign_in Factory(:admin) | ||
| 222 | + end | ||
| 223 | + | ||
| 224 | + context "successful issue creation" do | ||
| 225 | + context "lighthouseapp tracker" do | ||
| 226 | + let(:notice) { Factory :notice } | ||
| 227 | + let(:tracker) { Factory :lighthouseapp_tracker, :app => notice.err.app } | ||
| 228 | + let(:err) { notice.err } | ||
| 229 | + | ||
| 230 | + before(:each) do | ||
| 231 | + number = 5 | ||
| 232 | + @issue_link = "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets/#{number}.xml" | ||
| 233 | + body = "<ticket><number type=\"integer\">#{number}</number></ticket>" | ||
| 234 | + stub_request(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml").to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) | ||
| 235 | + | ||
| 236 | + post :create_issue, :app_id => err.app.id, :id => err.id | ||
| 237 | + err.reload | ||
| 238 | + end | ||
| 239 | + | ||
| 240 | + it "should make request to Lighthouseapp with err params" do | ||
| 241 | + requested = have_requested(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml") | ||
| 242 | + WebMock.should requested.with(:headers => {'X-Lighthousetoken' => tracker.api_token}) | ||
| 243 | + WebMock.should requested.with(:body => /<tag>errbit<\/tag>/) | ||
| 244 | + WebMock.should requested.with(:body => /<title>\[#{ err.environment }\]\[#{err.where}\] #{err.message.to_s.truncate(100)}<\/title>/) | ||
| 245 | + WebMock.should requested.with(:body => /<body>.+<\/body>/m) | ||
| 246 | + end | ||
| 247 | + | ||
| 248 | + it "should redirect to err page" do | ||
| 249 | + response.should redirect_to( app_err_path(err.app, err) ) | ||
| 250 | + end | ||
| 251 | + | ||
| 252 | + it "should create issue link for err" do | ||
| 253 | + err.issue_link.should == @issue_link.sub(/\.xml$/, '') | ||
| 254 | + end | ||
| 255 | + end | ||
| 256 | + end | ||
| 257 | + | ||
| 258 | + context "absent issue tracker" do | ||
| 259 | + let(:err) { Factory :err } | ||
| 260 | + | ||
| 261 | + before(:each) do | ||
| 262 | + post :create_issue, :app_id => err.app.id, :id => err.id | ||
| 263 | + end | ||
| 264 | + | ||
| 265 | + it "should redirect to err page" do | ||
| 266 | + response.should redirect_to( app_err_path(err.app, err) ) | ||
| 267 | + end | ||
| 268 | + | ||
| 269 | + it "should set flash error message telling issue tracker of the app doesn't exist" do | ||
| 270 | + flash[:error].should == "This up has no issue tracker setup." | ||
| 271 | + end | ||
| 272 | + end | ||
| 273 | + | ||
| 274 | + context "error during request to a tracker" do | ||
| 275 | + context "lighthouseapp tracker" do | ||
| 276 | + let(:tracker) { Factory :lighthouseapp_tracker } | ||
| 277 | + let(:err) { Factory :err, :app => tracker.app } | ||
| 278 | + | ||
| 279 | + before(:each) do | ||
| 280 | + stub_request(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml").to_return(:status => 500) | ||
| 281 | + | ||
| 282 | + post :create_issue, :app_id => err.app.id, :id => err.id | ||
| 283 | + end | ||
| 284 | + | ||
| 285 | + it "should redirect to err page" do | ||
| 286 | + response.should redirect_to( app_err_path(err.app, err) ) | ||
| 287 | + end | ||
| 288 | + | ||
| 289 | + it "should notify of connection error" do | ||
| 290 | + flash[:error].should == "There was an error during issue creation. Check your tracker settings or try again later." | ||
| 291 | + end | ||
| 292 | + end | ||
| 293 | + end | ||
| 294 | + end | ||
| 295 | + | ||
| 296 | + describe "DELETE /apps/:app_id/errs/:id/clear_issue" do | ||
| 297 | + before(:each) do | ||
| 298 | + sign_in Factory(:admin) | ||
| 299 | + end | ||
| 300 | + | ||
| 301 | + context "err with issue" do | ||
| 302 | + let(:err) { Factory :err, :issue_link => "http://some.host" } | ||
| 303 | + | ||
| 304 | + before(:each) do | ||
| 305 | + delete :clear_issue, :app_id => err.app.id, :id => err.id | ||
| 306 | + err.reload | ||
| 307 | + end | ||
| 308 | + | ||
| 309 | + it "should redirect to err page" do | ||
| 310 | + response.should redirect_to( app_err_path(err.app, err) ) | ||
| 311 | + end | ||
| 312 | + | ||
| 313 | + it "should clear issue link" do | ||
| 314 | + err.issue_link.should be_nil | ||
| 315 | + end | ||
| 316 | + end | ||
| 317 | + | ||
| 318 | + context "err without issue" do | ||
| 319 | + let(:err) { Factory :err } | ||
| 320 | + | ||
| 321 | + before(:each) do | ||
| 322 | + delete :clear_issue, :app_id => err.app.id, :id => err.id | ||
| 323 | + err.reload | ||
| 324 | + end | ||
| 325 | + | ||
| 326 | + it "should redirect to err page" do | ||
| 327 | + response.should redirect_to( app_err_path(err.app, err) ) | ||
| 328 | + end | ||
| 329 | + end | ||
| 330 | + end | ||
| 190 | end | 331 | end |
spec/factories.rb
| 1 | Factory.sequence(:name) {|n| "John #{n} Doe"} | 1 | Factory.sequence(:name) {|n| "John #{n} Doe"} |
| 2 | +Factory.sequence(:word) {|n| "word#{n}"} | ||
| 2 | Factory.sequence(:app_name) {|n| "App ##{n}"} | 3 | Factory.sequence(:app_name) {|n| "App ##{n}"} |
| 3 | Factory.sequence(:email) {|n| "email#{n}@example.com"} | 4 | Factory.sequence(:email) {|n| "email#{n}@example.com"} |
| 4 | Factory.sequence(:user_email) {|n| "user.#{n}@example.com"} | 5 | Factory.sequence(:user_email) {|n| "user.#{n}@example.com"} |
spec/factories/app_factories.rb
| @@ -25,4 +25,4 @@ Factory.define(:deploy) do |d| | @@ -25,4 +25,4 @@ Factory.define(:deploy) do |d| | ||
| 25 | d.repository 'git@github.com/jdpace/errbit.git' | 25 | d.repository 'git@github.com/jdpace/errbit.git' |
| 26 | d.environment 'production' | 26 | d.environment 'production' |
| 27 | d.revision ActiveSupport::SecureRandom.hex(10) | 27 | d.revision ActiveSupport::SecureRandom.hex(10) |
| 28 | -end | ||
| 29 | \ No newline at end of file | 28 | \ No newline at end of file |
| 29 | +end |
| @@ -0,0 +1,7 @@ | @@ -0,0 +1,7 @@ | ||
| 1 | +Factory.define :lighthouseapp_tracker, :class => IssueTracker do |e| | ||
| 2 | + e.issue_tracker_type 'lighthouseapp' | ||
| 3 | + e.account { Factory.next :word } | ||
| 4 | + e.api_token { Factory.next :word } | ||
| 5 | + e.project_id { Factory.next :word } | ||
| 6 | + e.association :app, :factory => :app | ||
| 7 | +end | ||
| 0 | \ No newline at end of file | 8 | \ No newline at end of file |
spec/spec_helper.rb
| @@ -4,6 +4,7 @@ ENV["RAILS_ENV"] ||= 'test' | @@ -4,6 +4,7 @@ ENV["RAILS_ENV"] ||= 'test' | ||
| 4 | require File.expand_path("../../config/environment", __FILE__) | 4 | require File.expand_path("../../config/environment", __FILE__) |
| 5 | require 'rspec/rails' | 5 | require 'rspec/rails' |
| 6 | require 'database_cleaner' | 6 | require 'database_cleaner' |
| 7 | +require 'webmock/rspec' | ||
| 7 | 8 | ||
| 8 | # Requires supporting files with custom matchers and macros, etc, | 9 | # Requires supporting files with custom matchers and macros, etc, |
| 9 | # in ./support/ and its subdirectories. | 10 | # in ./support/ and its subdirectories. |
| @@ -21,4 +22,5 @@ RSpec.configure do |config| | @@ -21,4 +22,5 @@ RSpec.configure do |config| | ||
| 21 | DatabaseCleaner[:mongoid].strategy = :truncation | 22 | DatabaseCleaner[:mongoid].strategy = :truncation |
| 22 | DatabaseCleaner.clean | 23 | DatabaseCleaner.clean |
| 23 | end | 24 | end |
| 25 | + config.include WebMock::API | ||
| 24 | end | 26 | end |
| 25 | \ No newline at end of file | 27 | \ No newline at end of file |