whoops 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/app/controllers/event_groups_controller.rb +5 -4
- data/app/controllers/events_controller.rb +3 -5
- data/app/controllers/notification_subscriptions_controller.rb +1 -3
- data/app/controllers/whoops_controller.rb +17 -0
- data/app/helpers/event_groups_helper.rb +1 -1
- data/app/models/whoops/authorized_service_lookup.rb +45 -0
- data/app/models/whoops/event.rb +0 -12
- data/app/models/whoops/event_group.rb +0 -32
- data/app/models/whoops/filter.rb +21 -1
- data/app/models/whoops/new_event.rb +55 -0
- data/app/models/whoops/notification_subscription.rb +4 -1
- data/app/views/filters/_filters.html.haml +2 -2
- metadata +7 -5
- data/app/models/whoops/notification_rule.rb +0 -63
@@ -1,5 +1,4 @@
|
|
1
|
-
class EventGroupsController <
|
2
|
-
layout 'whoops'
|
1
|
+
class EventGroupsController < WhoopsController
|
3
2
|
before_filter :update_event_group_filter
|
4
3
|
helper_method :event_group_filter
|
5
4
|
|
@@ -25,11 +24,13 @@ class EventGroupsController < ApplicationController
|
|
25
24
|
end
|
26
25
|
|
27
26
|
def event_group_filter
|
28
|
-
session[:event_group_filter] ||=
|
27
|
+
session[:event_group_filter] ||= new_whoops_filter
|
29
28
|
end
|
30
29
|
|
31
30
|
def event_group_filter=(filter)
|
32
|
-
|
31
|
+
filter = Whoops::Filter.new_from_params(filter)
|
32
|
+
filter.authorized_service_lookup = authorized_service_lookup
|
33
|
+
session[:event_group_filter] = filter
|
33
34
|
end
|
34
35
|
|
35
36
|
def update
|
@@ -1,8 +1,6 @@
|
|
1
|
-
class EventsController <
|
2
|
-
layout 'whoops'
|
3
|
-
|
1
|
+
class EventsController < WhoopsController
|
4
2
|
def index
|
5
|
-
@event_group = Whoops::EventGroup.find(params[:whoops_event_group_id])
|
3
|
+
@event_group = Whoops::EventGroup.where(new_whoops_filter.to_query_document).find(params[:whoops_event_group_id])
|
6
4
|
|
7
5
|
events_base = @event_group.events
|
8
6
|
unless params[:query].blank?
|
@@ -22,7 +20,7 @@ class EventsController < ApplicationController
|
|
22
20
|
|
23
21
|
# TODO break this out into a more metal-y controller
|
24
22
|
def create
|
25
|
-
Whoops::
|
23
|
+
Whoops::NewEvent.new(params[:event]).record!
|
26
24
|
render :status => 200, :nothing => true
|
27
25
|
end
|
28
26
|
end
|
@@ -1,6 +1,4 @@
|
|
1
|
-
class NotificationSubscriptionsController <
|
2
|
-
layout 'whoops'
|
3
|
-
|
1
|
+
class NotificationSubscriptionsController < WhoopsController
|
4
2
|
def index
|
5
3
|
@notification_subscription = Whoops::NotificationSubscription.new
|
6
4
|
@notification_subscription.build_filter
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class WhoopsController < ApplicationController
|
2
|
+
layout 'whoops'
|
3
|
+
|
4
|
+
private
|
5
|
+
helper_method :authorized_service_lookup
|
6
|
+
|
7
|
+
# overwrite this to implement rules for hiding services
|
8
|
+
def authorized_service_lookup
|
9
|
+
@authorized_service_lookup ||= Whoops::AuthorizedServiceLookup.new(nil)
|
10
|
+
end
|
11
|
+
|
12
|
+
def new_whoops_filter
|
13
|
+
filter = Whoops::Filter.new
|
14
|
+
filter.authorized_service_lookup = authorized_service_lookup
|
15
|
+
filter
|
16
|
+
end
|
17
|
+
end
|
@@ -11,7 +11,7 @@ module EventGroupsHelper
|
|
11
11
|
# group services by root, eg "sv1.web" and "sv1.resque" are in the
|
12
12
|
# same sub array
|
13
13
|
previous_service_root = ""
|
14
|
-
Whoops::EventGroup.services.to_a.sort.each { |service|
|
14
|
+
authorized_service_lookup.filter_authorized(Whoops::EventGroup.services).to_a.sort.each { |service|
|
15
15
|
service_root = (/(.*?)\./ =~ service && $~[1]) || service
|
16
16
|
if service_root == previous_service_root
|
17
17
|
@filter_field_allowed_values["service"].last << service
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# This class is meant to be overwritten in host applications
|
2
|
+
#
|
3
|
+
# Authorized Service Lookups interact with filters to limit the
|
4
|
+
# services which a user is allowed to see. This allows host
|
5
|
+
# applications to more easily implement authorization.
|
6
|
+
#
|
7
|
+
# For example, if your Whoops installation is used by many different
|
8
|
+
# teams, but you don't want the teams to see each others' data, you
|
9
|
+
# could create a mapping between the team members' email addresses and
|
10
|
+
# the services they're allowed to see.
|
11
|
+
#
|
12
|
+
# Since filters are used when viewing events or sending notifications,
|
13
|
+
# the authorized service lookup allows you to modify the filters to
|
14
|
+
# prevent the unauthorized services from being seen
|
15
|
+
class Whoops::AuthorizedServiceLookup
|
16
|
+
|
17
|
+
# @param key the value used to look up authorized services
|
18
|
+
def initialize(key)
|
19
|
+
@key = key
|
20
|
+
end
|
21
|
+
|
22
|
+
# if there are services given, then show all services
|
23
|
+
# however, if we're looking at authorized services, then "all
|
24
|
+
# services" means "all authorized services"
|
25
|
+
#
|
26
|
+
# if there is a filter on service then only allow authorized
|
27
|
+
# services
|
28
|
+
#
|
29
|
+
# one thing to note is that if both services and authorized_services
|
30
|
+
# are blank, then no filter will be applied to service at all
|
31
|
+
def filter_authorized(services)
|
32
|
+
matches = services.select{ |s| service_authorized?(s) }
|
33
|
+
matches.empty? ? authorized_services : matches
|
34
|
+
end
|
35
|
+
|
36
|
+
# Overwrite this in your subclasses if you want to implement
|
37
|
+
# authorized services
|
38
|
+
def authorized_services
|
39
|
+
[]
|
40
|
+
end
|
41
|
+
|
42
|
+
def service_authorized?(service)
|
43
|
+
authorized_services.blank? || /^(#{authorized_services.join("|")})/ =~ service
|
44
|
+
end
|
45
|
+
end
|
data/app/models/whoops/event.rb
CHANGED
@@ -15,18 +15,6 @@ class Whoops::Event
|
|
15
15
|
|
16
16
|
before_save :set_keywords, :sanitize_details
|
17
17
|
|
18
|
-
def self.record(params)
|
19
|
-
params = params.with_indifferent_access
|
20
|
-
|
21
|
-
event_group_params = params.slice(*Whoops::EventGroup.field_names)
|
22
|
-
event_group_params[:last_recorded_at] = params[:event_time]
|
23
|
-
event_group_params
|
24
|
-
event_group = Whoops::EventGroup.handle_new_event(event_group_params)
|
25
|
-
|
26
|
-
event_params = params.slice(*Whoops::Event.field_names)
|
27
|
-
event_group.events.create(event_params)
|
28
|
-
end
|
29
|
-
|
30
18
|
def self.search(query)
|
31
19
|
conditions = Whoops::MongoidSearchParser.new(query).conditions
|
32
20
|
where(conditions)
|
@@ -17,28 +17,6 @@ class Whoops::EventGroup
|
|
17
17
|
field :last_recorded_at, :type => DateTime
|
18
18
|
field :archived, :type => Boolean, :default => false
|
19
19
|
field :event_count, :type => Integer, :default => 0
|
20
|
-
|
21
|
-
class << self
|
22
|
-
def handle_new_event(params)
|
23
|
-
identifying_params = params.slice(*Whoops::EventGroup.identifying_fields)
|
24
|
-
event_group = Whoops::EventGroup.first(:conditions => identifying_params)
|
25
|
-
|
26
|
-
if event_group
|
27
|
-
event_group.attributes = params
|
28
|
-
else
|
29
|
-
event_group = Whoops::EventGroup.new(params)
|
30
|
-
end
|
31
|
-
|
32
|
-
if event_group.valid?
|
33
|
-
event_group.send_notifications
|
34
|
-
event_group.archived = false
|
35
|
-
event_group.event_count += 1
|
36
|
-
event_group.save
|
37
|
-
end
|
38
|
-
|
39
|
-
event_group
|
40
|
-
end
|
41
|
-
end
|
42
20
|
|
43
21
|
has_many :events, :class_name => "Whoops::Event"
|
44
22
|
|
@@ -52,14 +30,4 @@ class Whoops::EventGroup
|
|
52
30
|
def self.services
|
53
31
|
all.distinct(:service).sort
|
54
32
|
end
|
55
|
-
|
56
|
-
def should_send_notifications?
|
57
|
-
(archived || new_record) && Rails.application.config.whoops_sender
|
58
|
-
end
|
59
|
-
|
60
|
-
def send_notifications
|
61
|
-
return unless should_send_notifications?
|
62
|
-
matcher = Whoops::NotificationSubscription::Matcher.new(self)
|
63
|
-
Whoops::NotificationMailer.event_notification(self, matcher.matching_emails).deliver unless matcher.matching_emails.empty?
|
64
|
-
end
|
65
33
|
end
|
data/app/models/whoops/filter.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
class Whoops::Filter
|
2
|
+
attr_accessor :authorized_service_lookup
|
3
|
+
|
2
4
|
include Mongoid::Document
|
3
5
|
include FieldNames
|
4
6
|
|
@@ -13,7 +15,7 @@ class Whoops::Filter
|
|
13
15
|
def to_query_document
|
14
16
|
doc = attributes.except(:_id, "_id", :_type, "_type").delete_if{|k, v| v.blank?}
|
15
17
|
# match all services under namespace. ie, if "app" given, match "app.web", "app.backend" etc
|
16
|
-
doc["service"] =
|
18
|
+
doc["service"] = service.collect{ |d| /^#{d}/ } unless service.blank?
|
17
19
|
doc.inject({}) do |hash, current|
|
18
20
|
hash[current.first.to_sym.in] = current.last unless current.last.empty?
|
19
21
|
hash
|
@@ -34,6 +36,24 @@ class Whoops::Filter
|
|
34
36
|
update_attributes(self.class.clean_params(params))
|
35
37
|
end
|
36
38
|
|
39
|
+
def service
|
40
|
+
if authorized_services_provided?
|
41
|
+
authorized_services
|
42
|
+
else
|
43
|
+
attributes["service"]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def authorized_services_provided?
|
50
|
+
authorized_service_lookup
|
51
|
+
end
|
52
|
+
|
53
|
+
def authorized_services
|
54
|
+
authorized_service_lookup.filter_authorized(attributes["service"].to_a)
|
55
|
+
end
|
56
|
+
|
37
57
|
class << self
|
38
58
|
def new_from_params(params)
|
39
59
|
if params
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Receives new event params and processes them
|
2
|
+
class Whoops::NewEvent
|
3
|
+
def initialize(params)
|
4
|
+
@params = params.with_indifferent_access
|
5
|
+
end
|
6
|
+
|
7
|
+
# both records and sends notifications
|
8
|
+
def record!
|
9
|
+
find_or_build_event_group
|
10
|
+
update_event_group_attributes
|
11
|
+
send_notifications_for_event_group
|
12
|
+
|
13
|
+
@event_group.archived = false
|
14
|
+
@event_group.save
|
15
|
+
@event_group.events.create(event_params)
|
16
|
+
@event_group
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def find_or_build_event_group
|
22
|
+
@event_group = Whoops::EventGroup.first(:conditions => event_group_identifying_fields) || Whoops::EventGroup.new(event_group_params)
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_event_group_attributes
|
26
|
+
if @event_group.valid?
|
27
|
+
@event_group.attributes = event_group_params
|
28
|
+
@event_group.last_recorded_at = Time.now
|
29
|
+
@event_group.event_count += 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def should_send_notifications?
|
34
|
+
@event_group.valid? && (@event_group.archived || @event_group.new_record) && Rails.application.config.whoops_sender
|
35
|
+
end
|
36
|
+
|
37
|
+
def send_notifications_for_event_group
|
38
|
+
return unless should_send_notifications?
|
39
|
+
matcher = Whoops::NotificationSubscription::Matcher.new(@event_group)
|
40
|
+
Whoops::NotificationMailer.event_notification(@event_group, matcher.matching_emails).deliver unless matcher.matching_emails.empty?
|
41
|
+
end
|
42
|
+
|
43
|
+
# TODO does it make sense to have a separate params object?
|
44
|
+
def event_group_params
|
45
|
+
@params.slice(*Whoops::EventGroup.field_names)
|
46
|
+
end
|
47
|
+
|
48
|
+
def event_group_identifying_fields
|
49
|
+
@params.slice(*Whoops::EventGroup.identifying_fields)
|
50
|
+
end
|
51
|
+
|
52
|
+
def event_params
|
53
|
+
@params.slice(*Whoops::Event.field_names)
|
54
|
+
end
|
55
|
+
end
|
@@ -21,7 +21,10 @@ class Whoops::NotificationSubscription
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def matching_emails
|
24
|
-
Whoops::NotificationSubscription.all.select
|
24
|
+
Whoops::NotificationSubscription.all.select do |ns|
|
25
|
+
lookup = Whoops::AuthorizedServiceLookup.new(ns.email)
|
26
|
+
ns.filter.matches_event_group?(self.event_group)
|
27
|
+
end.collect(&:email)
|
25
28
|
end
|
26
29
|
end
|
27
30
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
- filter_field_allowed_values.each do |field_name, allowed_value_groups|
|
1
|
+
- %w{service environment event_type}.map{|k| [k, filter_field_allowed_values[k]]}.each do |field_name, allowed_value_groups|
|
2
2
|
%h4= field_name
|
3
3
|
%ul.filter-selects.inputs-list
|
4
4
|
- allowed_value_groups.each do |allowed_values|
|
@@ -9,4 +9,4 @@
|
|
9
9
|
= allowed_value
|
10
10
|
- unless allowed_values == allowed_value_groups.last
|
11
11
|
%li.divider
|
12
|
-
%hr
|
12
|
+
%hr
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: whoops
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 5
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.5.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Daniel Higginbotham
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-12-03 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: rails
|
@@ -187,17 +187,19 @@ files:
|
|
187
187
|
- app/controllers/event_groups_controller.rb
|
188
188
|
- app/controllers/events_controller.rb
|
189
189
|
- app/controllers/notification_subscriptions_controller.rb
|
190
|
+
- app/controllers/whoops_controller.rb
|
190
191
|
- app/helpers/event_groups_helper.rb
|
191
192
|
- app/helpers/events_helper.rb
|
192
193
|
- app/helpers/filters_helper.rb
|
193
194
|
- app/helpers/notifications_helper.rb
|
194
195
|
- app/lib/field_names.rb
|
195
196
|
- app/mailers/whoops/notification_mailer.rb
|
197
|
+
- app/models/whoops/authorized_service_lookup.rb
|
196
198
|
- app/models/whoops/event.rb
|
197
199
|
- app/models/whoops/event_group.rb
|
198
200
|
- app/models/whoops/filter.rb
|
199
201
|
- app/models/whoops/mongoid_search_parser.rb
|
200
|
-
- app/models/whoops/
|
202
|
+
- app/models/whoops/new_event.rb
|
201
203
|
- app/models/whoops/notification_subscription.rb
|
202
204
|
- app/views/event_groups/_list.html.haml
|
203
205
|
- app/views/event_groups/index.html.haml
|
@@ -1,63 +0,0 @@
|
|
1
|
-
class Whoops::NotificationRule
|
2
|
-
include Mongoid::Document
|
3
|
-
|
4
|
-
field :email, :type => String
|
5
|
-
field :matchers, :type => Array
|
6
|
-
|
7
|
-
validates_presence_of :email
|
8
|
-
|
9
|
-
# This might come in handy in the future?
|
10
|
-
# class << self.fields["matchers"]
|
11
|
-
# def set(object)
|
12
|
-
# object = object.split("\n").collect{ |m| m.strip } if object.is_a?(String)
|
13
|
-
# object
|
14
|
-
# end
|
15
|
-
# end
|
16
|
-
|
17
|
-
before_save :downcase_email
|
18
|
-
|
19
|
-
def downcase_email
|
20
|
-
self.email.downcase!
|
21
|
-
end
|
22
|
-
|
23
|
-
def matchers=(matchers)
|
24
|
-
write_attribute(:matchers, split_matchers(matchers).sort)
|
25
|
-
end
|
26
|
-
|
27
|
-
def add_matchers(new_matchers)
|
28
|
-
split = split_matchers(new_matchers)
|
29
|
-
write_attribute(:matchers, (self.matchers | split).sort)
|
30
|
-
self.save
|
31
|
-
end
|
32
|
-
|
33
|
-
def split_matchers(new_matchers)
|
34
|
-
new_matchers.split("\n").collect{ |m| m.strip }
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.add_rules(params)
|
38
|
-
params[:email] = params[:email].downcase
|
39
|
-
if rule = first(:conditions => {:email => params[:email]})
|
40
|
-
rule.add_matchers(params[:matchers])
|
41
|
-
else
|
42
|
-
create(params)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
class Matcher
|
47
|
-
attr_accessor :event_group
|
48
|
-
|
49
|
-
# @param [ Whoops::EventGroup ]
|
50
|
-
def initialize(event_group)
|
51
|
-
self.event_group = event_group
|
52
|
-
end
|
53
|
-
|
54
|
-
def matching_emails
|
55
|
-
matches.collect{|m| m.email}.uniq
|
56
|
-
end
|
57
|
-
|
58
|
-
def matches
|
59
|
-
service_matches = event_group.service.split(".").inject([]){|collection, part| collection << [collection.last, part].compact.join(".")}.join("|")
|
60
|
-
@matches ||= Whoops::NotificationRule.where(:matchers => /^((#{service_matches})\S*$|(#{service_matches}).*#{event_group.environment})/)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|