whoops 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|