Commit 93cf2659cdad67e293b49e0dcd54b01e8425d072
Exists in
master
and in
1 other branch
Merge remote-tracking branch 'upstream/master'
Showing
29 changed files
with
290 additions
and
110 deletions
Show diff stats
.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 < 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
app/mailers/mailer.rb
| ... | ... | @@ -6,7 +6,12 @@ class Mailer < 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 < 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 < 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 < 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 'hoptoad_notifier' |
| 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 < 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 < 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 < 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
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
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? && |
| 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 < 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 | ... | ... |