Commit 9c79849276ec0a292cc6fb5591837abe3d76aa83

Authored by Stephen Crosby
2 parents ccadc6a2 759a2c9a
Exists in master and in 1 other branch production

Merge branch 'master' into features/extract_issue_tracker

Conflicts:
	Gemfile.lock
.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.18'
  3 +RAILS_VERSION = '~> 3.2.19'
4 4  
5 5 gem 'actionmailer', RAILS_VERSION
6 6 gem 'actionpack', RAILS_VERSION
... ...
Gemfile.lock
1 1 GEM
2 2 remote: https://rubygems.org/
3 3 specs:
4   - actionmailer (3.2.18)
5   - actionpack (= 3.2.18)
  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.18)
12   - activemodel (= 3.2.18)
13   - activesupport (= 3.2.18)
  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.18)
22   - activesupport (= 3.2.18)
  21 + activemodel (3.2.19)
  22 + activesupport (= 3.2.19)
23 23 builder (~> 3.0.0)
24   - activerecord (3.2.18)
25   - activemodel (= 3.2.18)
26   - activesupport (= 3.2.18)
  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.18)
30   - activemodel (= 3.2.18)
31   - activesupport (= 3.2.18)
32   - activesupport (3.2.18)
  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.6)
... ... @@ -177,7 +177,7 @@ GEM
177 177 kgio (2.8.1)
178 178 launchy (2.3.0)
179 179 addressable (~> 2.3)
180   - libv8 (3.16.14.3)
  180 + libv8 (3.16.14.7)
181 181 libxml-ruby (2.7.0)
182 182 lighthouse-api (2.0)
183 183 activeresource (>= 3.0.0)
... ... @@ -260,7 +260,7 @@ GEM
260 260 cliver (~> 0.3.1)
261 261 multi_json (~> 1.0)
262 262 websocket-driver (>= 0.2.0)
263   - polyglot (0.3.4)
  263 + polyglot (0.3.5)
264 264 premailer (1.7.3)
265 265 css_parser (>= 1.1.9)
266 266 htmlentities (>= 4.0.0)
... ... @@ -284,25 +284,25 @@ GEM
284 284 rack-ssl-enforcer (0.2.6)
285 285 rack-test (0.6.2)
286 286 rack (>= 1.0)
287   - rails (3.2.18)
288   - actionmailer (= 3.2.18)
289   - actionpack (= 3.2.18)
290   - activerecord (= 3.2.18)
291   - activeresource (= 3.2.18)
292   - activesupport (= 3.2.18)
  287 + rails (3.2.19)
  288 + actionmailer (= 3.2.19)
  289 + actionpack (= 3.2.19)
  290 + activerecord (= 3.2.19)
  291 + activeresource (= 3.2.19)
  292 + activesupport (= 3.2.19)
293 293 bundler (~> 1.0)
294   - railties (= 3.2.18)
  294 + railties (= 3.2.19)
295 295 rails_autolink (1.1.4)
296 296 rails (> 3.1)
297   - railties (3.2.18)
298   - actionpack (= 3.2.18)
299   - activesupport (= 3.2.18)
  297 + railties (3.2.19)
  298 + actionpack (= 3.2.19)
  299 + activesupport (= 3.2.19)
300 300 rack-ssl (~> 1.3.2)
301 301 rake (>= 0.8.7)
302 302 rdoc (~> 3.4)
303 303 thor (>= 0.14.6, < 2.0)
304 304 raindrops (0.12.0)
305   - rake (10.3.1)
  305 + rake (10.3.2)
306 306 rdoc (3.12.2)
307 307 json (~> 1.4)
308 308 ref (1.0.5)
... ... @@ -352,7 +352,7 @@ GEM
352 352 railties (~> 3.0)
353 353 term-ansicolor (1.2.2)
354 354 tins (~> 0.8)
355   - therubyracer (0.12.0)
  355 + therubyracer (0.12.1)
356 356 libv8 (~> 3.16.14.0)
357 357 ref
358 358 thor (0.19.1)
... ... @@ -367,7 +367,7 @@ GEM
367 367 turbo-sprockets-rails3 (0.3.10)
368 368 railties (> 3.2.8, < 4.0.0)
369 369 sprockets (>= 2.0.0)
370   - tzinfo (0.3.39)
  370 + tzinfo (0.3.41)
