Commit 005eaf224602e154cd1d233c084597802d100d3c
Exists in
master
and in
1 other branch
Merge branch 'master' of https://github.com/boblail/errbit into boblail_master
Conflicts: Gemfile Gemfile.lock app/models/notice.rb app/views/notices/_environment.html.haml app/views/notices/_summary.html.haml spec/models/notice_spec.rb
Showing
12 changed files
with
107 additions
and
10 deletions
Show diff stats
Gemfile
@@ -9,6 +9,7 @@ gem 'devise', '~> 1.1.8' | @@ -9,6 +9,7 @@ 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 | gem 'redmine_client', :git => "git://github.com/oruen/redmine_client.git" |
11 | gem 'mongoid_rails_migrations' | 11 | gem 'mongoid_rails_migrations' |
12 | +gem 'useragent', '~> 0.3.1' | ||
12 | 13 | ||
13 | platform :ruby do | 14 | platform :ruby do |
14 | gem 'bson_ext', '~> 1.2' | 15 | gem 'bson_ext', '~> 1.2' |
Gemfile.lock
@@ -114,6 +114,7 @@ GEM | @@ -114,6 +114,7 @@ GEM | ||
114 | treetop (1.4.9) | 114 | treetop (1.4.9) |
115 | polyglot (>= 0.3.1) | 115 | polyglot (>= 0.3.1) |
116 | tzinfo (0.3.26) | 116 | tzinfo (0.3.26) |
117 | + useragent (0.3.1) | ||
117 | warden (1.0.3) | 118 | warden (1.0.3) |
118 | rack (>= 1.0.0) | 119 | rack (>= 1.0.0) |
119 | webmock (1.6.2) | 120 | webmock (1.6.2) |
@@ -138,5 +139,6 @@ DEPENDENCIES | @@ -138,5 +139,6 @@ DEPENDENCIES | ||
138 | redmine_client! | 139 | redmine_client! |
139 | rspec (~> 2.5) | 140 | rspec (~> 2.5) |
140 | rspec-rails (~> 2.5) | 141 | rspec-rails (~> 2.5) |
142 | + useragent (~> 0.3.1) | ||
141 | webmock | 143 | webmock |
142 | will_paginate | 144 | will_paginate |
app/controllers/notices_controller.rb
@@ -4,7 +4,8 @@ class NoticesController < ApplicationController | @@ -4,7 +4,8 @@ class NoticesController < ApplicationController | ||
4 | skip_before_filter :authenticate_user!, :only => :create | 4 | skip_before_filter :authenticate_user!, :only => :create |
5 | 5 | ||
6 | def create | 6 | def create |
7 | - @notice = Notice.from_xml(request.raw_post) | 7 | + # params[:data] if the notice came from a GET request, raw_post if it came via POST |
8 | + @notice = Notice.from_xml(params[:data] || request.raw_post) | ||
8 | respond_with @notice | 9 | respond_with @notice |
9 | end | 10 | end |
10 | 11 |
app/helpers/application_helper.rb
1 | module ApplicationHelper | 1 | module ApplicationHelper |
2 | + | ||
3 | + | ||
2 | def lighthouse_tracker? object | 4 | def lighthouse_tracker? object |
3 | object.issue_tracker_type == "lighthouseapp" | 5 | object.issue_tracker_type == "lighthouseapp" |
4 | end | 6 | end |
7 | + | ||
8 | + | ||
9 | + def user_agent_graph(error) | ||
10 | + tallies = tally(error.notices) {|notice| pretty_user_agent(notice.user_agent)} | ||
11 | + create_percentage_table(tallies, :total => error.notices.count) | ||
12 | + end | ||
13 | + | ||
14 | + def pretty_user_agent(user_agent) | ||
15 | + (user_agent.nil? || user_agent.none?) ? "N/A" : "#{user_agent.browser} #{user_agent.version}" | ||
16 | + end | ||
17 | + | ||
18 | + | ||
19 | + def tally(collection, &block) | ||
20 | + collection.inject({}) do |tallies, item| | ||
21 | + value = yield item | ||
22 | + tallies[value] = (tallies[value] || 0) + 1 | ||
23 | + tallies | ||
24 | + end | ||
25 | + end | ||
26 | + | ||
27 | + | ||
28 | + def create_percentage_table(tallies, options={}) | ||
29 | + total = (options[:total] || total_from_tallies(tallies)) | ||
30 | + percent = 100.0 / total.to_f | ||
31 | + rows = tallies.map {|value, count| [(count.to_f * percent), value]} \ | ||
32 | + .sort {|a, b| a[0] <=> b[0]} | ||
33 | + render :partial => "errs/tally_table", :locals => {:rows => rows} | ||
34 | + end | ||
35 | + | ||
36 | + | ||
37 | +private | ||
38 | + | ||
39 | + | ||
40 | + def total_from_tallies(tallies) | ||
41 | + tallies.values.inject(0) {|sum, n| sum + n} | ||
42 | + end | ||
43 | + | ||
44 | + | ||
5 | end | 45 | end |
app/models/notice.rb
@@ -51,6 +51,11 @@ class Notice | @@ -51,6 +51,11 @@ class Notice | ||
51 | }) | 51 | }) |
52 | end | 52 | end |
53 | 53 | ||
54 | + def user_agent | ||
55 | + agent_string = env_vars['HTTP_USER_AGENT'] | ||
56 | + agent_string.blank? ? nil : UserAgent.parse(agent_string) | ||
57 | + end | ||
58 | + | ||
54 | def request | 59 | def request |
55 | read_attribute(:request) || {} | 60 | read_attribute(:request) || {} |
56 | end | 61 | end |
app/views/errs/_table.html.haml
@@ -11,7 +11,7 @@ | @@ -11,7 +11,7 @@ | ||
11 | - errs.each do |err| | 11 | - errs.each do |err| |
12 | %tr{:class => err.resolved? ? 'resolved' : 'unresolved'} | 12 | %tr{:class => err.resolved? ? 'resolved' : 'unresolved'} |
13 | %td.app | 13 | %td.app |
14 | - = err.app.name | 14 | + = link_to err.app.name, app_path(err.app) |
15 | %span.environment= err.environment | 15 | %span.environment= err.environment |
16 | %td.message | 16 | %td.message |
17 | = link_to err.message, app_err_path(err.app, err) | 17 | = link_to err.message, app_err_path(err.app, err) |
app/views/notices/_environment.html.haml
1 | .window | 1 | .window |
2 | %table.environment | 2 | %table.environment |
3 | - - notice.env_vars.each do |key,val| | 3 | + - notice.env_vars.sort_by {|pair| pair[0]}.each do |pair| |
4 | %tr | 4 | %tr |
5 | - %th= raw key | ||
6 | - %td.main= val | ||
7 | \ No newline at end of file | 5 | \ No newline at end of file |
6 | + %th= pair[0] | ||
7 | + %td.main= pair[1] |
app/views/notices/_summary.html.haml
@@ -15,4 +15,7 @@ | @@ -15,4 +15,7 @@ | ||
15 | %td= notice.created_at.to_s(:micro) | 15 | %td= notice.created_at.to_s(:micro) |
16 | %tr | 16 | %tr |
17 | %th Similar | 17 | %th Similar |
18 | - %td= notice.err.notices_count - 1 | ||
19 | \ No newline at end of file | 18 | \ No newline at end of file |
19 | + %td= notice.err.notices.count - 1 | ||
20 | + %tr | ||
21 | + %th Browser | ||
22 | + %td= user_agent_graph(notice.err) |
public/stylesheets/application.css
@@ -562,7 +562,7 @@ table.errs td.app .environment { | @@ -562,7 +562,7 @@ table.errs td.app .environment { | ||
562 | table.errs td.message a { | 562 | table.errs td.message a { |
563 | width: 420px; | 563 | width: 420px; |
564 | display: block; | 564 | display: block; |
565 | - word-wrap: break-word; | 565 | + word-wrap: break-word; |
566 | } | 566 | } |
567 | table.errs td.message em { | 567 | table.errs td.message em { |
568 | color: #727272; | 568 | color: #727272; |
@@ -574,6 +574,27 @@ table.errs tr.resolved td > * { | @@ -574,6 +574,27 @@ table.errs tr.resolved td > * { | ||
574 | -webkit-opacity: 0.5; | 574 | -webkit-opacity: 0.5; |
575 | } | 575 | } |
576 | 576 | ||
577 | +/* Tally tables */ | ||
578 | +table.tally { | ||
579 | + border:none; | ||
580 | +} | ||
581 | +table.tally td, | ||
582 | +table.tally th { | ||
583 | + border:none !important; | ||
584 | + background:none !important; | ||
585 | + padding:8px 0 0; | ||
586 | +} | ||
587 | +table.tally tbody tr:first-child td, | ||
588 | +table.tally tbody tr:first-child th { | ||
589 | + padding-top:0; | ||
590 | +} | ||
591 | +table.tally td.percent { | ||
592 | + width:4.5em; | ||
593 | +} | ||
594 | +table.tally th.value { | ||
595 | + text-transform:none; | ||
596 | +} | ||
597 | + | ||
577 | /* Resolve Errs */ | 598 | /* Resolve Errs */ |
578 | #action-bar a.resolve { | 599 | #action-bar a.resolve { |
579 | background: transparent url(images/icons/thumbs-up.png) 6px 5px no-repeat; | 600 | background: transparent url(images/icons/thumbs-up.png) 6px 5px no-repeat; |
spec/controllers/notices_controller_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper' | @@ -2,7 +2,7 @@ require 'spec_helper' | ||
2 | 2 | ||
3 | describe NoticesController do | 3 | describe NoticesController do |
4 | 4 | ||
5 | - context 'POST[XML] notices#create' do | 5 | + context 'notices API' do |
6 | before do | 6 | before do |
7 | @xml = Rails.root.join('spec','fixtures','hoptoad_test_notice.xml').read | 7 | @xml = Rails.root.join('spec','fixtures','hoptoad_test_notice.xml').read |
8 | @app = Factory(:app_with_watcher) | 8 | @app = Factory(:app_with_watcher) |
@@ -11,15 +11,21 @@ describe NoticesController do | @@ -11,15 +11,21 @@ describe NoticesController do | ||
11 | 11 | ||
12 | request.env['Content-type'] = 'text/xml' | 12 | request.env['Content-type'] = 'text/xml' |
13 | request.env['Accept'] = 'text/xml, application/xml' | 13 | request.env['Accept'] = 'text/xml, application/xml' |
14 | - request.should_receive(:raw_post).and_return(@xml) | ||
15 | end | 14 | end |
16 | 15 | ||
17 | - it "generates a notice from the xml" do | 16 | + it "generates a notice from xml [POST]" do |
18 | Notice.should_receive(:from_xml).with(@xml).and_return(@notice) | 17 | Notice.should_receive(:from_xml).with(@xml).and_return(@notice) |
18 | + request.should_receive(:raw_post).and_return(@xml) | ||
19 | post :create | 19 | post :create |
20 | end | 20 | end |
21 | 21 | ||
22 | + it "generates a notice from xml [GET]" do | ||
23 | + Notice.should_receive(:from_xml).with(@xml).and_return(@notice) | ||
24 | + get :create, {:data => @xml} | ||
25 | + end | ||
26 | + | ||
22 | it "sends a notification email" do | 27 | it "sends a notification email" do |
28 | + request.should_receive(:raw_post).and_return(@xml) | ||
23 | post :create | 29 | post :create |
24 | email = ActionMailer::Base.deliveries.last | 30 | email = ActionMailer::Base.deliveries.last |
25 | email.to.should include(@app.watchers.first.email) | 31 | email.to.should include(@app.watchers.first.email) |
spec/models/notice_spec.rb
@@ -119,6 +119,19 @@ describe Notice do | @@ -119,6 +119,19 @@ describe Notice do | ||
119 | end | 119 | end |
120 | end | 120 | end |
121 | 121 | ||
122 | + describe "user agent" do | ||
123 | + it "should be parsed and human-readable" do | ||
124 | + notice = Factory.build(:notice, :request => {'cgi-data' => {'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.204 Safari/534.16'}}) | ||
125 | + notice.user_agent.browser.should == 'Chrome' | ||
126 | + notice.user_agent.version.to_s.should =~ /^10\.0/ | ||
127 | + end | ||
128 | + | ||
129 | + it "should be nil if HTTP_USER_AGENT is blank" do | ||
130 | + notice = Factory.build(:notice) | ||
131 | + notice.user_agent.should == nil | ||
132 | + end | ||
133 | + end | ||
134 | + | ||
122 | describe "email notifications" do | 135 | describe "email notifications" do |
123 | before do | 136 | before do |
124 | @app = Factory(:app_with_watcher) | 137 | @app = Factory(:app_with_watcher) |