flapjack 0.8.10 → 0.8.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/Gemfile +1 -1
  4. data/bin/flapjack +10 -1
  5. data/bin/flapjack-nagios-receiver +1 -2
  6. data/bin/simulate-failed-check +12 -4
  7. data/etc/flapjack_config.yaml.example +2 -1
  8. data/flapjack.gemspec +1 -0
  9. data/lib/flapjack/data/contact.rb +46 -26
  10. data/lib/flapjack/data/entity.rb +28 -0
  11. data/lib/flapjack/data/entity_check.rb +52 -11
  12. data/lib/flapjack/data/event.rb +9 -3
  13. data/lib/flapjack/data/notification_rule.rb +8 -0
  14. data/lib/flapjack/gateways/api.rb +0 -1
  15. data/lib/flapjack/gateways/api/entity_check_presenter.rb +2 -1
  16. data/lib/flapjack/gateways/email.rb +1 -2
  17. data/lib/flapjack/gateways/jabber.rb +3 -3
  18. data/lib/flapjack/gateways/jsonapi.rb +186 -38
  19. data/lib/flapjack/gateways/jsonapi/check_methods.rb +120 -0
  20. data/lib/flapjack/gateways/jsonapi/{entity_check_presenter.rb → check_presenter.rb} +7 -6
  21. data/lib/flapjack/gateways/jsonapi/contact_methods.rb +61 -352
  22. data/lib/flapjack/gateways/jsonapi/entity_methods.rb +117 -248
  23. data/lib/flapjack/gateways/jsonapi/medium_methods.rb +179 -0
  24. data/lib/flapjack/gateways/jsonapi/notification_rule_methods.rb +124 -0
  25. data/lib/flapjack/gateways/jsonapi/pagerduty_credential_methods.rb +128 -0
  26. data/lib/flapjack/gateways/jsonapi/rack/json_params_parser.rb +4 -5
  27. data/lib/flapjack/gateways/jsonapi/report_methods.rb +143 -0
  28. data/lib/flapjack/gateways/web.rb +1 -0
  29. data/lib/flapjack/gateways/web/public/js/backbone.jsonapi.js +165 -101
  30. data/lib/flapjack/gateways/web/public/js/contacts.js +34 -46
  31. data/lib/flapjack/gateways/web/public/js/select2.js +232 -90
  32. data/lib/flapjack/gateways/web/public/js/select2.min.js +4 -4
  33. data/lib/flapjack/gateways/web/views/check.html.erb +11 -2
  34. data/lib/flapjack/processor.rb +6 -6
  35. data/lib/flapjack/version.rb +1 -1
  36. data/spec/lib/flapjack/data/entity_check_spec.rb +1 -1
  37. data/spec/lib/flapjack/data/event_spec.rb +10 -9
  38. data/spec/lib/flapjack/gateways/api/entity_methods_spec.rb +25 -25
  39. data/spec/lib/flapjack/gateways/api_spec.rb +23 -1
  40. data/spec/lib/flapjack/gateways/email_spec.rb +40 -2
  41. data/spec/lib/flapjack/gateways/jabber_spec.rb +1 -1
  42. data/spec/lib/flapjack/gateways/jsonapi/check_methods_spec.rb +134 -0
  43. data/spec/lib/flapjack/gateways/jsonapi/{entity_check_presenter_spec.rb → check_presenter_spec.rb} +17 -17
  44. data/spec/lib/flapjack/gateways/jsonapi/contact_methods_spec.rb +27 -232
  45. data/spec/lib/flapjack/gateways/jsonapi/entity_methods_spec.rb +217 -687
  46. data/spec/lib/flapjack/gateways/jsonapi/medium_methods_spec.rb +232 -0
  47. data/spec/lib/flapjack/gateways/jsonapi/notification_rule_methods_spec.rb +131 -0
  48. data/spec/lib/flapjack/gateways/jsonapi/pagerduty_credential_methods_spec.rb +113 -0
  49. data/spec/lib/flapjack/gateways/jsonapi/report_methods_spec.rb +546 -0
  50. data/spec/lib/flapjack/gateways/jsonapi_spec.rb +10 -1
  51. data/spec/lib/flapjack/gateways/web_spec.rb +1 -0
  52. data/spec/support/jsonapi_helper.rb +62 -0
  53. metadata +36 -8
  54. data/lib/flapjack/gateways/jsonapi/entity_presenter.rb +0 -75
  55. data/spec/lib/flapjack/gateways/jsonapi/entity_presenter_spec.rb +0 -108