371 371 uglifier (2.2.1)
372 372 execjs (>= 0.3.0)
373 373 json (>= 1.8.0)
... ... @@ -392,9 +392,9 @@ PLATFORMS
392 392 ruby
393 393  
394 394 DEPENDENCIES
395   - actionmailer (~> 3.2.18)
  395 + actionmailer (~> 3.2.19)
396 396 actionmailer_inline_css
397   - actionpack (~> 3.2.18)
  397 + actionpack (~> 3.2.19)
398 398 airbrake
399 399 better_errors
400 400 binding_of_caller
... ... @@ -443,7 +443,7 @@ DEPENDENCIES
443 443 rack-ssl
444 444 rack-ssl-enforcer
445 445 rails_autolink
446   - railties (~> 3.2.18)
  446 + railties (~> 3.2.19)
447 447 ri_cal
448 448 rspec-rails
449 449 rushover
... ...
README.md
... ... @@ -78,25 +78,20 @@ There is a demo available at [http://errbit-demo.herokuapp.com/](http://errbit-d
78 78 Email: demo@errbit-demo.herokuapp.com<br/>
79 79 Password: password
80 80  
81   -# Requirement
  81 +# Requirements
82 82  
83   -The list of requirement to install Errbit is :
  83 +The list of requirements to install Errbit are :
84 84  
85 85 * Ruby 1.9.3 or higher
86 86 * MongoDB 2.2.0 or higher
87 87  
88   -By default it's the Ruby 2.0.0 to use. But you can define your own ruby
89   -version with RUBY_VERSION variable between :
90   -
91   - * 1.9.3
92   - * 2.0.0
93   - * 2.1.0
  88 +Errbit uses Ruby 2.0.0 as a default. However, it is compatible with Ruby 1.9.3 and above.
94 89  
95 90 Installation
96 91 ------------
97 92  
98 93 *Note*: This app is intended for people with experience deploying and maintaining
99   -Rails applications. If you're uncomfortable with any step below then Errbit is not
  94 +Rails applications. If you're uncomfortable with any steps below then Errbit is not
100 95 for you.
101 96  
102 97 **Set up your local box or server(Ubuntu):**
... ... @@ -108,10 +103,10 @@ apt-get update
108 103 apt-get install mongodb-10gen
109 104 ```
110 105  
111   - * Install libxml and libcurl
  106 + * Install libxml, libzip, libssl and libcurl
112 107  
113 108 ```bash
114   -apt-get install libxml2 libxml2-dev libxslt-dev libcurl4-openssl-dev
  109 +apt-get install libxml2 libxml2-dev libxslt-dev libcurl4-openssl-dev libzip-dev libssl-dev
115 110 ```
116 111  
117 112 * Install Bundler
... ... @@ -179,9 +174,6 @@ heroku config:add HEROKU=true
179 174 heroku config:add SECRET_TOKEN="$(bundle exec rake secret)"
180 175 heroku config:add ERRBIT_HOST=some-hostname.example.com
181 176 heroku config:add ERRBIT_EMAIL_FROM=example@example.com
182   -# This next line is required to access env variables during asset compilation.
183   -# For more info, go to this link: https://devcenter.heroku.com/articles/labs-user-env-compile
184   -heroku labs:enable user-env-compile
185 177 git push heroku master
186 178 ```
187 179  
... ... @@ -360,17 +352,16 @@ rake db:migrate
360 352 rake assets:precompile
361 353 ```
362 354  
363   -If we change the way that data is stored, this will run any migrations to bring your database up to date.
  355 +This will ensure that your application stays up to date with any schema changes.
364 356  
365 357  
366   -### Upgrade from errbit 0.2 to 0.3
  358 +### Upgrading errbit from version 0.2 to 0.3
367 359  
368   -The file of MongoDB connection config/mongoid.yml change between 0.2 to
369   -0.3. So Check the new config/mongoid.yml.example file and update it in
370   -good way.
  360 +The MongoDB connection file `config/mongoid.yml` has changed between version 0.2 and
  361 +0.3. We have provided a new example configuration file to use at `config/mongoid.example.yml`.
371 362  
372   -This change is not need to be done if you use only ENV variable to
373   -define you access to MongoDB database.
  363 +This change is not needed if you use ENV variables to
  364 +define access to your MongoDB database.
374 365  
375 366  
376 367 ## User information in error reports
... ... @@ -379,11 +370,12 @@ Errbit can now display information about the user who experienced an error.
379 370 This gives you the ability to ask the user for more information,
380 371 and let them know when you've fixed the bug.
381 372  
382   -If you would like to include information about the current user in your error reports,
383   -you can replace the `airbrake` gem in your Gemfile with `airbrake_user_attributes`,
384   -which wraps the `airbrake` gem and injects user information.
385   -It will inject information about the current user into the error report
386   -if your Rails app's controller responds to a `#current_user` method.
  373 +If you are running a Rails application and would like to include information
  374 +about the current user in your error reports, you can replace the `airbrake`
  375 +gem in your Gemfile with `airbrake_user_attributes`.
  376 +This gem is a wrapper around the `airbrake` gem and will automatically
  377 +inject information about the user into any error reports,
  378 +so long as your controllers respond to a `#current_user` method.
387 379 The user's attributes are filtered to remove authentication fields.
388 380  
389 381 If user information is received with an error report,
... ... @@ -394,26 +386,33 @@ it will be displayed under the *User Details* tab:
394 386  
395 387 (This tab will be hidden if no user information is available.)
396 388  
397   -Adding javascript errors notifications
  389 +Javascript error notifications
398 390 --------------------------------------
399 391  
400   -Errbit easily supports javascript errors notifications. You just need to add `config.js_notifier = true` to the errbit initializer in the rails app.
  392 +You can log javascript errors that occur in your application by following the directions below.
  393 +
  394 +# Rails Applications
  395 +
  396 +Add the following line to the `<head>` section of your application template.
  397 +
  398 +```
  399 +<%= airbrake_javascript_notifier %>
  400 +```
  401 +
  402 +# Other Platforms
  403 +
  404 +include the following before any javascript is loaded in your application.
401 405  
402 406 ```
403   -Errbit.configure do |config|
404   - config.host = 'YOUR-ERRBIT-HOST'
405   - config.api_key = 'YOUR-PROJECT-API-KEY'
406   - config.js_notifier = true
407   -end
  407 +<script src='http://YOUR-ERRBIT-HOST/javascripts/notifier.js' type='text/javascript'></script>
408 408 ```
409 409  
410   -Then get the `notifier.js` from `errbit/public/javascript/notifier.js` and add to `application.js` on your rails app or include `http://YOUR-ERRBIT-HOST/javascripts/notifier.js` on your `application.html.erb.`
411 410  
412 411 Using custom fingerprinting methods
413 412 -----------------------------------
414 413  
415   -Errbit now allows you to easily use your own Fingerprint Strategy if that's what you'd like to do. If you are upgrading from a very old version of errbit, you can use the `LegacyFingerprint` to provide yourself
416   -with compatibility. The fingerprint strategy can be changed by adding an initializer to errbit:
  414 +Errbit allows you to use your own Fingerprinting Strategy.
  415 +If you are upgrading from a very old version of errbit, you can use the `LegacyFingerprint` for compatibility. The fingerprint strategy can be changed by adding an initializer to errbit:
417 416  
418 417 ```ruby
419 418 # config/fingerprint.rb
... ... @@ -533,23 +532,18 @@ Solutions known to work are listed below:
533 532 <table>
534 533 <tr>
535 534 <th>PHP (&gt;= 5.3)</th>
536   - <td>https://github.com/flippa/errbit-php</td>
  535 + <td>[flippa/errbit-php](https://github.com/flippa/errbit-php)</td>
537 536 </tr>
538 537 <tr>
539 538 <th>OOP PHP (&gt;= 5.3)</th>
540   - <td>https://github.com/emgiezet/errbitPHP</td>
  539 + <td>[emgiezet/errbitPHP](https://github.com/emgiezet/errbitPHP)</td>
541 540 </tr>
542 541 <tr>
543 542 <th>Python</th>
544   - <td>https://github.com/mkorenkov/errbit.py , https://github.com/pulseenergy/airbrakepy</td>
  543 + <td>[mkorenkov/errbit.py](https://github.com/mkorenkov/errbit.py) , [pulseenergy/airbrakepy](https://github.com/pulseenergy/airbrakepy)</td>
545 544 </tr>
546 545 </table>
547 546  
548   -Develop on Errbit
549   ------------------
550   -
551   -A guide can help on this way on [**Errbit Advanced Developer Guide**](docs/DEVELOPER-ADVANCED.md)
552   -
553 547 ## Other documentation
554 548  
555 549 * [All ENV variables availables to configure Errbit](docs/ENV-VARIABLES.md)
... ... @@ -582,7 +576,7 @@ Special Thanks
582 576 See the [contributors graph](https://github.com/errbit/errbit/graphs/contributors) for further details. You can see another list of Contributors by release version on [CONTRIBUTORS.md]
583 577  
584 578  
585   -Contributing
  579 +Contributing to Errbit
586 580 ------------
587 581  
588 582 We welcome any contributions. If you need to tweak Errbit for your organization's needs,
... ... @@ -604,9 +598,13 @@ and make **optional** features configurable via `config/config.yml`.
604 598 * Send us a pull request. Bonus points for topic branches.
605 599 * Add you on the CONTRIBUTORS.md file on the current release
606 600  
  601 +# Running tests
  602 +
  603 +More information can be found in the [**Errbit Advanced Developer Guide**](docs/DEVELOPER-ADVANCED.md)
  604 +
607 605  
608 606 Copyright
609 607 ---------
610 608  
611   -Copyright (c) 2010-2013 Errbit Team. See LICENSE for details.
  609 +Copyright (c) 2010-2014 Errbit Team. See LICENSE for details.
612 610  
... ...
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/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/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/show.html.haml
... ... @@ -88,8 +88,7 @@
88 88 - if app.problems.any?
89 89 %h3.clear=t('.errors')
90 90 %section
91   - = form_tag search_problems_path(:all_errs => all_errs, :app_id => app.id), :method => :get, :remote => true do
92   - = text_field_tag :search, params[:search], :placeholder => t('.search_placeholder')
  91 + = render 'problems/search', :all_errs => @all_errs, :app_id => app.id
93 92 %br
94 93 %section
95 94 .problem_table{:id => 'problem_table'}
... ...
app/views/problems/_search.html.haml 0 → 100644
... ... @@ -0,0 +1,2 @@
  1 += form_tag search_problems_path(:all_errs => all_errs, :app_id => app_id), :method => :get, :remote => true do
  2 + = text_field_tag :search, params[:search], :placeholder => t('.search_placeholder')
... ...
app/views/problems/index.html.haml
... ... @@ -9,8 +9,7 @@
9 9 = link_to 'show resolved', problems_path(:all_errs => true), :class => 'button'
10 10  
11 11 %section
12   - = form_tag search_problems_path(:all_errs => @all_errs), :method => :get, :remote => true do
13   - = text_field_tag :search, params[:search], :placeholder => 'Search for issues'
  12 + = render 'problems/search', :all_errs => @all_errs, :app_id => nil
14 13 %br
15 14 %section
16 15 #problem_table.problem_table
... ...
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/locales/en.yml
... ... @@ -77,6 +77,8 @@ en:
77 77 merge: "Merge select issues? They can be unmerged later."
78 78 unmerge: "Unmerge selected issues? They can be re-merged later."
79 79 unresolve: "Unresolve selected issues? They can be resolved again later."
  80 + search:
  81 + search_placeholder: 'Search for issues'
80 82  
81 83 comments:
82 84 confirm_delete: "Permanently delete this comment?"
... ... @@ -118,7 +120,6 @@ en:
118 120 no_watcher: "Sadly, no one is watching this app"
119 121 repository: Repository
120 122 revision: Revision
121   - search_placeholder: 'Search for issues'
122 123 show_hide: "(show/hide)"
123 124 unresolved_errs: unresolved errs
124 125 unwatch: unwatch
... ...
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
... ...
public/robots.txt
1 1 # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file
2   -#
3   -# To ban all spiders from the entire site uncomment the next two lines:
4   -# User-Agent: *
5   -# Disallow: /
  2 +User-Agent: *
  3 +Disallow: /
... ...
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/problems_controller_spec.rb
... ... @@ -124,6 +124,31 @@ describe ProblemsController do
124 124 end
125 125 end
126 126  
  127 + describe "GET /problems/search" do
  128 + before do
  129 + sign_in Fabricate(:admin)
  130 + @app = Fabricate(:app)
  131 + @problem1 = Fabricate(:problem, :app=>@app, message: "Most important")
  132 + @problem2 = Fabricate(:problem, :app=>@app, message: "Very very important")
  133 + end
  134 +
  135 + it "renders successfully" do
  136 + get :search
  137 + expect(response).to be_success
  138 + end
  139 +
  140 + it "renders index template" do
  141 + get :search
  142 + expect(response).to render_template('problems/index')
  143 + end
  144 +
  145 + it "searches problems for given string" do
  146 + get :search, :search => "Most important"
  147 + expect(controller.problems).to include(@problem1)
  148 + expect(controller.problems).to_not include(@problem2)
  149 + end
  150 + end
  151 +
127 152 describe "GET /apps/:app_id/problems/:id" do
128 153 #render_views
129 154  
... ...
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/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
... ...