diff --git a/Gemfile b/Gemfile index 2157737..f005b2a 100644 --- a/Gemfile +++ b/Gemfile @@ -14,7 +14,7 @@ end group :development, :test do gem 'rspec-rails', '~> 2.5' - gem 'webmock' + gem 'webmock', :require => false end group :test do diff --git a/app/controllers/apps_controller.rb b/app/controllers/apps_controller.rb index 27449b8..3bf6082 100644 --- a/app/controllers/apps_controller.rb +++ b/app/controllers/apps_controller.rb @@ -22,6 +22,7 @@ class AppsController < ApplicationController def new @app = App.new @app.watchers.build + @app.issue_tracker = IssueTracker.new end def edit diff --git a/app/controllers/errs_controller.rb b/app/controllers/errs_controller.rb index e7ca172..d4aa55d 100644 --- a/app/controllers/errs_controller.rb +++ b/app/controllers/errs_controller.rb @@ -1,6 +1,7 @@ class ErrsController < ApplicationController before_filter :find_app, :except => [:index, :all] + before_filter :find_err, :except => [:index, :all] def index app_scope = current_user.admin? ? App.all : current_user.apps @@ -20,16 +21,25 @@ class ErrsController < ApplicationController end def show - @err = @app.errs.find(params[:id]) page = (params[:notice] || @err.notices.count) page = 1 if page.to_i.zero? @notices = @err.notices.ordered.paginate(:page => page, :per_page => 1) @notice = @notices.first end + + def create_issue + if @app.issue_tracker + @app.issue_tracker.create_issue @err + else + flash[:error] = "This up has no issue tracker setup." + end + redirect_to app_err_path(@app, @err) + rescue ActiveResource::ConnectionError + flash[:error] = "There was an error during issue creation. Check your tracker settings or try again later." + redirect_to app_err_path(@app, @err) + end def resolve - @err = @app.errs.find(params[:id]) - # Deal with bug in mogoid where find is returning an Enumberable obj @err = @err.first if @err.respond_to?(:first) @@ -51,5 +61,9 @@ class ErrsController < ApplicationController # apparently finding by 'watchers.email' and 'id' is broken raise(Mongoid::Errors::DocumentNotFound.new(App,@app.id)) unless current_user.admin? || current_user.watching?(@app) end + + def find_err + @err = @app.errs.find(params[:id]) + end end diff --git a/app/models/err.rb b/app/models/err.rb index d38f1eb..9f7e338 100644 --- a/app/models/err.rb +++ b/app/models/err.rb @@ -9,6 +9,7 @@ class Err field :fingerprint field :last_notice_at, :type => DateTime field :resolved, :type => Boolean, :default => false + field :issue_link, :type => String index :last_notice_at diff --git a/app/models/issue_tracker.rb b/app/models/issue_tracker.rb index 8242fe3..3ce6360 100644 --- a/app/models/issue_tracker.rb +++ b/app/models/issue_tracker.rb @@ -1,6 +1,9 @@ class IssueTracker include Mongoid::Document include Mongoid::Timestamps + include HashHelper + include Rails.application.routes.url_helpers + default_url_options[:host] = Errbit::Application.config.action_mailer.default_url_options[:host] validate :check_lighthouseapp_params @@ -15,11 +18,72 @@ class IssueTracker Lighthouse.account = account Lighthouse.token = api_token + # updating lighthouse account + Lighthouse::Ticket.site + ticket = Lighthouse::Ticket.new(:project_id => project_id) - ticket.title = "[#{ err.where }] #{err.message.to_s.truncate(27)}" - #ticket.body = err.backtrace.join("\n") + ticket.title = "[#{ err.environment }][#{ err.where }] #{err.message.to_s.truncate(100)}" + + ticket.body = "" + ticket.body += "[See this exception on Errbit](#{ app_err_url err.app, err } \"See this exception on Errbit\")" + ticket.body += "\n" + if notice = err.notices.first + ticket.body += "# #{notice.message} #" + ticket.body += "\n" + ticket.body += "## Summary ##" + ticket.body += "\n" + if notice.request['url'].present? + ticket.body += "### URL ###" + ticket.body += "\n" + ticket.body += "[#{notice.request['url']}](#{notice.request['url']})" + ticket.body += "\n" + end + ticket.body += "### Where ###" + ticket.body += "\n" + ticket.body += notice.err.where + ticket.body += "\n" + + ticket.body += "### Occured ###" + ticket.body += "\n" + ticket.body += notice.created_at.to_s(:micro) + ticket.body += "\n" + + ticket.body += "### Similar ###" + ticket.body += "\n" + ticket.body += (notice.err.notices.count - 1).to_s + ticket.body += "\n" + + ticket.body += "## Params ##" + ticket.body += "\n" + ticket.body += "#{pretty_hash(notice.params)}" + ticket.body += "\n" + + ticket.body += "## Session ##" + ticket.body += "\n" + ticket.body += "#{pretty_hash(notice.session)}" + ticket.body += "\n" + + ticket.body += "## Backtrace ##" + ticket.body += "\n" + ticket.body += "" + for line in notice.backtrace + ticket.body += "#{line['number']}: #{line['file'].sub(/^\[PROJECT_ROOT\]/, '')} -> **#{line['method']}**" + ticket.body += "\n" + end + ticket.body += "" + ticket.body += "\n" + + ticket.body += "## Environment ##" + ticket.body += "\n" + for key, val in notice.env_vars + ticket.body += "#{key}: #{val}" + end + ticket.body += "\n" + end + ticket.tags << "errbit" - ticket.save + ticket.save! + 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$/, '') end protected diff --git a/app/views/errs/show.html.haml b/app/views/errs/show.html.haml index 11b4fb9..475c0e1 100644 --- a/app/views/errs/show.html.haml +++ b/app/views/errs/show.html.haml @@ -10,7 +10,13 @@ %strong Last Notice: = last_notice_at(@err).to_s(:micro) - content_for :action_bar do - %span= link_to 'resolve', resolve_app_err_path(@app, @err), :method => :put, :confirm => err_confirm, :class => 'resolve' if @err.unresolved? + - if @err.unresolved? + - if @err.app.issue_tracker + - if @err.issue_link.blank? + %span= link_to 'create issue', create_issue_app_err_path(@app, @err), :method => :post, :class => 'create-issue' + - else + %span= link_to 'go to issue', @err.issue_link, :class => 'goto-issue' + %span= link_to 'resolve', resolve_app_err_path(@app, @err), :method => :put, :confirm => err_confirm, :class => 'resolve' %h4= @notice.try(:message) diff --git a/config/environments/development.rb b/config/environments/development.rb index 6662691..4a01584 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -16,6 +16,7 @@ Errbit::Application.configure do # Don't care if the mailer can't send config.action_mailer.raise_delivery_errors = false + config.action_mailer.default_url_options = { :host => 'localhost:3000' } # Print deprecation notices to the Rails logger config.active_support.deprecation = :log diff --git a/config/environments/test.rb b/config/environments/test.rb index 9dfc650..1922d21 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -24,6 +24,7 @@ Errbit::Application.configure do # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test + config.action_mailer.default_url_options = { :host => 'test.host' } # Use SQL instead of Active Record's schema dumper when creating the test database. # This is necessary if your schema can't be completely dumped by the schema dumper, diff --git a/config/routes.rb b/config/routes.rb index 33afb06..a29d8e9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -20,6 +20,7 @@ Errbit::Application.routes.draw do resources :notices member do put :resolve + post :create_issue end end diff --git a/spec/controllers/errs_controller_spec.rb b/spec/controllers/errs_controller_spec.rb index 3952604..017bd80 100644 --- a/spec/controllers/errs_controller_spec.rb +++ b/spec/controllers/errs_controller_spec.rb @@ -123,6 +123,33 @@ describe ErrsController do get :show, :app_id => app.id, :id => err.id response.should be_success end + + context "create issue button" do + let(:button_matcher) { match(/create issue/) } + + it "should not exist for err's app without issue tracker" do + err = Factory :err + get :show, :app_id => err.app.id, :id => err.id + + response.body.should_not button_matcher + end + + it "should exist for err's app with issue tracker" do + tracker = Factory(:lighthouseapp_tracker) + err = Factory(:err, :app => tracker.app) + get :show, :app_id => err.app.id, :id => err.id + + response.body.should button_matcher + end + + it "should not exist for err with issue_link" do + tracker = Factory(:lighthouseapp_tracker) + err = Factory(:err, :app => tracker.app, :issue_link => "http://some.host") + get :show, :app_id => err.app.id, :id => err.id + + response.body.should_not button_matcher + end + end end context 'when logged in as a user' do @@ -186,5 +213,83 @@ describe ErrsController do response.should redirect_to(errs_path) end end - + + describe "POST /apps/:app_id/errs/:id/create_issue" do + render_views + + before(:each) do + sign_in Factory(:admin) + end + + context "successful issue creation" do + context "lighthouseapp tracker" do + let(:notice) { Factory :notice } + let(:tracker) { Factory :lighthouseapp_tracker, :app => notice.err.app } + let(:err) { notice.err } + + before(:each) do + number = 5 + @issue_link = "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets/#{number}.xml" + body = "#{number}" + stub_request(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml").to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) + + post :create_issue, :app_id => err.app.id, :id => err.id + err.reload + end + + it "should make request to Lighthouseapp with err params" do + requested = have_requested(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml") + WebMock.should requested.with(:headers => {'X-Lighthousetoken' => tracker.api_token}) + WebMock.should requested.with(:body => /errbit<\/tag>/) + WebMock.should requested.with(:body => /\[#{ err.environment }\]\[#{err.where}\] #{err.message.to_s.truncate(100)}<\/title>/) + WebMock.should requested.with(:body => /<body>.+<\/body>/m) + end + + it "should redirect to err page" do + response.should redirect_to( app_err_path(err.app, err) ) + end + + it "should create issue link for err" do + err.issue_link.should == @issue_link.sub(/\.xml$/, '') + end + end + end + + context "absent issue tracker" do + let(:err) { Factory :err } + + before(:each) do + post :create_issue, :app_id => err.app.id, :id => err.id + end + + it "should redirect to err page" do + response.should redirect_to( app_err_path(err.app, err) ) + end + + it "should set flash error message telling issue tracker of the app doesn't exist" do + flash[:error].should == "This up has no issue tracker setup." + end + end + + context "error during request to a tracker" do + context "lighthouseapp tracker" do + let(:tracker) { Factory :lighthouseapp_tracker } + let(:err) { Factory :err, :app => tracker.app } + + before(:each) do + stub_request(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml").to_return(:status => 500) + + post :create_issue, :app_id => err.app.id, :id => err.id + end + + it "should redirect to err page" do + response.should redirect_to( app_err_path(err.app, err) ) + end + + it "should notify of connection error" do + flash[:error].should == "There was an error during issue creation. Check your tracker settings or try again later." + end + end + end + end end diff --git a/spec/models/issue_tracker_spec.rb b/spec/models/issue_tracker_spec.rb index 9b7e8c7..6b5f577 100644 --- a/spec/models/issue_tracker_spec.rb +++ b/spec/models/issue_tracker_spec.rb @@ -2,16 +2,4 @@ require 'spec_helper' describe IssueTracker do - describe "#create_issue" do - context "lighthouseapp tracker" do - let(:tracker) { Factory :lighthouseapp_tracker } - let(:err) { Factory :err } - - it "should make request to Lighthouseapp with err params" do - stub_request(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml") - tracker.create_issue err - WebMock.should have_requested(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml") - end - end - end end -- libgit2 0.21.2