@@ -12,7 +12,7 @@ module Flapjack
12
12
 
13
13
  class JSONAPI < Sinatra::Base
14
14
 
15
- class EntityCheckPresenter
15
+ class CheckPresenter
16
16
 
17
17
  def initialize(entity_check)
18
18
  @entity_check = entity_check
@@ -24,6 +24,7 @@ module Flapjack
24
24
  'enabled' => @entity_check.enabled?,
25
25
  'summary' => @entity_check.summary,
26
26
  'details' => @entity_check.details,
27
+ 'perfdata' => @entity_check.perfdata,
27
28
  'in_unscheduled_maintenance' => @entity_check.in_unscheduled_maintenance?,
28
29
  'in_scheduled_maintenance' => @entity_check.in_scheduled_maintenance?,
29
30
  'last_update' => @entity_check.last_update,
@@ -32,7 +33,7 @@ module Flapjack
32
33
  'last_acknowledgement_notification' => @entity_check.last_notification_for_state(:acknowledgement)[:timestamp]}
33
34
  end
34
35
 
35
- def outages(start_time, end_time, options = {})
36
+ def outage(start_time, end_time, options = {})
36
37
  # hist_states is an array of hashes, with [state, timestamp, summary] keys
37
38
  hist_states = @entity_check.historical_states(start_time, end_time)
38
39
  return hist_states if hist_states.empty?
@@ -83,7 +84,7 @@ module Flapjack
83
84
  result
84
85
  end
85
86
 
86
- def unscheduled_maintenances(start_time, end_time)
87
+ def unscheduled_maintenance(start_time, end_time)
87
88
  # unsched_maintenance is an array of hashes, with [duration, timestamp, summary] keys
88
89
  unsched_maintenance = @entity_check.maintenances(start_time, end_time,
89
90
  :scheduled => false)
@@ -98,7 +99,7 @@ module Flapjack
98
99
  start_in_unsched + unsched_maintenance
99
100
  end
100
101
 
101
- def scheduled_maintenances(start_time, end_time)
102
+ def scheduled_maintenance(start_time, end_time)
102
103
  # sched_maintenance is an array of hashes, with [duration, timestamp, summary] keys
103
104
  sched_maintenance = @entity_check.maintenances(start_time, end_time,
104
105
  :scheduled => true)
@@ -119,9 +120,9 @@ module Flapjack
119
120
  #
120
121
  # TODO test performance with larger data sets
121
122
  def downtime(start_time, end_time)
122
- sched_maintenances = scheduled_maintenances(start_time, end_time)
123
+ sched_maintenances = scheduled_maintenance(start_time, end_time)
123
124
 
124
- outs = outages(start_time, end_time)
125
+ outs = outage(start_time, end_time)
125
126
 
126
127
  total_secs = {}
127
128
  percentages = {}
@@ -18,28 +18,11 @@ module Flapjack
18
18
 
19
19
  module Helpers
20
20
 
21
- def find_contact(contact_id)
22
- contact = Flapjack::Data::Contact.find_by_id(contact_id, :logger => logger, :redis => redis)
23
- raise Flapjack::Gateways::JSONAPI::ContactNotFound.new(contact_id) if contact.nil?
24
- contact
25
- end
26
-
27
- def find_rule(rule_id)
28
- rule = Flapjack::Data::NotificationRule.find_by_id(rule_id, :logger => logger, :redis => redis)
29
- raise Flapjack::Gateways::JSONAPI::NotificationRuleNotFound.new(rule_id) if rule.nil?
30
- rule
31
- end
32
-
33
- def find_tags(tags)
34
- halt err(400, "no tags given") if tags.nil? || tags.empty?
35
- tags
36
- end
37
-
38
21
  def obtain_semaphore(resource)
39
22
  semaphore = nil
40
23
  strikes = 0
41
24
  begin
42
- semaphore = Flapjack::Data::Semaphore.new(resource, {:redis => redis, :expiry => 30})
25
+ semaphore = Flapjack::Data::Semaphore.new(resource, :redis => redis, :expiry => 30)
43
26
  rescue Flapjack::Data::Semaphore::ResourceLocked
