Commit 93cf2659cdad67e293b49e0dcd54b01e8425d072

Authored by Thomas Cioppettini
2 parents 4cd3fc46 4678fd1d
Exists in master and in 1 other branch production

Merge remote-tracking branch 'upstream/master'

.travis.yml
... ... @@ -4,13 +4,14 @@ env:
4 4 rvm:
5 5 - 2.0.0
6 6 - 1.9.3
7   - - rbx-19mode
  7 + - 2.1.2
  8 + - rbx-2
8 9 - ruby-head
9 10 services: mongodb
10 11 #script: ./script/rspec-queue-mongoid.rb --format progress spec
11 12 matrix:
12 13 allow_failures:
13   - - rvm: rbx-19mode
  14 + - rvm: rbx-2
14 15 - rvm: ruby-head
15 16  
16 17  
... ...
CONTRIBUTORS.md
... ... @@ -42,11 +42,14 @@
42 42 - [@nashby][]
43 43 - [@shingara][]
44 44 - [@tscolari][]
  45 +- [@callumd][]
  46 +
45 47  
46 48 [@anicet]: https://github.com/anicet
47 49 [@nashby]: https://github.com/nashby
48 50 [@shingara]: https://github.com/shingara
49 51 [@tscolari]: https://github.com/tscolari
  52 +[@callumd]: https://github.com/callumd
50 53  
51 54 ## 0.2.0 - 2013-09-11
52 55  
... ...
Gemfile
1 1 source 'https://rubygems.org'
2 2  
3   -RAILS_VERSION = '~> 3.2.17'
  3 +RAILS_VERSION = '~> 3.2.19'
4 4  
5 5 gem 'actionmailer', RAILS_VERSION
6 6 gem 'actionpack', RAILS_VERSION
... ... @@ -79,8 +79,6 @@ group :development, :test do
79 79 gem 'rspec-rails'
80 80 gem 'webmock', :require => false
81 81 gem 'airbrake', :require => false
82   - gem 'ruby-debug', :platform => :mri_18
83   - gem 'debugger', :platform => :mri_19
84 82 gem 'pry-rails'
85 83 # gem 'rpm_contrib'
86 84 # gem 'newrelic_rpm'
... ...
Gemfile.lock
1 1 GEM
2 2 remote: https://rubygems.org/
3 3 specs:
4   - actionmailer (3.2.17)
5   - actionpack (= 3.2.17)
  4 + actionmailer (3.2.19)
  5 + actionpack (= 3.2.19)
6 6 mail (~> 2.5.4)
7 7 actionmailer_inline_css (1.5.3)
8 8 actionmailer (>= 3.0.0)
9 9 nokogiri (>= 1.4.4)
10 10 premailer (>= 1.7.1)
11   - actionpack (3.2.17)
12   - activemodel (= 3.2.17)
13   - activesupport (= 3.2.17)
  11 + actionpack (3.2.19)
  12 + activemodel (= 3.2.19)
  13 + activesupport (= 3.2.19)
14 14 builder (~> 3.0.0)
15 15 erubis (~> 2.7.0)
16 16 journey (~> 1.0.4)
... ... @@ -18,18 +18,18 @@ GEM
18 18 rack-cache (~> 1.2)
19 19 rack-test (~> 0.6.1)
20 20 sprockets (~> 2.2.1)
21   - activemodel (3.2.17)
22   - activesupport (= 3.2.17)
  21 + activemodel (3.2.19)
  22 + activesupport (= 3.2.19)
23 23 builder (~> 3.0.0)
24   - activerecord (3.2.17)
25   - activemodel (= 3.2.17)
26   - activesupport (= 3.2.17)
  24 + activerecord (3.2.19)
  25 + activemodel (= 3.2.19)
  26 + activesupport (= 3.2.19)
27 27 arel (~> 3.0.2)
28 28 tzinfo (~> 0.3.29)
29   - activeresource (3.2.17)
30   - activemodel (= 3.2.17)
31   - activesupport (= 3.2.17)
32   - activesupport (3.2.17)
  29 + activeresource (3.2.19)
  30 + activemodel (= 3.2.19)
  31 + activesupport (= 3.2.19)
  32 + activesupport (3.2.19)
33 33 i18n (~> 0.6, >= 0.6.4)
34 34 multi_json (~> 1.0)
35 35 addressable (2.3.5)
... ... @@ -67,7 +67,6 @@ GEM
67 67 rack-test (>= 0.5.4)
68 68 xpath (~> 2.0)
69 69 coderay (1.0.9)
70   - columnize (0.3.6)
71 70 coveralls (0.7.0)
72 71 multi_json (~> 1.3)
73 72 rest-client
... ... @@ -80,12 +79,6 @@ GEM
80 79 addressable
81 80 database_cleaner (1.2.0)
82 81 debug_inspector (0.0.2)
83   - debugger (1.6.3)
84   - columnize (>= 0.3.1)
85   - debugger-linecache (~> 1.2.0)
86   - debugger-ruby_core_source (~> 1.2.4)
87   - debugger-linecache (1.2.0)
88   - debugger-ruby_core_source (1.2.4)
89 82 decent_exposure (2.3.0)
90 83 devise (3.1.1)
91 84 bcrypt-ruby (~> 3.0)
... ... @@ -133,7 +126,7 @@ GEM
133 126 json (~> 1.8)
134 127 multi_xml (>= 0.5.2)
135 128 httpauth (0.2.0)
136   - i18n (0.6.9)
  129 + i18n (0.6.11)
