Commit 08a1ac92e9bbb23ca5699ff42592dc91d1f65a81
Exists in
master
and in
1 other branch
Merge branch 'email_at_notices_per_app' of https://github.com/crossroads/errbit into pull-request-52
Showing
10 changed files
with
100 additions
and
25 deletions
Show diff stats
app/controllers/apps_controller.rb
| ... | ... | @@ -2,6 +2,7 @@ class AppsController < ApplicationController |
| 2 | 2 | |
| 3 | 3 | before_filter :require_admin!, :except => [:index, :show] |
| 4 | 4 | before_filter :find_app, :except => [:index, :new, :create] |
| 5 | + before_filter :parse_email_at_notices_or_set_default, :only => [:create, :update] | |
| 5 | 6 | |
| 6 | 7 | def index |
| 7 | 8 | @apps = current_user.admin? ? App.all : current_user.apps.all |
| ... | ... | @@ -73,4 +74,21 @@ class AppsController < ApplicationController |
| 73 | 74 | # apparently finding by 'watchers.email' and 'id' is broken |
| 74 | 75 | raise(Mongoid::Errors::DocumentNotFound.new(App,@app.id)) unless current_user.admin? || current_user.watching?(@app) |
| 75 | 76 | end |
| 77 | + | |
| 78 | + # email_at_notices is edited as a string, and stored as an array. | |
| 79 | + def parse_email_at_notices_or_set_default | |
| 80 | + if params[:app] && val = params[:app][:email_at_notices] | |
| 81 | + # Sanitize negative values, split on comma, | |
| 82 | + # strip, parse as integer, remove all '0's. | |
| 83 | + # If empty, set as default and show an error message. | |
| 84 | + email_at_notices = val.gsub(/-\d+/,"").split(",").map{|v| v.strip.to_i }.reject{|v| v == 0} | |
| 85 | + if email_at_notices.any? | |
| 86 | + params[:app][:email_at_notices] = email_at_notices | |
| 87 | + else | |
| 88 | + default_array = params[:app][:email_at_notices] = Errbit::Config.default_email_at_notices | |
| 89 | + flash[:error] = "Couldn't parse your notification frequency. Value was reset to default (#{default_array.join(', ')})." | |
| 90 | + end | |
| 91 | + end | |
| 92 | + end | |
| 76 | 93 | end |
| 94 | + | ... | ... |
app/models/app.rb
| ... | ... | @@ -8,6 +8,7 @@ class App |
| 8 | 8 | field :resolve_errs_on_deploy, :type => Boolean, :default => false |
| 9 | 9 | field :notify_on_errs, :type => Boolean, :default => true |
| 10 | 10 | field :notify_on_deploys, :type => Boolean, :default => true |
| 11 | + field :email_at_notices, :type => Array, :default => Errbit::Config.default_email_at_notices | |
| 11 | 12 | |
| 12 | 13 | # Some legacy apps may have sting as key instead of BSON::ObjectID |
| 13 | 14 | identity :type => String | ... | ... |
app/models/notice.rb
| ... | ... | @@ -55,11 +55,11 @@ class Notice |
| 55 | 55 | agent_string = env_vars['HTTP_USER_AGENT'] |
| 56 | 56 | agent_string.blank? ? nil : UserAgent.parse(agent_string) |
| 57 | 57 | end |
| 58 | - | |
| 58 | + | |
| 59 | 59 | def self.in_app_backtrace_line? line |
| 60 | 60 | !!(line['file'] =~ %r{^\[PROJECT_ROOT\]/(?!(vendor))}) |
| 61 | 61 | end |
| 62 | - | |
| 62 | + | |
| 63 | 63 | def request |
| 64 | 64 | read_attribute(:request) || {} |
| 65 | 65 | end |
| ... | ... | @@ -83,11 +83,11 @@ class Notice |
| 83 | 83 | def cache_last_notice_at |
| 84 | 84 | err.update_attributes(:last_notice_at => created_at) |
| 85 | 85 | end |
| 86 | - | |
| 86 | + | |
| 87 | 87 | protected |
| 88 | 88 | |
| 89 | 89 | def should_notify? |
| 90 | - err.app.notify_on_errs? && Errbit::Config.email_at_notices.include?(err.notices.count) && err.app.watchers.any? | |
| 90 | + err.app.notify_on_errs? && err.app.email_at_notices.include?(err.notices.count) && err.app.watchers.any? | |
| 91 | 91 | end |
| 92 | 92 | |
| 93 | 93 | |
| ... | ... | @@ -122,3 +122,4 @@ class Notice |
| 122 | 122 | end |
| 123 | 123 | end |
| 124 | 124 | end |
| 125 | + | ... | ... |
app/views/apps/_fields.html.haml
| ... | ... | @@ -4,21 +4,34 @@ |
| 4 | 4 | = f.label :name |
| 5 | 5 | = f.text_field :name |
| 6 | 6 | |
| 7 | -%div.checkbox | |
| 8 | - = f.check_box :notify_on_errs | |
| 9 | - = f.label :notify_on_errs, 'Notify on errors' | |
| 10 | - | |
| 11 | 7 | %div |
| 12 | 8 | = f.label :github_url |
| 13 | 9 | = f.text_field :github_url |
| 14 | 10 | |
| 11 | +%fieldset | |
| 12 | + %legend Notifications | |
| 13 | + %div.checkbox | |
| 14 | + = f.check_box :notify_on_errs | |
| 15 | + = f.label :notify_on_errs, 'Notify on errors' | |
| 16 | + %div.email_at_notices_nested{:style => f.object.notify_on_errs ? '' : 'display: none;'} | |
| 17 | + .field-helpertext Send a notification every | |
| 18 | + -# Edit the email_at_notices array as a CSV string | |
| 19 | + = f.text_field :email_at_notices, :value => f.object.email_at_notices.join(", ") | |
| 20 | + .field-helpertext times an error occurs. (CSV) | |
| 21 | + %div.checkbox | |
| 22 | + = f.check_box :notify_on_deploys | |
| 23 | + = f.label :notify_on_deploys, 'Notify on deploys' | |
| 24 | + | |
| 25 | +:javascript | |
| 26 | + $('#app_notify_on_errs').change(function(){ | |
| 27 | + var el = $('.email_at_notices_nested'); | |
| 28 | + this.checked ? el.show() : el.hide(); | |
| 29 | + }); | |
| 30 | + | |
| 15 | 31 | %div.checkbox |
| 16 | 32 | = f.check_box :resolve_errs_on_deploy |
| 17 | 33 | = f.label :resolve_errs_on_deploy, 'Resolve errs on deploy' |
| 18 | 34 | |
| 19 | -%div.checkbox | |
| 20 | - = f.check_box :notify_on_deploys | |
| 21 | - = f.label :notify_on_deploys, 'Notify on deploys' | |
| 22 | 35 | |
| 23 | 36 | %fieldset.nested-wrapper |
| 24 | 37 | %legend Watchers | ... | ... |
config/config.example.yml
| ... | ... | @@ -14,11 +14,13 @@ host: errbit.example.com |
| 14 | 14 | # will be sent from. |
| 15 | 15 | email_from: errbit@example.com |
| 16 | 16 | |
| 17 | -# Configure when emails are sent for an error. | |
| 17 | +# Configure the default value for when emails are sent for an error. | |
| 18 | 18 | # [1,3,7] = 1st, 3rd, and 7th occurence triggers |
| 19 | 19 | # an email notification. |
| 20 | -email_at_notices: [1, 10, 100] | |
| 20 | +# Each app can be configured individually. | |
| 21 | +default_email_at_notices: [1, 10, 100] | |
| 21 | 22 | |
| 22 | -# Set to false to suppress confirmation when | |
| 23 | +# Set to false to suppress confirmation when | |
| 23 | 24 | # resolving errors. |
| 24 | 25 | confirm_resolve_err: true |
| 26 | + | ... | ... |
config/initializers/_load_config.rb
| ... | ... | @@ -4,7 +4,7 @@ if ENV['HEROKU'] |
| 4 | 4 | Errbit::Config = OpenStruct.new |
| 5 | 5 | Errbit::Config.host = ENV['ERRBIT_HOST'] |
| 6 | 6 | Errbit::Config.email_from = ENV['ERRBIT_EMAIL_FROM'] |
| 7 | - Errbit::Config.email_at_notices = [1,3,10] #ENV['ERRBIT_EMAIL_AT_NOTICES'] | |
| 7 | + Errbit::Config.default_email_at_notices = [1,3,10] #ENV['ERRBIT_EMAIL_AT_NOTICES'] | |
| 8 | 8 | Errbit::Application.config.action_mailer.smtp_settings = { |
| 9 | 9 | :address => "smtp.sendgrid.net", |
| 10 | 10 | :port => "25", |
| ... | ... | @@ -25,4 +25,5 @@ end |
| 25 | 25 | # Set config specific values |
| 26 | 26 | (Errbit::Application.config.action_mailer.default_url_options ||= {}).tap do |default| |
| 27 | 27 | default.merge! :host => Errbit::Config.host if default[:host].blank? |
| 28 | -end | |
| 29 | 28 | \ No newline at end of file |
| 29 | +end | |
| 30 | + | ... | ... |
public/stylesheets/application.css
| ... | ... | @@ -350,6 +350,17 @@ form .error-messages ul { |
| 350 | 350 | list-style-type: square; |
| 351 | 351 | } |
| 352 | 352 | |
| 353 | +form .field-helpertext { | |
| 354 | + display: inline; | |
| 355 | + text-transform: uppercase; | |
| 356 | +} | |
| 357 | + | |
| 358 | +form input#app_email_at_notices { | |
| 359 | + width: 130px; | |
| 360 | + margin: 0 5px; | |
| 361 | +} | |
| 362 | + | |
| 363 | + | |
| 353 | 364 | /* Tables */ |
| 354 | 365 | table { |
| 355 | 366 | width: 100%; |
| ... | ... | @@ -683,3 +694,4 @@ span.click_span { |
| 683 | 694 | #deploys_div, #repository_div, #watchers_div { |
| 684 | 695 | display: none; |
| 685 | 696 | } |
| 697 | + | ... | ... |
spec/controllers/apps_controller_spec.rb
| ... | ... | @@ -241,6 +241,27 @@ describe AppsController do |
| 241 | 241 | end |
| 242 | 242 | end |
| 243 | 243 | |
| 244 | + context "changing email_at_notices" do | |
| 245 | + it "should parse legal csv values" do | |
| 246 | + put :update, :id => @app.id, :app => { :email_at_notices => '1, 4, 7,8, 10' } | |
| 247 | + @app.reload | |
| 248 | + @app.email_at_notices.should == [1, 4, 7, 8, 10] | |
| 249 | + end | |
| 250 | + context "failed parsing of CSV" do | |
| 251 | + it "should set the default value" do | |
| 252 | + @app = Factory(:app, :email_at_notices => [1, 2, 3, 4]) | |
| 253 | + put :update, :id => @app.id, :app => { :email_at_notices => 'asdf, -1,0,foobar,gd00,0,abc' } | |
| 254 | + @app.reload | |
| 255 | + @app.email_at_notices.should == Errbit::Config.default_email_at_notices | |
| 256 | + end | |
| 257 | + | |
| 258 | + it "should display a message" do | |
| 259 | + put :update, :id => @app.id, :app => { :email_at_notices => 'qwertyuiop' } | |
| 260 | + request.flash[:error].should match(/Couldn't parse/) | |
| 261 | + end | |
| 262 | + end | |
| 263 | + end | |
| 264 | + | |
| 244 | 265 | context "setting up issue tracker", :cur => true do |
| 245 | 266 | context "unknown tracker type" do |
| 246 | 267 | before(:each) do |
| ... | ... | @@ -386,3 +407,4 @@ describe AppsController do |
| 386 | 407 | end |
| 387 | 408 | |
| 388 | 409 | end |
| 410 | + | ... | ... |
spec/factories/app_factories.rb
| 1 | 1 | Factory.define(:app) do |p| |
| 2 | 2 | p.name { Factory.next :app_name } |
| 3 | + p.email_at_notices { [1, 10, 100] } | |
| 3 | 4 | end |
| 4 | 5 | |
| 5 | 6 | Factory.define(:app_with_watcher, :parent => :app) do |p| |
| ... | ... | @@ -26,3 +27,4 @@ Factory.define(:deploy) do |d| |
| 26 | 27 | d.environment 'production' |
| 27 | 28 | d.revision ActiveSupport::SecureRandom.hex(10) |
| 28 | 29 | end |
| 30 | + | ... | ... |
spec/models/notice_spec.rb
| ... | ... | @@ -21,15 +21,15 @@ describe Notice do |
| 21 | 21 | notice.errors[:notifier].should include("can't be blank") |
| 22 | 22 | end |
| 23 | 23 | end |
| 24 | - | |
| 24 | + | |
| 25 | 25 | context '.in_app_backtrace_line?' do |
| 26 | 26 | let(:backtrace) do [{ |
| 27 | 27 | 'number' => rand(999), |
| 28 | - 'file' => '[GEM_ROOT]/gems/actionpack-3.0.4/lib/action_controller/metal/rescue.rb', | |
| 28 | + 'file' => '[GEM_ROOT]/gems/actionpack-3.0.4/lib/action_controller/metal/rescue.rb', | |
| 29 | 29 | 'method' => ActiveSupport.methods.shuffle.first |
| 30 | 30 | }, { |
| 31 | 31 | 'number' => rand(999), |
| 32 | - 'file' => '[PROJECT_ROOT]/vendor/plugins/seamless_database_pool/lib/seamless_database_pool/controller_filter.rb', | |
| 32 | + 'file' => '[PROJECT_ROOT]/vendor/plugins/seamless_database_pool/lib/seamless_database_pool/controller_filter.rb', | |
| 33 | 33 | 'method' => ActiveSupport.methods.shuffle.first |
| 34 | 34 | }, { |
| 35 | 35 | 'number' => rand(999), |
| ... | ... | @@ -50,7 +50,7 @@ describe Notice do |
| 50 | 50 | Notice.in_app_backtrace_line?(backtrace[2]).should == true |
| 51 | 51 | end |
| 52 | 52 | end |
| 53 | - | |
| 53 | + | |
| 54 | 54 | context '#from_xml' do |
| 55 | 55 | before do |
| 56 | 56 | @xml = Rails.root.join('spec','fixtures','hoptoad_test_notice.xml').read |
| ... | ... | @@ -159,20 +159,22 @@ describe Notice do |
| 159 | 159 | notice.user_agent.browser.should == 'Chrome' |
| 160 | 160 | notice.user_agent.version.to_s.should =~ /^10\.0/ |
| 161 | 161 | end |
| 162 | - | |
| 162 | + | |
| 163 | 163 | it "should be nil if HTTP_USER_AGENT is blank" do |
| 164 | 164 | notice = Factory.build(:notice) |
| 165 | 165 | notice.user_agent.should == nil |
| 166 | 166 | end |
| 167 | 167 | end |
| 168 | - | |
| 169 | - describe "email notifications" do | |
| 168 | + | |
| 169 | + describe "email notifications (configured individually for each app)" do | |
| 170 | + custom_thresholds = [2, 4, 8, 16, 32, 64] | |
| 171 | + | |
| 170 | 172 | before do |
| 171 | - @app = Factory(:app_with_watcher) | |
| 173 | + @app = Factory(:app_with_watcher, :email_at_notices => custom_thresholds) | |
| 172 | 174 | @err = Factory(:err, :app => @app) |
| 173 | 175 | end |
| 174 | 176 | |
| 175 | - Errbit::Config.email_at_notices.each do |threshold| | |
| 177 | + custom_thresholds.each do |threshold| | |
| 176 | 178 | it "sends an email notification after #{threshold} notice(s)" do |
| 177 | 179 | @err.notices.stub(:count).and_return(threshold) |
| 178 | 180 | Mailer.should_receive(:err_notification). |
| ... | ... | @@ -183,3 +185,4 @@ describe Notice do |
| 183 | 185 | end |
| 184 | 186 | |
| 185 | 187 | end |
| 188 | + | ... | ... |