44
27
  strikes += 1
45
28
  raise Flapjack::Gateways::JSONAPI::ResourceLocked.new(resource) unless strikes < 3
@@ -50,50 +33,32 @@ module Flapjack
50
33
  semaphore
51
34
  end
52
35
 
53
- def apply_json_patch(object_path, &block)
54
- ops = params[:ops]
36
+ def bulk_contact_operation(contact_ids, &block)
37
+ semaphore = obtain_semaphore(SEMAPHORE_CONTACT_MASS_UPDATE)
55
38
 
56
- if ops.nil? || !ops.is_a?(Array)
57
- halt err(400, "Invalid JSON-Patch request")
39
+ contacts_by_id = contact_ids.inject({}) do |memo, contact_id|
40
+ # can't use find_contact here as that would halt immediately
41
+ memo[contact_id] = Flapjack::Data::Contact.find_by_id(contact_id, :redis => redis, :logger => logger)
42
+ memo
58
43
  end
59
44
 
60
- ops.each do |operation|
61
- linked = nil
62
- property = nil
63
-
64
- op = operation['op']
65
- operation['path'] =~ /\A\/#{object_path}\/0\/([^\/]+)(?:\/([^\/]+)(?:\/([^\/]+))?)?\z/
66
- if 'links'.eql?($1)
67
- linked = $2
68
-
69
- value = case op
70
- when 'add'
71
- operation['value']
72
- when 'remove'
73
- $3
74
- end
75
- elsif 'replace'.eql?(op)
76
- property = $1
77
- value = $3
78
- else
79
- next
80
- end
81
-
82
- yield(op, property, linked, value)
45
+ missing_ids = contacts_by_id.select {|k, v| v.nil? }.keys
46
+ unless missing_ids.empty?
47
+ semaphore.release
48
+ halt(404, "Contacts with ids #{missing_ids.join(', ')} were not found")
83
49
  end
50
+
51
+ block.call(contacts_by_id.select {|k, v| !v.nil? }.values)
52
+ semaphore.release
84
53
  end
85
54
 
86
55
  end
87
56
 
88
57
  def self.registered(app)
89
-
58
+ app.helpers Flapjack::Gateways::JSONAPI::Helpers
90
59
  app.helpers Flapjack::Gateways::JSONAPI::ContactMethods::Helpers
91
60
 
92
61
  app.post '/contacts' do
93
- pass unless is_json_request?
94
- content_type :json
95
- cors_headers
96
-
97
62
  contacts_data = params[:contacts]
98
63
 
99
64
  if contacts_data.nil? || !contacts_data.is_a?(Enumerable)
@@ -126,23 +91,20 @@ module Flapjack
126
91
 
127
92
  ids = contacts_data.map {|c| c['id']}
128
93
  location(ids)
94
+ status 201
129
95
 
130
96
  contacts_data.map {|cd| cd['id']}.to_json
131
97
  end
132
98
 
133
- # Returns all (/contacts) or some (/contacts/1,2,3) or one (/contact/2) contact(s)
99
+ # Returns all (/contacts) or some (/contacts/1,2,3) or one (/contacts/2) contact(s)
134
100
  # https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts
135
- app.get %r{/contacts(?:/)?([^/]+)?$} do
136
- content_type 'application/vnd.api+json'
137
- cors_headers
138
-
101
+ app.get %r{^/contacts(?:/)?([^/]+)?$} do
139
102
  requested_contacts = if params[:captures] && params[:captures][0]
140
103
  params[:captures][0].split(',').uniq
141
104
  else
142
105
  nil
143
106
  end
144
107
 
145
- #FIXME: do we need to url decode the ids? has rack or some middleware already done this?
146
108
  contacts = if requested_contacts
147
109
  Flapjack::Data::Contact.find_by_ids(requested_contacts, :logger => logger, :redis => redis)
148
110
  else
@@ -154,315 +116,62 @@ module Flapjack
154
116
  raise Flapjack::Gateways::JSONAPI::ContactsNotFound.new(requested_contacts)
155
117
  end
156
118
 