137 130 jira-ruby (0.1.2)
138 131 activesupport
139 132 oauth
... ... @@ -156,8 +149,6 @@ GEM
156 149 lighthouse-api (2.0)
157 150 activeresource (>= 3.0.0)
158 151 activesupport (>= 3.0.0)
159   - linecache (0.46)
160   - rbx-require-relative (> 0.0.4)
161 152 mail (2.5.4)
162 153 mime-types (~> 1.16)
163 154 treetop (~> 1.4.8)
... ... @@ -183,7 +174,7 @@ GEM
183 174 rails (>= 3.2.0)
184 175 railties (>= 3.2.0)
185 176 moped (1.5.1)
186   - multi_json (1.9.2)
  177 + multi_json (1.10.1)
187 178 multi_xml (0.5.5)
188 179 multipart-post (1.2.0)
189 180 net-scp (1.1.2)
... ... @@ -231,7 +222,7 @@ GEM
231 222 rest-client (~> 1.6.0)
232 223 pjax_rails (0.3.4)
233 224 jquery-rails
234   - polyglot (0.3.4)
  225 + polyglot (0.3.5)
235 226 premailer (1.7.3)
236 227 css_parser (>= 1.1.9)
237 228 htmlentities (>= 4.0.0)
... ... @@ -250,31 +241,30 @@ GEM
250 241 rack (>= 0.4)
251 242 rack-contrib (1.1.0)
252 243 rack (>= 0.9.1)
253   - rack-ssl (1.3.3)
  244 + rack-ssl (1.3.4)
254 245 rack
255 246 rack-ssl-enforcer (0.2.6)
256 247 rack-test (0.6.2)
257 248 rack (>= 1.0)
258   - rails (3.2.17)
259   - actionmailer (= 3.2.17)
260   - actionpack (= 3.2.17)
261   - activerecord (= 3.2.17)
262   - activeresource (= 3.2.17)
263   - activesupport (= 3.2.17)
  249 + rails (3.2.19)
  250 + actionmailer (= 3.2.19)
  251 + actionpack (= 3.2.19)
  252 + activerecord (= 3.2.19)
  253 + activeresource (= 3.2.19)
  254 + activesupport (= 3.2.19)
264 255 bundler (~> 1.0)
265   - railties (= 3.2.17)
  256 + railties (= 3.2.19)
266 257 rails_autolink (1.1.4)
267 258 rails (> 3.1)
268   - railties (3.2.17)
269   - actionpack (= 3.2.17)
270   - activesupport (= 3.2.17)
  259 + railties (3.2.19)
  260 + actionpack (= 3.2.19)
  261 + activesupport (= 3.2.19)
271 262 rack-ssl (~> 1.3.2)
272 263 rake (>= 0.8.7)
273 264 rdoc (~> 3.4)
274 265 thor (>= 0.14.6, < 2.0)
275 266 raindrops (0.12.0)
276   - rake (10.1.1)
277   - rbx-require-relative (0.0.9)
  267 + rake (10.3.2)
278 268 rdoc (3.12.2)
279 269 json (~> 1.4)
280 270 ref (1.0.5)
... ... @@ -296,11 +286,6 @@ GEM
296 286 rspec-core (~> 2.14.0)
297 287 rspec-expectations (~> 2.14.0)
298 288 rspec-mocks (~> 2.14.0)
299   - ruby-debug (0.10.4)
300   - columnize (>= 0.1)
301   - ruby-debug-base (~> 0.10.4.0)
302   - ruby-debug-base (0.10.4)
303   - linecache (>= 0.3)
304 289 ruby-fogbugz (0.1.1)
305 290 crack
306 291 rushover (0.3.0)
... ... @@ -330,7 +315,7 @@ GEM
330 315 therubyracer (0.12.0)
331 316 libv8 (~> 3.16.14.0)
332 317 ref
333   - thor (0.18.1)
  318 + thor (0.19.1)
334 319 thread_safe (0.1.3)
335 320 atomic
336 321 tilt (1.4.1)
... ... @@ -342,7 +327,7 @@ GEM
342 327 turbo-sprockets-rails3 (0.3.10)
343 328 railties (> 3.2.8, < 4.0.0)
344 329 sprockets (>= 2.0.0)
345   - tzinfo (0.3.38)
  330 + tzinfo (0.3.41)
346 331 uglifier (2.2.1)
347 332 execjs (>= 0.3.0)
348 333 multi_json (~> 1.0, >= 1.0.2)
... ... @@ -366,9 +351,9 @@ PLATFORMS
366 351 ruby
367 352  
368 353 DEPENDENCIES
369   - actionmailer (~> 3.2.17)
  354 + actionmailer (~> 3.2.19)
