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>/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