157
- linked_entity_data, linked_entity_ids = if contacts.empty?
158
- [[], []]
159
- else
160
- Flapjack::Data::Contact.entities_jsonapi(contacts.map(&:id), :redis => redis)
161
- end
162
-
163
- linked_media_data = []
164
- linked_media_ids = {}
165
- contacts.each do |contact|
166
- contact.media.keys.each do |medium|
167
- id = "#{contact.id}_#{medium}"
168
- interval = contact.media_intervals[medium].nil? ? nil : contact.media_intervals[medium].to_i
169
- rollup_threshold = contact.media_rollup_thresholds[medium].nil? ? nil : contact.media_rollup_thresholds[medium].to_i
170
- linked_media_ids[contact.id] = id
171
- linked_media_data <<
172
- { "id" => id,
173
- "type" => medium,
174
- "address" => contact.media[medium],
175
- "interval" => interval,
176
- "rollup_threshold" => rollup_threshold,
177
- "contact_id" => contact.id }
178
- end
179
- end
119
+ entity_ids = Flapjack::Data::Contact.entity_ids_for(contacts.map(&:id), :redis => redis)
180
120
 
181
121
  contacts_json = contacts.collect {|contact|
182
- contact.linked_entity_ids = linked_entity_ids[contact.id]
183
- contact.linked_media_ids = linked_media_ids[contact.id]
184
- contact.to_jsonapi
122
+ contact.to_jsonapi(:entity_ids => entity_ids[contact.id])
185
123
  }.join(", ")
186
124
 
187
- '{"contacts":[' + contacts_json + ']' +
188
- ',"linked":{"entities":' + linked_entity_data.to_json +
189
- ',"media":' + linked_media_data.to_json + '}}'
190
- end
191
-
192
- # Returns the core information about the specified contact
193
- # https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id
194
- app.get '/contacts/:contact_id' do
195
- content_type 'application/vnd.api+json'
196
- cors_headers
197
- contact = find_contact(params[:contact_id])
198
-
199
- entities = contact.entities.map {|e| e[:entity] }
200
-
201
- '{"contacts":[' + contact.to_jsonapi + ']' +
202
- ( entities.empty? ? '}' :
203
- ', "linked": {"entities":' + entities.values.to_json + '}}')
204
- end
205
-
206
- # Updates a contact
207
- app.put '/contacts/:contact_id' do
208
- cors_headers
209
- content_type :json
210
-
211
- contacts_data = params[:contacts]
212
-
213
- if contacts_data.nil? || !contacts_data.is_a?(Enumerable)
214
- halt err(422, "No valid contacts were submitted")
215
- end
216
-
217
- unless contacts_data.length == 1
218
- halt err(422, "Exactly one contact hash must be supplied.")
219
- end
220
-
221
- contact_data = contacts_data.first
222
-
223
- if contact_data['id'] && contact_data['id'].to_s != params[:contact_id]
224
- halt err(422, "ID, if supplied, must match URL")
225
- end
226
-
227
- contact = find_contact(params[:contact_id])
228
- #contact_data = hashify('first_name', 'last_name', 'email', 'media', 'tags') {|k| [k, params[k]]}
229
- logger.debug("contact_data: #{contact_data}")
230
- contact.update(contact_data)
231
-
232
- contact.to_jsonapi
233
- end
234
-
235
- # TODO this should build up all data, verify entities exist, etc.
236
- # before applying any changes
237
- # TODO generalise JSON-Patch data parsing code
238
- app.patch '/contacts/:contact_id' do
239
- pass unless is_jsonpatch_request?
240
- content_type :json
241
- cors_headers
242
-
243
- contact = find_contact(params[:contact_id])
244
-
245
- apply_json_patch('contacts') do |op, property, linked, value|
246
- case op
247
- when 'replace'
248
- if ['first_name', 'last_name', 'email'].include?(property)
249
- contact.update(property => value)
250
- end
251
- when 'add'
252
- logger.debug "patch add operation. linked: #{linked}"
253
- if 'entities'.eql?(linked)
254
- entity = Flapjack::Data::Entity.find_by_id(value, :redis => redis)
255
- logger.debug "adding this entity: #{entity}"
256
- contact.add_entity(entity) unless entity.nil?
257
- end
258
- when 'remove'
259
- if 'entities'.eql?(linked)
260
- entity = Flapjack::Data::Entity.find_by_id(value, :redis => redis)
261
- contact.remove_entity(entity) unless entity.nil?
125
+ '{"contacts":[' + contacts_json + ']}'
126
+ end
127
+
128
+ app.patch '/contacts/:id' do
129
+ bulk_contact_operation(params[:id].split(',')) do |contacts|
130
+ contacts.each do |contact|
131
+ apply_json_patch('contacts') do |op, property, linked, value|
132
+ case op
133
+ when 'replace'
134
+ if ['first_name', 'last_name', 'email'].include?(property)
135
+ contact.update(property => value)
136
+ end
137
+ when 'add'
138
+ case linked
139
+ when 'entities'
140
+ entity = Flapjack::Data::Entity.find_by_id(value, :redis => redis)
141
+ contact.add_entity(entity) unless entity.nil?
142
+ when 'notification_rules'
143
+ notification_rule = Flapjack::Data::NotificationRule.find_by_id(value, :redis => redis)
144
+ unless notification_rule.nil?
145
+ contact.grab_notification_rule(notification_rule)
146
+ end
147
+ # when 'media' # not supported yet due to id brokenness
148
+ end
149
+ when 'remove'
150
+ case linked
151
+ when 'entities'
152
+ entity = Flapjack::Data::Entity.find_by_id(value, :redis => redis)
153
+ contact.remove_entity(entity) unless entity.nil?
154
+ when 'notification_rules'
155
+ notification_rule = Flapjack::Data::NotificationRule.find_by_id(value, :redis => redis)
156
+ unless notification_rule.nil?
157
+ contact.delete_notification_rule(notification_rule)
158
+ end
159
+ # when 'media' # not supported yet due to id brokenness
160
+ end
161
+ end
262
162
  end