370 355 actionmailer_inline_css
371   - actionpack (~> 3.2.17)
  356 + actionpack (~> 3.2.19)
372 357 airbrake
373 358 better_errors
374 359 binding_of_caller
... ... @@ -378,7 +363,6 @@ DEPENDENCIES
378 363 capybara
379 364 coveralls
380 365 database_cleaner
381   - debugger
382 366 decent_exposure
383 367 devise
384 368 email_spec
... ... @@ -413,10 +397,9 @@ DEPENDENCIES
413 397 rack-ssl
414 398 rack-ssl-enforcer
415 399 rails_autolink
416   - railties (~> 3.2.17)
  400 + railties (~> 3.2.19)
417 401 ri_cal
418 402 rspec-rails
419   - ruby-debug
420 403 ruby-fogbugz
421 404 rushover
422 405 strong_parameters
... ...
README.md
... ... @@ -175,9 +175,6 @@ heroku config:add HEROKU=true
175 175 heroku config:add SECRET_TOKEN="$(bundle exec rake secret)"
176 176 heroku config:add ERRBIT_HOST=some-hostname.example.com
177 177 heroku config:add ERRBIT_EMAIL_FROM=example@example.com
178   -# This next line is required to access env variables during asset compilation.
179   -# For more info, go to this link: https://devcenter.heroku.com/articles/labs-user-env-compile
180   -heroku labs:enable user-env-compile
181 178 git push heroku master
182 179 ```
183 180  
... ...
app/controllers/api/v1/problems_controller.rb
1 1 class Api::V1::ProblemsController < ApplicationController
2 2 respond_to :json, :xml
  3 + FIELDS = %w{app_id app_name environment message where first_notice_at last_notice_at resolved resolved_at notices_count}
  4 +
  5 + def show
  6 + result = benchmark("[api/v1/problems_controller/show] query time") do
  7 + begin
  8 + Problem.only(FIELDS).find(params[:id])
  9 + rescue Mongoid::Errors::DocumentNotFound
  10 + head :not_found
  11 + return false
  12 + end
  13 + end
  14 +
  15 + respond_to do |format|
  16 + format.any(:html, :json) { render :json => result } # render JSON if no extension specified on path
  17 + format.xml { render :xml => result }
  18 + end
  19 + end
3 20  
4 21 def index
5 22 query = {}
6   - fields = %w{app_id app_name environment message where first_notice_at last_notice_at resolved resolved_at notices_count}
7 23  
8 24 if params.key?(:start_date) && params.key?(:end_date)
9 25 start_date = Time.parse(params[:start_date]).utc
... ... @@ -11,8 +27,8 @@ class Api::V1::ProblemsController &lt; ApplicationController
11 27 query = {:first_notice_at=>{"$lte"=>end_date}, "$or"=>[{:resolved_at=>nil}, {:resolved_at=>{"$gte"=>start_date}}]}
12 28 end
13 29  
14   - results = benchmark("[api/v1/problems_controller] query time") do
15   - Problem.where(query).with(:consistency => :strong).only(fields).to_a
  30 + results = benchmark("[api/v1/problems_controller/index] query time") do
  31 + Problem.where(query).with(:consistency => :strong).only(FIELDS).to_a
16 32 end
17 33  
18 34 respond_to do |format|
... ...
app/controllers/apps_controller.rb
... ... @@ -36,6 +36,10 @@ class AppsController &lt; ApplicationController
36 36 app.deploys.order_by(:created_at.desc).limit(5)
37 37 }
38 38  
  39 + expose(:users) {
  40 + User.all.sort_by {|u| u.name.downcase }
  41 + }
  42 +
39 43 def index; end
40 44 def show
41 45 app
... ...
app/mailers/mailer.rb
... ... @@ -6,7 +6,12 @@ class Mailer &lt; ActionMailer::Base
6 6 helper ApplicationHelper
7 7 helper BacktraceLineHelper
8 8  
9   - default :from => Errbit::Config.email_from
  9 + default :from => Errbit::Config.email_from,
  10 + 'X-Errbit-Host' => Errbit::Config.host,
  11 + 'X-Mailer' => 'Errbit',
  12 + 'X-Auto-Response-Suppress' => 'OOF, AutoReply',
  13 + 'Precedence' => 'bulk',
  14 + 'Auto-Submitted' => 'auto-generated'
10 15  
11 16 def err_notification(notice)
12 17 @notice = notice
... ... @@ -15,6 +20,10 @@ class Mailer &lt; ActionMailer::Base
15 20 count = @notice.similar_count
16 21 count = count > 1 ? "(#{count}) " : ""
17 22  
  23 + errbit_headers 'App' => @app.name,
  24 + 'Environment' => @notice.environment_name,
  25 + 'Error-Id' => @notice.err_id
  26 +
18 27 mail :to => @app.notification_recipients,
19 28 :subject => "#{count}[#{@app.name}][#{@notice.environment_name}] #{@notice.message.truncate(50)}"
20 29 end
... ... @@ -23,6 +32,11 @@ class Mailer &lt; ActionMailer::Base
23 32 @deploy = deploy
24 33 @app = deploy.app
25 34  
  35 + errbit_headers 'App' => @app.name,
  36 + 'Environment' => @deploy.environment,
  37 + 'Deploy-Revision' => @deploy.revision,
  38 + 'Deploy-User' => @deploy.username
  39 +
26 40 mail :to => @app.notification_recipients,
27 41 :subject => "[#{@app.name}] Deployed to #{@deploy.environment} by #{@deploy.username}"
28 42 end
... ... @@ -36,7 +50,18 @@ class Mailer &lt; ActionMailer::Base
36 50  
37 51 recipients = @comment.notification_recipients
38 52  
  53 + errbit_headers 'App' => @app.name,
  54 + 'Environment' => @notice.environment_name,
  55 + 'Problem-Id' => @problem.id,
  56 + 'Comment-Author' => @user.name
  57 +
39 58 mail :to => recipients,
40 59 :subject => "#{@user.name} commented on [#{@app.name}][#{@notice.environment_name}] #{@notice.message.truncate(50)}"
41 60 end
  61 +
  62 + private
  63 +
  64 + def errbit_headers(header)
  65 + header.each { |key,value| headers["X-Errbit-#{key}"] = value.to_s }
  66 + end
42 67 end
... ...
app/models/error_report.rb
... ... @@ -15,15 +15,18 @@ require &#39;hoptoad_notifier&#39;
15 15 # * <tt>:notifier</tt> - information to identify the source of the error report
16 16 #
17 17 class ErrorReport
18   - attr_reader :error_class, :message, :request, :server_environment, :api_key, :notifier, :user_attributes, :framework
  18 + attr_reader :error_class, :message, :request, :server_environment, :api_key,
  19 + :notifier, :user_attributes, :framework, :notice
19 20  
20 21 cattr_accessor :fingerprint_strategy do
21 22 Fingerprint
22 23 end
23 24  
24 25 def initialize(xml_or_attributes)
25   - @attributes = (xml_or_attributes.is_a?(String) ? Hoptoad.parse_xml!(xml_or_attributes) : xml_or_attributes).with_indifferent_access
26   - @attributes.each{|k, v| instance_variable_set(:"@#{k}", v) }
  26 + @attributes = xml_or_attributes
  27 + @attributes = Hoptoad.parse_xml!(@attributes) if @attributes.is_a? String
  28 + @attributes = @attributes.with_indifferent_access
  29 + @attributes.each { |k, v| instance_variable_set(:"@#{k}", v) }
27 30 end
28 31  
29 32 def rails_env
... ... @@ -33,30 +36,29 @@ class ErrorReport
33 36 end
34 37  
35 38 def app
36   - @app ||= App.where(:api_key => api_key).first
  39 + @app ||= App.where(api_key: api_key).first
37 40 end
38 41  
39 42 def backtrace
40   - @normalized_backtrace ||= Backtrace.find_or_create(:raw => @backtrace)
  43 + @normalized_backtrace ||= Backtrace.find_or_create(raw: @backtrace)
41 44 end
42 45  
43 46 def generate_notice!
44 47 return unless valid?
45 48 return @notice if @notice
46 49 @notice = Notice.new(
47   - :message => message,
48   - :error_class => error_class,
49   - :backtrace_id => backtrace.id,
50   - :request => request,
51   - :server_environment => server_environment,
52   - :notifier => notifier,
53   - :user_attributes => user_attributes,
54   - :framework => framework
  50 + message: message,
  51 + error_class: error_class,
  52 + backtrace_id: backtrace.id,
  53 + request: request,
  54 + server_environment: server_environment,
  55 + notifier: notifier,
  56 + user_attributes: user_attributes,
  57 + framework: framework
55 58 )
56 59 error.notices << @notice
57 60 @notice
58 61 end
59   - attr_reader :notice
60 62  
61 63 ##
62 64 # Error associate to this error_report
... ... @@ -66,23 +68,22 @@ class ErrorReport
66 68 # @return [ Error ]
67 69 def error
68 70 @error ||= app.find_or_create_err!(
69   - :error_class => error_class,
70   - :environment => rails_env,
71   - :fingerprint => fingerprint
  71 + error_class: error_class,
  72 + environment: rails_env,
  73 + fingerprint: fingerprint
72 74 )
73 75 end
74 76  
75 77 def valid?
76   - !!app
  78 + app.present?
77 79 end
78 80  
79 81 def should_keep?
80 82 app_version = server_environment['app-version'] || ''
81   - if self.app.current_app_version.present? && ( app_version.length <= 0 || Gem::Version.new(app_version) < Gem::Version.new(self.app.current_app_version) )
82   - false
83   - else
84   - true
85   - end
  83 + current_version = app.current_app_version
  84 + return true unless current_version.present?
  85 + return false if app_version.length <= 0
  86 + Gem::Version.new(app_version) >= Gem::Version.new(current_version)
86 87 end
87 88  
88 89 private
... ... @@ -90,5 +91,4 @@ class ErrorReport
90 91 def fingerprint
91 92 @fingerprint ||= fingerprint_strategy.generate(notice, api_key)
92 93 end
93   -
94 94 end
... ...
app/models/notice.rb
... ... @@ -78,6 +78,17 @@ class Notice
78 78 "N/A"
79 79 end
80 80  
  81 + def to_curl
  82 + return "N/A" if url.blank?
  83 + headers = %w(Accept Accept-Encoding Accept-Language Cookie Referer User-Agent).each_with_object([]) do |name, h|
  84 + if value = env_vars["HTTP_#{name.underscore.upcase}"]
  85 + h << "-H '#{name}: #{value}'"
  86 + end
  87 + end
  88 +
  89 + "curl -X #{env_vars['REQUEST_METHOD'] || 'GET'} #{headers.join(' ')} #{url}"
  90 + end
  91 +
81 92 def env_vars
82 93 request['cgi-data'] || {}
83 94 end
... ...
app/models/notification_service.rb
... ... @@ -54,6 +54,6 @@ class NotificationService
54 54 end
55 55  
56 56 def problem_url(problem)
57   - "http://#{Errbit::Config.host}/apps/#{problem.app.id}/problems/#{problem.id}"
  57 + "#{Errbit::Config.protocol}://#{Errbit::Config.host}/apps/#{problem.app.id}/problems/#{problem.id}"
58 58 end
59 59 end
... ...
app/models/notification_services/campfire_service.rb
... ... @@ -30,7 +30,7 @@ if defined? Campy
30 30 # build the campfire client
31 31 campy = Campy::Room.new(:account => subdomain, :token => api_token, :room_id => room_id)
32 32 # post the issue to the campfire room
33   - campy.speak "[errbit] #{problem.app.name} #{notification_description problem} - http://#{Errbit::Config.host}/apps/#{problem.app.id.to_s}/problems/#{problem.id.to_s}"
  33 + campy.speak "[errbit] #{problem.app.name} #{notification_description problem} - #{Errbit::Config.protocol}://#{Errbit::Config.host}/apps/#{problem.app.id.to_s}/problems/#{problem.id.to_s}"
34 34 end
35 35 end
36 36 end
... ...
app/models/notification_services/gtalk_service.rb
... ... @@ -47,7 +47,7 @@ class NotificationServices::GtalkService &lt; NotificationService
47 47  
48 48 #has to look like this to be formatted properly in the client
49 49 message = """#{problem.app.name.to_s}
50   -http://#{Errbit::Config.host}/apps/#{problem.app.id.to_s}
  50 +#{Errbit::Config.protocol}://#{Errbit::Config.host}/apps/#{problem.app.id.to_s}
51 51 #{notification_description problem}"""
52 52  
53 53 # post the issue to the xmpp room(s)
... ...
app/models/notification_services/hoiio_service.rb
... ... @@ -35,7 +35,7 @@ class NotificationServices::HoiioService &lt; NotificationService
35 35  
36 36 # send sms
37 37 room_id.split(',').each do |number|
38   - sms.send :dest => number, :msg => "http://#{Errbit::Config.host}/apps/#{problem.app.id.to_s} #{notification_description problem}"
  38 + sms.send :dest => number, :msg => "#{Errbit::Config.protocol}://#{Errbit::Config.host}/apps/#{problem.app.id.to_s} #{notification_description problem}"
39 39 end
40 40  
41 41 end
... ...
app/models/notification_services/pushover_service.rb
... ... @@ -26,7 +26,7 @@ class NotificationServices::PushoverService &lt; NotificationService
26 26 notification = Rushover::Client.new(subdomain)
27 27  
28 28 # send push notification to pushover
29   - notification.notify(api_token, "#{notification_description problem}", :priority => 1, :title => "Errbit Notification", :url => "http://#{Errbit::Config.host}/apps/#{problem.app.id.to_s}", :url_title => "Link to error")
  29 + notification.notify(api_token, "#{notification_description problem}", :priority => 1, :title => "Errbit Notification", :url => "#{Errbit::Config.protocol}://#{Errbit::Config.host}/apps/#{problem.app.id.to_s}", :url_title => "Link to error")
30 30  
31 31 end
32 32 end
... ...
app/models/problem.rb
... ... @@ -50,7 +50,6 @@ class Problem
50 50  
51 51 validates_presence_of :last_notice_at, :first_notice_at
52 52  
53   -
54 53 def self.all_else_unresolved(fetch_all)
55 54 if fetch_all
56 55 all
... ...
app/views/apps/_fields.html.haml
... ... @@ -55,7 +55,7 @@
55 55 = w.radio_button :watcher_type, :email
56 56 = label_tag :watcher_type_email, 'Email Address', :for => label_for_attr(w, 'watcher_type_email')
57 57 %div.watcher_params.user{:class => w.object.email.blank? ? 'chosen' : nil}
58   - = w.select :user_id, User.all.map{|u| [u.name,u.id.to_s]}, :include_blank => '-- Select a User --'
  58 + = w.select :user_id, users.map{|u| [u.name,u.id.to_s]}, :include_blank => '-- Select a User --'
59 59 %div.watcher_params.email{:class => w.object.email.present? ? 'chosen' : nil}
60 60 = w.text_field :email
61 61  
... ... @@ -65,4 +65,3 @@
65 65  
66 66 = render "issue_tracker_fields", :f => f
67 67 = render "service_notification_fields", :f => f
68   -
... ...
app/views/notices/_summary.html.haml
... ... @@ -10,6 +10,9 @@
10 10 %tr
11 11 %th URL
12 12 %td.nowrap= link_to notice.request['url'], notice.request['url']
  13 + %tr
  14 + %th &nbsp;
  15 + %td= notice.to_curl
13 16 %tr
14 17 %th Where
15 18 %td= notice.where
... ...
config/cloud/cloud66/files/_load_config.rb
... ... @@ -17,6 +17,7 @@ unless defined?(Errbit::Config)
17 17 Errbit::Config.use_gravatar = ENV['ERRBIT_USE_GRAVATAR']
18 18 Errbit::Config.gravatar_default = ENV['ERRBIT_GRAVATAR_DEFAULT']
19 19  
  20 + Errbit::Config.github_url = ENV['GITHUB_URL']
20 21 Errbit::Config.github_authentication = ENV['GITHUB_AUTHENTICATION']
21 22 Errbit::Config.github_client_id = ENV['GITHUB_CLIENT_ID']
22 23 Errbit::Config.github_secret = ENV['GITHUB_SECRET']
... ...
config/initializers/_load_config.rb
... ... @@ -6,9 +6,12 @@ unless defined?(Errbit::Config)
6 6 Errbit::Config = OpenStruct.new
7 7 use_env = ENV['HEROKU'] || ENV['USE_ENV']
8 8  
  9 + Errbit::Config.protocol = 'http'
  10 +
9 11 # If Errbit is running on Heroku, config can be set from environment variables.
10 12 if use_env
11 13 Errbit::Config.host = ENV['ERRBIT_HOST']
  14 + Errbit::Config.protocol = ENV['ERRBIT_PROTOCOL'] || 'http'
12 15 Errbit::Config.port = ENV['ERRBIT_PORT']
13 16 Errbit::Config.email_from = ENV['ERRBIT_EMAIL_FROM']
14 17 # Not really easy to use like an env because need an array and ENV return a string :(
... ... @@ -34,7 +37,8 @@ unless defined?(Errbit::Config)
34 37 :authentication => :plain,
35 38 :user_name => ENV['SMTP_USERNAME'] || ENV['SENDGRID_USERNAME'],
36 39 :password => ENV['SMTP_PASSWORD'] || ENV['SENDGRID_PASSWORD'],
37   - :domain => ENV['SMTP_DOMAIN'] || ENV['SENDGRID_DOMAIN'] || ENV['ERRBIT_EMAIL_FROM'].split('@').last
  40 + :domain => ENV['SMTP_DOMAIN'] || ENV['SENDGRID_DOMAIN'] ||
  41 + (ENV['ERRBIT_EMAIL_FROM'] ? ENV['ERRBIT_EMAIL_FROM'].split('@').last : nil)
38 42 }
39 43 end
40 44  
... ...
config/initializers/mongo.rb
... ... @@ -6,7 +6,7 @@ if config_file.file? &amp;&amp;
6 6 elsif ENV['HEROKU'] || ENV['USE_ENV']
7 7 # No mongoid.yml file. Use ENV variable to define your MongoDB
8 8 # configuration
9   - if mongo = ENV['MONGOLAB_URI'] || ENV['MONGOHQ_URL'] || ENV['MONGODB_URL']
  9 + if mongo = ENV['MONGOLAB_URI'] || ENV['MONGOHQ_URL'] || ENV['MONGODB_URL'] || ENV['MONGO_URL']
10 10 settings = URI.parse(mongo)
11 11 database_name = settings.path.gsub(/^\//, '')
12 12 else
... ...
config/routes.rb
... ... @@ -50,7 +50,7 @@ Errbit::Application.routes.draw do
50 50  
51 51 namespace :api do
52 52 namespace :v1 do
53   - resources :problems, :only => [:index], :defaults => { :format => 'json' }
  53 + resources :problems, :only => [:index, :show], :defaults => { :format => 'json' }
54 54 resources :notices, :only => [:index], :defaults => { :format => 'json' }
55 55 resources :stats, :only => [], :defaults => { :format => 'json' } do
56 56 collection do
... ...
lib/tasks/errbit/demo.rake
... ... @@ -49,7 +49,8 @@ namespace :errbit do
49 49 :backtrace => random_backtrace,
50 50 :request => {
51 51 'component' => 'main',
52   - 'action' => 'error'
  52 + 'action' => 'error',
  53 + 'url' => "http://example.com/post/#{[111, 222, 333].sample}",
53 54 },
54 55 :server_environment => {'environment-name' => Rails.env.to_s},
55 56 :notifier => {:name => "seeds.rb"},
... ...
spec/controllers/api/v1/problems_controller_spec.rb
... ... @@ -7,6 +7,70 @@ describe Api::V1::ProblemsController do
7 7 @user = Fabricate(:user)
8 8 end
9 9  
  10 + describe "GET /api/v1/problems/:id" do
  11 + before do
  12 + notice = Fabricate(:notice)
  13 + err = Fabricate(:err, :notices => [notice])
  14 + @problem = Fabricate(:problem, :errs => [err])
  15 + end
  16 +
  17 + it "should return JSON if JSON is requested" do
  18 + get :show, :auth_token => @user.authentication_token, :format => "json", :id => Problem.first.id
  19 + expect { JSON.load(response.body) }.not_to raise_error() #JSON::ParserError
  20 + end
  21 +
  22 + it "should return XML if XML is requested" do
  23 + get :index, :auth_token => @user.authentication_token, :format => "xml", :id => @problem.id
  24 + expect(Nokogiri::XML(response.body).errors).to be_empty
  25 + end
  26 +
  27 + it "should return JSON by default" do
  28 + get :show, :auth_token => @user.authentication_token, :id => @problem.id
  29 + expect { JSON.load(response.body) }.not_to raise_error()#JSON::ParserError)
  30 + end
  31 +
  32 + it "should return the correct problem" do
  33 + get :show, :auth_token => @user.authentication_token, :format => "json", :id => @problem.id
  34 +
  35 + returned_problem = JSON.parse(response.body)
  36 + expect( returned_problem["_id"] ).to eq(@problem.id.to_s)
  37 + end
  38 +
  39 + it "should return only the correct fields" do
  40 + get :show, :auth_token => @user.authentication_token, :format => "json", :id => @problem.id
  41 + returned_problem = JSON.parse(response.body)
  42 +
  43 + expect( returned_problem.keys ).to match_array([
  44 + "app_name",
  45 + "first_notice_at",
  46 + "error_class",
  47 + "messages",
  48 + "hosts",
  49 + "created_at",
  50 + "app_id",
  51 + "last_notice_at",
  52 + "_id",
  53 + "issue_link",
  54 + "resolved",
  55 + "updated_at",
  56 + "resolved_at",
  57 + "last_deploy_at",
  58 + "where",
  59 + "issue_type",
  60 + "notices_count",
  61 + "user_agents",
  62 + "comments_count",
  63 + "message",
  64 + "environment"
  65 + ])
  66 + end
  67 +
  68 + it "returns a 404 if the problem cannot be found" do
  69 + get :show, :auth_token => @user.authentication_token, :format => "json", :id => 'IdontExist'
  70 + expect( response.status ).to eq(404)
  71 + end
  72 + end
  73 +
10 74 describe "GET /api/v1/problems" do
11 75 before do
12 76 Fabricate(:problem, :first_notice_at => Date.new(2012, 8, 01), :resolved_at => Date.new(2012, 8, 02))
... ...
spec/controllers/apps_controller_spec.rb
... ... @@ -73,6 +73,16 @@ describe AppsController do
73 73 expect(response).to be_success
74 74 end
75 75  
  76 + it "should list available watchers by name" do
  77 + Fabricate(:user, :name => "Carol")
  78 + Fabricate(:user, :name => "Alice")
  79 + Fabricate(:user, :name => "Betty")
  80 +
  81 + get :show, :id => app.id
  82 +
  83 + expect(controller.users.to_a).to eq(User.all.to_a.sort_by(&:name))
  84 + end
  85 +
76 86 context "pagination" do
77 87 before(:each) do
78 88 35.times { Fabricate(:err, :problem => Fabricate(:problem, :app => app)) }
... ... @@ -392,4 +402,3 @@ describe AppsController do
392 402 end
393 403  
394 404 end
395   -
... ...
spec/mailers/mailer_spec.rb
1 1 require 'spec_helper'
2 2  
  3 +shared_examples "a notification email" do
  4 + it "should have X-Mailer header" do
  5 + expect(@email).to have_header('X-Mailer', 'Errbit')
  6 + end
  7 +
  8 + it "should have X-Errbit-Host header" do
  9 + expect(@email).to have_header('X-Errbit-Host', Errbit::Config.host)
  10 + end
  11 +
  12 + it "should have Precedence header" do
  13 + expect(@email).to have_header('Precedence', 'bulk')
  14 + end
  15 +
  16 + it "should have Auto-Submitted header" do
  17 + expect(@email).to have_header('Auto-Submitted', 'auto-generated')
  18 + end
  19 +
  20 + it "should have X-Auto-Response-Suppress header" do
  21 + # http://msdn.microsoft.com/en-us/library/ee219609(v=EXCHG.80).aspx
  22 + expect(@email).to have_header('X-Auto-Response-Suppress', 'OOF, AutoReply')
  23 + end
  24 +
  25 + it "should send the email" do
  26 + expect(ActionMailer::Base.deliveries.size).to eq 1
  27 + end
  28 +end
  29 +
3 30 describe Mailer do
4 31 context "Err Notification" do
5 32 include EmailSpec::Helpers
... ... @@ -19,9 +46,8 @@ describe Mailer do
19 46 @email = Mailer.err_notification(notice).deliver
20 47 end
21 48  
22   - it "should send the email" do
23   - expect(ActionMailer::Base.deliveries.size).to eq 1
24   - end
  49 + it_should_behave_like "a notification email"
  50 +
25 51  
26 52 it "should html-escape the notice's message for the html part" do
27 53 expect(@email).to have_body_text("class &lt; ActionController::Base")
... ... @@ -62,10 +88,6 @@ describe Mailer do
62 88 @email = Mailer.comment_notification(comment).deliver
63 89 end
64 90  
65   - it "should send the email" do
66   - expect(ActionMailer::Base.deliveries.size).to eq 1
67   - end
68   -
69 91 it "should be sent to comment notification recipients" do
70 92 expect(@email.to).to eq recipients
71 93 end
... ...
spec/models/notice_spec.rb
... ... @@ -35,6 +35,28 @@ describe Notice do
35 35 end
36 36 end
37 37  
  38 + describe "to_curl" do
  39 + let(:notice) { Fabricate.build(:notice, request: request) }
  40 +
  41 + context "when it has a request url" do
  42 + let(:request) { {'url' => "http://example.com/resource/12", 'cgi-data' => {'HTTP_USER_AGENT' => 'Mozilla/5.0'}} }
  43 +
  44 + it 'has a curl representation' do
  45 + cmd = notice.to_curl
  46 + expect(cmd).to eq(%q[curl -X GET -H 'User-Agent: Mozilla/5.0' http://example.com/resource/12])
  47 + end
  48 + end
  49 +
  50 + context "when it has not a request url" do
  51 + let(:request) { {'cgi-data' => {'HTTP_USER_AGENT' => 'Mozilla/5.0'}} }
  52 +
  53 + it 'has a curl representation' do
  54 + cmd = notice.to_curl
  55 + expect(cmd).to eq "N/A"
  56 + end
  57 + end
  58 + end
  59 +
38 60 describe "user agent" do
39 61 it "should be parsed and human-readable" do
40 62 notice = Fabricate.build(:notice, :request => {'cgi-data' => {
... ...
spec/models/notification_service/gtalk_service_spec.rb
... ... @@ -17,7 +17,7 @@ describe NotificationService::GtalkService do
17 17 expect(gtalk).to receive(:connect).with(notification_service.service)
18 18 expect(gtalk).to receive(:auth).with(notification_service.api_token)
19 19 message_value = """#{problem.app.name.to_s}
20   -http://#{Errbit::Config.host}/apps/#{problem.app.id.to_s}
  20 +#{Errbit::Config.protocol}://#{Errbit::Config.host}/apps/#{problem.app.id.to_s}
21 21 #{notification_service.notification_description problem}"""
22 22  
23 23 expect(Jabber::Message).to receive(:new).with(notification_service.user_id, message_value).and_return(message)
... ... @@ -39,7 +39,7 @@ http://#{Errbit::Config.host}/apps/#{problem.app.id.to_s}
39 39 @notification_service = Fabricate :gtalk_notification_service, :app => @notice.app
40 40 @problem = @notice.problem
41 41 @error_msg = """#{@problem.app.name.to_s}
42   -http://#{Errbit::Config.host}/apps/#{@problem.app.id.to_s}
  42 +#{Errbit::Config.protocol}://#{Errbit::Config.host}/apps/#{@problem.app.id.to_s}
43 43 #{@notification_service.notification_description @problem}"""
44 44  
45 45 # gtalk stubbing
... ... @@ -105,7 +105,7 @@ http://#{Errbit::Config.host}/apps/#{@problem.app.id.to_s}
105 105 expect(gtalk).to receive(:connect)
106 106 expect(gtalk).to receive(:auth).with(notification_service.api_token)
107 107 message_value = """#{problem.app.name.to_s}
108   -http://#{Errbit::Config.host}/apps/#{problem.app.id.to_s}
  108 +#{Errbit::Config.protocol}://#{Errbit::Config.host}/apps/#{problem.app.id.to_s}
109 109 #{notification_service.notification_description problem}"""
110 110  
111 111 expect(Jabber::Message).to receive(:new).with(notification_service.room_id, message_value).and_return(message)
... ...
spec/models/notification_service/notification_service_spec.rb 0 → 100644
... ... @@ -0,0 +1,18 @@
  1 +require 'spec_helper'
  2 +
  3 +describe NotificationService do
  4 +
  5 + let(:notice) { Fabricate :notice }
  6 + let(:notification_service) { Fabricate :notification_service, :app => notice.app }
  7 + let(:problem) { notice.problem }
  8 +
  9 + it "it should use http by default in #problem_url" do
  10 + notification_service.problem_url(problem).should start_with 'http://'
  11 + end
  12 +
  13 + it "it should use the protocol value specified in the config in #problem_url" do
  14 + Errbit::Config.protocol = 'https'
  15 + notification_service.problem_url(problem).should start_with 'https://'
  16 + end
  17 +
  18 +end
... ...