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 | + | ... | ... |