263
163
  end
264
164
  end
265
165
 
266
- # will need to be 200 and return contact.to_jsonapi
267
- # if updated_at changes, or Etag, when those are introduced
268
- status 204
269
- end
270
-
271
- # Deletes a contact
272
- app.delete '/contacts/:contact_id' do
273
- cors_headers
274
- semaphore = obtain_semaphore(SEMAPHORE_CONTACT_MASS_UPDATE)
275
- contact = find_contact(params[:contact_id])
276
- contact.delete!
277
- semaphore.release
278
166
  status 204
279
167
  end
280
168
 
281
- app.post '/media' do
282
- pass unless is_json_request?
283
- content_type :json
284
- cors_headers
285
-
286
- media_data = params[:media]
287
-
288
- if media_data.nil? || !media_data.is_a?(Enumerable)
289
- halt err(422, "No valid media were submitted")
290
- end
291
-
292
- unless media_data.all? {|m| m['id'].nil? }
293
- halt err(422, "Media creation cannot include IDs")
169
+ # Delete one or more contacts
170
+ app.delete '/contacts/:id' do
171
+ bulk_contact_operation(params[:id].split(',')) do |contacts|
172
+ contacts.each {|contact| contact.delete!}
294
173
  end
295
174
 
296
- semaphore = obtain_semaphore(SEMAPHORE_CONTACT_MASS_UPDATE)
297
-
298
- contacts = media_data.inject({}) {|memo, medium_data|
299
- contact_id = medium_data['contact_id']
300
- if contact_id.nil?
301
- semaphore.release
302
- halt err(422, "Media data must include 'contact_id'")
303
- end
304
- next memo if memo.has_key?(contact_id)
305
- contact = Flapjack::Data::Contact.find_by_id(contact_id, :redis => redis)
306
- if contact.nil?
307
- semaphore.release
308
- halt err(422, "Contact id:'#{contact_id}' could not be loaded")
309
- end
310
- memo[contact_id] = contact
311
- memo
312
- }
313
-
314
- media_data.each do |medium_data|
315
- contact = contacts[medium_data['contact_id']]
316
- type = medium_data['type']
317
- contact.set_address_for_media(type, medium_data['address'])
318
- contact.set_interval_for_media(type, medium_data['interval'])
319
- contact.set_rollup_threshold_for_media(type, medium_data['rollup_threshold'])
320
- medium_data['id'] = "#{contact.id}_#{type}"
321
- end
322
-
323
- semaphore.release
324
-
325
- '{"media":' + media_data.to_json + '}'
326
- end
327
-
328
- app.patch '/media/:media_id' do
329
- pass unless is_jsonpatch_request?
330
- content_type :json
331
- cors_headers
332
-
333
- media_id = params[:media_id]
334
- media_id =~ /\A(.+)_(email|sms|jabber)\z/
335
-
336
- contact_id = $1
337
- type = $2
338
-
339
- halt err(422, "Could not get contact_id from media_id") if contact_id.nil?
340
- halt err(422, "Could not get type from media_id") if type.nil?
341
-
342
- contact = find_contact(contact_id)
343
-
344
- apply_json_patch('media') do |op, property, linked, value|
345
- if 'replace'.eql?(op)
346
- case property
347
- when 'address'
348
- contact.set_address_for_media(type, value)
349
- when 'interval'
350
- contact.set_interval_for_media(type, value)
351
- when 'rollup_threshold'
352
- contact.set_rollup_threshold_for_media(type, value)
353
- end
354
- end
355
- end
356
-
357
- status 204
358
- end
359
-
360
- app.get '/notification_rules/:id' do
361
- content_type :json
362
- cors_headers
363
-
364
- '{"notification_rules":[' +
365
- find_rule(params[:id]).to_json +
366
- ']}'
367
- end
368
-
369
- # Creates a notification rule or rules for a contact
370
- app.post '/notification_rules' do
371
- content_type :json
372
- cors_headers
373
-
374
- rules_data = params[:notification_rules]
375
-
376
- if rules_data.nil? || !rules_data.is_a?(Enumerable)
377
- halt err(422, "No valid notification rules were submitted")
378
- end
379
-
380
- errors = []
381
- rules_data.each do |rule_data|
382
- errors << Flapjack::Data::NotificationRule.prevalidate_data(symbolize(rule_data), {:logger => logger})
383
- end
384
- errors.compact!
385
-
386
- unless errors.nil? || errors.empty?
387
- halt err(422, *errors)
388
- end
389
-
390
- rules = []
391
- errors = []
392
- rules_data.each do |rule_data|
393
- rule_data = symbolize(rule_data)
394
- contact = find_contact(rule_data.delete(:contact_id))
395
- rule_or_errors = contact.add_notification_rule(rule_data, :logger => logger)
396
- if rule_or_errors.respond_to?(:critical_media)
397
- rules << rule_or_errors
398
- else
399
- errors << rule_or_errors
400
- end
401
- end
402
-
403
- if rules.empty?
404
- halt err(422, *errors)
405
- else
406
- if errors.empty?
407
- status 201
408
- else
409
- logger.warn("Errors during bulk notification rules creation: " + errors.join(', '))
410
- status 200
411
- end
412
- end
413
- ids = rules.map {|r| r.id}
414
- location(ids)
415
- '{"notification_rules":[' +
416
- rules.map {|r| r.to_json}.join(',') +
417
- ']}'
418
- end
419
-
420
- # Updates a notification rule
421
- app.put('/notification_rules/:id') do
422
- content_type :json
423
- cors_headers
424
-
425
- rules_data = params[:notification_rules]
426
-
427
- if rules_data.nil? || !rules_data.is_a?(Enumerable)
428
- halt err(422, "No valid notification rules were submitted")
429
- end
430
-
431
- unless rules_data.length == 1
432
- halt err(422, "Exactly one notification rules hash must be supplied.")
433
- end
434
-
435
- rule_data = rules_data.first
436
-
437
- if rule_data['id'] && rule_data['id'].to_s != params[:id]
438
- halt err(422, "ID, if supplied, must match URL")
439
- end
440
-
441
- rule = find_rule(params[:id])
442
- contact = find_contact(rule.contact_id)
443
-
444
- supplied_contact = rule_data.delete('contact_id')
445
- if supplied_contact && supplied_contact != contact.id
446
- halt err(422, "contact_id cannot be modified")
447
- end
448
-
449
- errors = rule.update(symbolize(rule_data), :logger => logger)
450
-
451
- unless errors.nil? || errors.empty?
452
- halt err(422, *errors)
453
- end
454
- '{"notification_rules":[' +
455
- rule.to_json +
456
- ']}'
457
- end
458
-
459
- # Deletes a notification rule
460
- app.delete('/notification_rules/:id') do
461
- cors_headers
462
- rule = find_rule(params[:id])
463
- logger.debug("rule to delete: #{rule.inspect}, contact_id: #{rule.contact_id}")
464
- contact = find_contact(rule.contact_id)
465
- contact.delete_notification_rule(rule)
466
175
  status 204
467
176
  end
468
177