flapjack 0.7.16 → 0.7.17

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -23,4 +23,7 @@ tmp/pids/*
23
23
  tmp/spec*
24
24
  tmp/profiles
25
25
  flapjack-*.gem
26
+ .rbenv-version
27
+ .ruby-version
28
+ .rvmrc
26
29
 
data/.travis.yml CHANGED
@@ -1,6 +1,7 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - "1.9.3"
4
+ - "2.0.0"
4
5
  gemfile:
5
6
  - Gemfile
6
7
  services:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  ## Flapjack Changelog
2
2
 
3
+ # 0.7.17 - 2013-07-04
4
+ - Feature: split API methods into two separate files, also specs gh-215 (@ali-graham)
5
+ - Feature: unlock ruby version, build with Ruby 2 in travis gh-237 (@ali-graham)
6
+ - Feature: include notification rule validation error details in api add, update functions gh-184 (@ali-graham)
7
+ - Feature: API: delete scheduled maintenance should return an error if no start_time parameter is passed gh-240 (@ali-graham)
8
+ - Bug: entity name in bulk status api response is incorrect gh-233 (@jessereynolds)
9
+ - Bug: non-changing checks creating state-change records gh-235 (@ali-graham)
10
+ - Bug: posting scheduled maintenance in new api format throwing 500 gh-239 (@jessereynolds)
11
+
3
12
  # 0.7.16 - 2013-06-27
4
13
  - Bug: errors accessing API gh-231 (@ali-graham)
5
14
 
@@ -221,16 +221,21 @@ module Flapjack
221
221
  # FIXME: Iterate through a list of tags associated with an entity:check pair, and update counters
222
222
  @redis.zrem("failed_checks:client:#{client}", @key) if client
223
223
  end
224
- end
225
224
 
226
- # Retain event data for entity:check pair
227
- @redis.rpush("#{@key}:states", timestamp)
228
- @redis.set("#{@key}:#{timestamp}:state", new_state)
229
- @redis.set("#{@key}:#{timestamp}:summary", summary) if summary
230
- @redis.set("#{@key}:#{timestamp}:details", details) if details
231
- @redis.set("#{@key}:#{timestamp}:count", count) if count
225
+ # Retain event data for entity:check pair
226
+ @redis.rpush("#{@key}:states", timestamp)
227
+ @redis.set("#{@key}:#{timestamp}:state", new_state)
228
+ @redis.set("#{@key}:#{timestamp}:summary", summary) if summary
229
+ @redis.set("#{@key}:#{timestamp}:details", details) if details
230
+ @redis.set("#{@key}:#{timestamp}:count", count) if count
231
+
232
+ @redis.zadd("#{@key}:sorted_state_timestamps", timestamp, timestamp)
233
+ end
232
234
 
233
- @redis.zadd("#{@key}:sorted_state_timestamps", timestamp, timestamp)
235
+ # Even if this isn't a state change, we need to update the current state
236
+ # hash summary and details (as they may have changed)
237
+ @redis.hset("check:#{@key}", 'summary', (summary || ''))
238
+ @redis.hset("check:#{@key}", 'details', (details || ''))
234
239
  end
235
240
 
236
241
  def last_update
@@ -37,15 +37,17 @@ module Flapjack
37
37
  raise "Redis connection not set" unless redis = options[:redis]
38
38
 
39
39
  rule_id = SecureRandom.uuid
40
- self.add_or_update(rule_data.merge(:id => rule_id), options)
40
+ errors = self.add_or_update(rule_data.merge(:id => rule_id), options)
41
+ return errors unless errors.nil? || errors.empty?
41
42
  self.find_by_id(rule_id, :redis => redis)
42
43
  end
43
44
 
44
45
  def update(rule_data, opts = {})
45
- return false unless self.class.add_or_update({:contact_id => @contact_id}.merge(rule_data.merge(:id => @id)),
46
+ errors = self.class.add_or_update({:contact_id => @contact_id}.merge(rule_data.merge(:id => @id)),
46
47
  :redis => @redis, :logger => opts[:logger])
48
+ return errors unless errors.nil? || errors.empty?
47
49
  refresh
48
- true
50
+ nil
49
51
  end
50
52
 
51
53
  # NB: ice_cube doesn't have much rule data validation, and has
@@ -119,7 +121,9 @@ module Flapjack
119
121
  rule_data[:warning_blackhole] = rule_data[:warning_blackhole] || false
120
122
  rule_data[:critical_blackhole] = rule_data[:critical_blackhole] || false
121
123
 
122
- return unless self.validate_data(rule_data, options)
124
+ errors = self.validate_data(rule_data, options)
125
+
126
+ return errors unless errors.nil? || errors.empty?
123
127
 
124
128
  # whitelisting fields, rather than passing through submitted data directly
125
129
  json_rule_data = {
@@ -139,7 +143,7 @@ module Flapjack
139
143
  json_rule_data[:id])
140
144
  redis.hmset("notification_rule:#{json_rule_data[:id]}",
141
145
  *json_rule_data.flatten)
142
- true
146
+ nil
143
147
  end
144
148
 
145
149
  def self.prepare_time_restriction(time_restriction, timezone = nil)
@@ -266,14 +270,14 @@ module Flapjack
266
270
  ret
267
271
  }
268
272
 
269
- return true if errors.empty?
273
+ return if errors.empty?
270
274
 
271
275
  if logger = options[:logger]
272
276
  error_str = errors.join(", ")
273
277
  logger.info "validation error: #{error_str}"
274
278
  logger.debug "rule failing validations: #{d.inspect}"
275
279
  end
276
- false
280
+ errors
277
281
  end
278
282
 
279
283
  def refresh
@@ -11,39 +11,13 @@ require 'time'
11
11
  require 'rack/fiber_pool'
12
12
  require 'sinatra/base'
13
13
 
14
- require 'flapjack/data/contact'
15
- require 'flapjack/data/entity'
16
- require 'flapjack/data/entity_check'
17
-
18
- require 'flapjack/gateways/api/entity_presenter'
19
- require 'flapjack/gateways/api/entity_check_presenter'
20
14
  require 'flapjack/rack_logger'
21
15
  require 'flapjack/redis_pool'
22
16
 
23
- # from https://github.com/sinatra/sinatra/issues/501
24
- # TODO move to its own file
25
- module Rack
26
- class JsonParamsParser < Struct.new(:app)
27
- def call(env)
28
- if env['rack.input'] and not input_parsed?(env) and type_match?(env)
29
- env['rack.request.form_input'] = env['rack.input']
30
- data = env['rack.input'].read
31
- env['rack.input'].rewind
32
- env['rack.request.form_hash'] = data.empty? ? {} : JSON.parse(data)
33
- end
34
- app.call(env)
35
- end
17
+ require 'flapjack/gateways/api/rack/json_params_parser'
36
18
 
37
- def input_parsed? env
38
- env['rack.request.form_input'].eql? env['rack.input']
39
- end
40
-
41
- def type_match? env
42
- type = env['CONTENT_TYPE'] and
43
- type.split(/\s*[;,]\s*/, 2).first.downcase == 'application/json'
44
- end
45
- end
46
- end
19
+ require 'flapjack/gateways/api/contact_methods'
20
+ require 'flapjack/gateways/api/entity_methods'
47
21
 
48
22
  module Flapjack
49
23
 
@@ -51,48 +25,20 @@ module Flapjack
51
25
 
52
26
  class API < Sinatra::Base
53
27
 
54
- # used for backwards-compatible route matching below
55
- ENTITY_CHECK_FRAGMENT = '(?:/([a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])(?:/(.+))?)?'
56
-
57
- class EntityCheckNotFound < RuntimeError
58
- attr_reader :entity, :check
59
- def initialize(entity, check)
60
- @entity = entity
61
- @check = check
62
- end
63
- end
64
-
65
- class EntityNotFound < RuntimeError
66
- attr_reader :entity
67
- def initialize(entity)
68
- @entity = entity
69
- end
70
- end
71
-
72
- class ContactNotFound < RuntimeError
73
- attr_reader :contact_id
74
- def initialize(contact_id)
75
- @contact_id = contact_id
76
- end
77
- end
78
-
79
- class NotificationRuleNotFound < RuntimeError
80
- attr_reader :rule_id
81
- def initialize(rule_id)
82
- @rule_id = rule_id
83
- end
84
- end
85
-
86
28
  include Flapjack::Utility
87
29
 
88
30
  set :show_exceptions, false
89
31
 
90
- rescue_exception = Proc.new { |env, exception|
91
- @logger.error exception.message
92
- @logger.error exception.backtrace.join("\n")
93
- [503, {}, {:errors => [exception.message]}.to_json]
94
- }
95
- use Rack::FiberPool, :size => 25, :rescue_exception => rescue_exception
32
+ #rescue_exception = Proc.new { |env, exception|
33
+ # @logger.error exception.message
34
+ # @logger.error exception.backtrace.join("\n")
35
+ # [503, {}, {:errors => [exception.message]}.to_json]
36
+ #}
37
+ #use Rack::FiberPool, :size => 25, :rescue_exception => rescue_exception
38
+ #
39
+ # FIXME: not sure why the above isn't working, had to add a general
40
+ # error handler later in this file
41
+ use Rack::FiberPool, :size => 25
96
42
 
97
43
  use Rack::MethodOverride
98
44
  use Rack::JsonParamsParser
@@ -118,496 +64,21 @@ module Flapjack
118
64
  self.class.instance_variable_get('@logger')
119
65
  end
120
66
 
121
- get '/entities' do
122
- content_type :json
123
- ret = Flapjack::Data::Entity.all(:redis => redis).sort_by(&:name).collect {|e|
124
- presenter = Flapjack::Gateways::API::EntityPresenter.new(e, :redis => redis)
125
- {'id' => e.id, 'name' => e.name, 'checks' => presenter.status }
126
- }
127
- ret.to_json
128
- end
129
-
130
- get '/checks/:entity' do
131
- content_type :json
132
- entity = find_entity(params[:entity])
133
- entity.check_list.to_json
134
- end
135
-
136
- get %r{/status#{ENTITY_CHECK_FRAGMENT}} do
137
- content_type :json
138
-
139
- captures = params[:captures] || []
140
- entity_name = captures[0]
141
- check = captures[1]
142
-
143
- entities, checks = entities_and_checks(entity_name, check)
144
-
145
- results = present_api_results(entities, checks, 'status') {|presenter|
146
- presenter.status
147
- }
148
-
149
- if entity_name
150
- # compatible with previous data format
151
- results = results.collect {|status_h| status_h[:status]}
152
- (check ? results.first : results).to_json
153
- else
154
- # new and improved data format which reflects the request param structure
155
- results.to_json
156
- end
157
- end
158
-
159
- get %r{/((?:outages|(?:un)?scheduled_maintenances|downtime))#{ENTITY_CHECK_FRAGMENT}} do
160
- action = params[:captures][0].to_sym
161
- entity_name = params[:captures][1]
162
- check = params[:captures][2]
163
-
164
- entities, checks = entities_and_checks(entity_name, check)
165
-
166
- start_time = validate_and_parsetime(params[:start_time])
167
- end_time = validate_and_parsetime(params[:end_time])
168
-
169
- results = present_api_results(entities, checks, action) {|presenter|
170
- presenter.send(action, start_time, end_time)
171
- }
172
-
173
- if check
174
- # compatible with previous data format
175
- results.first[action].to_json
176
- elsif entity_name
177
- # compatible with previous data format
178
- rename = {:unscheduled_maintenances => :unscheduled_maintenance,
179
- :scheduled_maintenances => :scheduled_maintenance}
180
- drop = [:entity]
181
- results.collect{|r|
182
- r.inject({}) {|memo, (k, v)|
183
- if new_k = rename[k]
184
- memo[new_k] = v
185
- elsif !drop.include?(k)
186
- memo[k] = v
187
- end
188
- memo
189
- }
190
- }.to_json
191
- else
192
- # new and improved data format which reflects the request param structure
193
- results.to_json
194
- end
195
- end
196
-
197
- # create a scheduled maintenance period for a check on an entity
198
- post %r{/scheduled_maintenances#{ENTITY_CHECK_FRAGMENT}} do
199
- entity_name = params[:captures][0]
200
- check = params[:captures][1]
201
-
202
- entities, checks = entities_and_checks(entity_name, check)
203
-
204
- start_time = validate_and_parsetime(params[:start_time])
205
-
206
- act_proc = proc {|entity_check|
207
- entity_check.create_scheduled_maintenance(:start_time => start_time,
208
- :duration => params[:duration].to_i, :summary => params[:summary])
209
- }
210
-
211
- bulk_api_check_action(entities, checks, act_proc)
212
- status 204
213
- end
214
-
215
- # create an acknowledgement for a service on an entity
216
- # NB currently, this does not acknowledge a specific failure event, just
217
- # the entity-check as a whole
218
- post %r{/acknowledgements#{ENTITY_CHECK_FRAGMENT}} do
219
- captures = params[:captures] || []
220
- entity_name = captures[0]
221
- check = captures[1]
222
-
223
- entities, checks = entities_and_checks(entity_name, check)
224
-
225
- dur = params[:duration] ? params[:duration].to_i : nil
226
- duration = (dur.nil? || (dur <= 0)) ? (4 * 60 * 60) : dur
227
- summary = params[:summary]
228
-
229
- opts = {'duration' => duration}
230
- opts['summary'] = summary if summary
231
-
232
- act_proc = proc {|entity_check|
233
- Flapjack::Data::Event.create_acknowledgement(
234
- entity_check.entity_name, entity_check.check,
235
- :summary => params[:summary],
236
- :duration => duration,
237
- :redis => redis)
238
- }
239
-
240
- bulk_api_check_action(entities, checks, act_proc)
241
- status 204
242
- end
243
-
244
- delete %r{/((?:un)?scheduled_maintenances)} do
245
- action = params[:captures][0]
246
-
247
- # no backwards-compatible mode here, it's a new method
248
- entities, checks = entities_and_checks(nil, nil)
249
-
250
- act_proc = case action
251
- when 'scheduled_maintenances'
252
- start_time = validate_and_parsetime(params[:start_time])
253
- opts = {}
254
- opts[:start_time] = start_time.to_i if start_time
255
- proc {|entity_check| entity_check.delete_scheduled_maintenance(opts) }
256
- when 'unscheduled_maintenances'
257
- end_time = validate_and_parsetime(params[:end_time])
258
- opts = {}
259
- opts[:end_time] = end_time.to_i if end_time
260
- proc {|entity_check| entity_check.end_unscheduled_maintenance(opts) }
261
- end
262
-
263
- bulk_api_check_action(entities, checks, act_proc)
264
- status 204
265
- end
266
-
267
- post %r{/test_notifications#{ENTITY_CHECK_FRAGMENT}} do
268
- captures = params[:captures] || []
269
- entity_name = captures[0]
270
- check = captures[1]
271
-
272
- entities, checks = entities_and_checks(entity_name, check)
273
-
274
- act_proc = proc {|entity_check|
275
- summary = params[:summary] ||
276
- "Testing notifications to all contacts interested in entity #{entity_check.entity.name}"
277
- Flapjack::Data::Event.test_notifications(
278
- entity_check.entity_name, entity_check.check,
279
- :summary => summary,
280
- :redis => redis)
281
- }
282
-
283
- bulk_api_check_action(entities, checks, act_proc)
284
- status 204
285
- end
286
-
287
- post '/entities' do
288
- pass unless 'application/json'.eql?(request.content_type)
289
-
290
- errors = []
291
- ret = nil
292
-
293
- # FIXME should scan for invalid records before making any changes, fail early
294
-
295
- entities = params[:entities]
296
- unless entities
297
- logger.debug("no entities object found in the following supplied JSON:")
298
- logger.debug(request.body)
299
- return err(403, "No entities object received")
300
- end
301
- return err(403, "The received entities object is not an Enumerable") unless entities.is_a?(Enumerable)
302
- return err(403, "Entity with a nil id detected") unless entities.any? {|e| !e['id'].nil?}
303
-
304
- entities.each do |entity|
305
- unless entity['id']
306
- errors << "Entity not imported as it has no id: #{entity.inspect}"
307
- next
308
- end
309
- Flapjack::Data::Entity.add(entity, :redis => redis)
310
- end
311
- errors.empty? ? 204 : err(403, *errors)
312
- end
313
-
314
- post '/contacts' do
315
- pass unless 'application/json'.eql?(request.content_type)
316
- content_type :json
317
-
318
- errors = []
319
-
320
- contacts_data = params[:contacts]
321
- if contacts_data.nil? || !contacts_data.is_a?(Enumerable)
322
- errors << "No valid contacts were submitted"
323
- else
324
- # stringifying as integer string params are automatically integered,
325
- # but our redis ids are strings
326
- contacts_data_ids = contacts_data.reject {|c| c['id'].nil? }.
327
- map {|co| co['id'].to_s }
328
-
329
- if contacts_data_ids.empty?
330
- errors << "No contacts with IDs were submitted"
331
- else
332
- contacts = Flapjack::Data::Contact.all(:redis => redis)
333
- contacts_h = hashify(*contacts) {|c| [c.id, c] }
334
- contacts_ids = contacts_h.keys
335
-
336
- # delete contacts not found in the bulk list
337
- (contacts_ids - contacts_data_ids).each do |contact_to_delete_id|
338
- contact_to_delete = contacts.detect {|c| c.id == contact_to_delete_id }
339
- contact_to_delete.delete!
340
- end
341
-
342
- # add or update contacts found in the bulk list
343
- contacts_data.reject {|cd| cd['id'].nil? }.each do |contact_data|
344
- if contacts_ids.include?(contact_data['id'].to_s)
345
- contacts_h[contact_data['id'].to_s].update(contact_data)
346
- else
347
- Flapjack::Data::Contact.add(contact_data, :redis => redis)
348
- end
349
- end
350
- end
351
- end
352
- errors.empty? ? 204 : err(403, *errors)
353
- end
354
-
355
- # Returns all the contacts
356
- # https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts
357
- get '/contacts' do
358
- content_type :json
359
-
360
- Flapjack::Data::Contact.all(:redis => redis).to_json
361
- end
362
-
363
- # Returns the core information about the specified contact
364
- # https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id
365
- get '/contacts/:contact_id' do
366
- content_type :json
367
-
368
- contact = find_contact(params[:contact_id])
369
- contact.to_json
370
- end
371
-
372
- # Lists this contact's notification rules
373
- # https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_notification_rules
374
- get '/contacts/:contact_id/notification_rules' do
375
- content_type :json
376
-
377
- contact = find_contact(params[:contact_id])
378
- contact.notification_rules.to_json
379
- end
380
-
381
- # Get the specified notification rule for this user
382
- # https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_notification_rules_id
383
- get '/notification_rules/:id' do
384
- content_type :json
385
-
386
- rule = find_rule(params[:id])
387
- rule.to_json
388
- end
389
-
390
- # Creates a notification rule for a contact
391
- # https://github.com/flpjck/flapjack/wiki/API#wiki-post_contacts_id_notification_rules
392
- post '/notification_rules' do
393
- content_type :json
394
-
395
- if params[:id]
396
- halt err(403, "post cannot be used for update, do a put instead")
397
- end
398
-
399
- logger.debug("post /notification_rules data: ")
400
- logger.debug(params.inspect)
401
-
402
- contact = find_contact(params[:contact_id])
403
-
404
- rule_data = hashify(:entities, :entity_tags,
405
- :warning_media, :critical_media, :time_restrictions,
406
- :warning_blackhole, :critical_blackhole) {|k| [k, params[k]]}
407
-
408
- unless rule = contact.add_notification_rule(rule_data, :logger => logger)
409
- halt err(403, "invalid notification rule data")
410
- end
411
- rule.to_json
412
- end
413
-
414
- # Updates a notification rule
415
- # https://github.com/flpjck/flapjack/wiki/API#wiki-put_notification_rules_id
416
- put('/notification_rules/:id') do
417
- content_type :json
418
-
419
- logger.debug("put /notification_rules/#{params[:id]} data: ")
420
- logger.debug(params.inspect)
421
-
422
- rule = find_rule(params[:id])
423
- contact = find_contact(rule.contact_id)
424
-
425
- rule_data = hashify(:entities, :entity_tags,
426
- :warning_media, :critical_media, :time_restrictions,
427
- :warning_blackhole, :critical_blackhole) {|k| [k, params[k]]}
428
-
429
- unless rule.update(rule_data, :logger => logger)
430
- halt err(403, "invalid notification rule data")
431
- end
432
- rule.to_json
433
- end
434
-
435
- # Deletes a notification rule
436
- # https://github.com/flpjck/flapjack/wiki/API#wiki-put_notification_rules_id
437
- delete('/notification_rules/:id') do
438
- logger.debug("delete /notification_rules/#{params[:id]}")
439
- rule = find_rule(params[:id])
440
- logger.debug("rule to delete: #{rule.inspect}, contact_id: #{rule.contact_id}")
441
- contact = find_contact(rule.contact_id)
442
- contact.delete_notification_rule(rule)
443
- status 204
444
- end
445
-
446
- # Returns the media of a contact
447
- # https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_media
448
- get '/contacts/:contact_id/media' do
449
- content_type :json
450
-
451
- contact = find_contact(params[:contact_id])
452
-
453
- media = contact.media
454
- media_intervals = contact.media_intervals
455
- media_addr_int = hashify(*media.keys) {|k|
456
- [k, {'address' => media[k],
457
- 'interval' => media_intervals[k] }]
458
- }
459
- media_addr_int.to_json
460
- end
461
-
462
- # Returns the specified media of a contact
463
- # https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_media_media
464
- get('/contacts/:contact_id/media/:id') do
465
- content_type :json
466
-
467
- contact = find_contact(params[:contact_id])
468
- media = contact.media[params[:id]]
469
- if media.nil?
470
- halt err(403, "no #{params[:id]} for contact '#{params[:contact_id]}'")
471
- end
472
- interval = contact.media_intervals[params[:id]]
473
- if interval.nil?
474
- halt err(403, "no #{params[:id]} interval for contact '#{params[:contact_id]}'")
475
- end
476
- {'address' => media,
477
- 'interval' => interval}.to_json
478
- end
479
-
480
- # Creates or updates a media of a contact
481
- # https://github.com/flpjck/flapjack/wiki/API#wiki-put_contacts_id_media_media
482
- put('/contacts/:contact_id/media/:id') do
483
- content_type :json
484
-
485
- contact = find_contact(params[:contact_id])
486
- errors = []
487
- if params[:address].nil?
488
- errors << "no address for '#{params[:id]}' media"
489
- end
490
-
491
- halt err(403, *errors) unless errors.empty?
492
-
493
- contact.set_address_for_media(params[:id], params[:address])
494
- contact.set_interval_for_media(params[:id], params[:interval])
495
-
496
- {'address' => contact.media[params[:id]],
497
- 'interval' => contact.media_intervals[params[:id]]}.to_json
498
- end
499
-
500
- # delete a media of a contact
501
- delete('/contacts/:contact_id/media/:id') do
502
- contact = find_contact(params[:contact_id])
503
- contact.remove_media(params[:id])
504
- status 204
505
- end
506
-
507
- # Returns the timezone of a contact
508
- # https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_timezone
509
- get('/contacts/:contact_id/timezone') do
510
- content_type :json
511
-
512
- contact = find_contact(params[:contact_id])
513
- contact.timezone.name.to_json
514
- end
515
-
516
- # Sets the timezone of a contact
517
- # https://github.com/flpjck/flapjack/wiki/API#wiki-put_contacts_id_timezone
518
- put('/contacts/:contact_id/timezone') do
519
- content_type :json
520
-
521
- contact = find_contact(params[:contact_id])
522
- contact.timezone = params[:timezone]
523
- contact.timezone.name.to_json
524
- end
525
-
526
- # Removes the timezone of a contact
527
- # https://github.com/flpjck/flapjack/wiki/API#wiki-put_contacts_id_timezone
528
- delete('/contacts/:contact_id/timezone') do
529
- contact = find_contact(params[:contact_id])
530
- contact.timezone = nil
531
- status 204
532
- end
533
-
534
- post '/contacts/:contact_id/tags' do
535
- content_type :json
536
-
537
- tags = find_tags(params[:tag])
538
- contact = find_contact(params[:contact_id])
539
- contact.add_tags(*tags)
540
- contact.tags.to_json
541
- end
542
-
543
- post '/contacts/:contact_id/entity_tags' do
544
- content_type :json
545
- contact = find_contact(params[:contact_id])
546
- contact.entities.map {|e| e[:entity]}.each do |entity|
547
- next unless tags = params[:entity][entity.name]
548
- entity.add_tags(*tags)
549
- end
550
- contact_ent_tag = hashify(*contact.entities(:tags => true)) {|et|
551
- [et[:entity].name, et[:tags]]
552
- }
553
- contact_ent_tag.to_json
554
- end
555
-
556
- delete '/contacts/:contact_id/tags' do
557
- tags = find_tags(params[:tag])
558
- contact = find_contact(params[:contact_id])
559
- contact.delete_tags(*tags)
560
- status 204
561
- end
562
-
563
- delete '/contacts/:contact_id/entity_tags' do
564
- contact = find_contact(params[:contact_id])
565
- contact.entities.map {|e| e[:entity]}.each do |entity|
566
- next unless tags = params[:entity][entity.name]
567
- entity.delete_tags(*tags)
568
- end
569
- status 204
570
- end
571
-
572
- get '/contacts/:contact_id/tags' do
573
- content_type :json
574
-
575
- contact = find_contact(params[:contact_id])
576
- contact.tags.to_json
577
- end
578
-
579
- get '/contacts/:contact_id/entity_tags' do
580
- content_type :json
581
-
582
- contact = find_contact(params[:contact_id])
583
- contact_ent_tag = hashify(*contact.entities(:tags => true)) {|et|
584
- [et[:entity].name, et[:tags]]
585
- }
586
- contact_ent_tag.to_json
587
- end
588
-
589
- post '/entities/:entity/tags' do
590
- content_type :json
591
-
592
- tags = find_tags(params[:tag])
593
- entity = find_entity(params[:entity])
594
- entity.add_tags(*tags)
595
- entity.tags.to_json
67
+ before do
68
+ input = env['rack.input'].read
69
+ input_short = input.gsub(/\n/, '').gsub(/\s+/, ' ')
70
+ logger.info("#{request.request_method} #{request.path_info}#{request.query_string} #{input_short[0..80]}")
71
+ logger.debug("#{request.request_method} #{request.path_info}#{request.query_string} #{input}")
72
+ env['rack.input'].rewind
596
73
  end
597
74
 
598
- delete '/entities/:entity/tags' do
599
- tags = find_tags(params[:tag])
600
- entity = find_entity(params[:entity])
601
- entity.delete_tags(*tags)
602
- status 204
75
+ after do
76
+ logger.debug("Returning #{response.status} for #{request.request_method} #{request.path_info}#{request.query_string}")
603
77
  end
604
78
 
605
- get '/entities/:entity/tags' do
606
- content_type :json
79
+ register Flapjack::Gateways::API::EntityMethods
607
80
 
608
- entity = find_entity(params[:entity])
609
- entity.tags.to_json
610
- end
81
+ register Flapjack::Gateways::API::ContactMethods
611
82
 
612
83
  not_found do
613
84
  logger.debug("in not_found :-(")
@@ -634,6 +105,11 @@ module Flapjack
634
105
  err(403, "could not find entity check '#{e.check}'")
635
106
  end
636
107
 
108
+ error do
109
+ e = env['sinatra.error']
110
+ err(response.status, "#{e.class} - #{e.message}")
111
+ end
112
+
637
113
  private
638
114
 
639
115
  def err(status, *msg)
@@ -642,109 +118,6 @@ module Flapjack
642
118
  [status, {}, {:errors => msg}.to_json]
643
119
  end
644
120
 
645
- def find_contact(contact_id)
646
- contact = Flapjack::Data::Contact.find_by_id(contact_id, :logger => logger, :redis => redis)
647
- raise Flapjack::Gateways::API::ContactNotFound.new(contact_id) if contact.nil?
648
- contact
649
- end
650
-
651
- def find_rule(rule_id)
652
- rule = Flapjack::Data::NotificationRule.find_by_id(rule_id, :logger => logger, :redis => redis)
653
- raise Flapjack::Gateways::API::NotificationRuleNotFound.new(rule_id) if rule.nil?
654
- rule
655
- end
656
-
657
- def find_entity(entity_name)
658
- entity = Flapjack::Data::Entity.find_by_name(entity_name, :redis => redis)
659
- raise Flapjack::Gateways::API::EntityNotFound.new(entity_name) if entity.nil?
660
- entity
661
- end
662
-
663
- def find_entity_check(entity, check)
664
- entity_check = Flapjack::Data::EntityCheck.for_entity(entity,
665
- check, :redis => redis)
666
- raise Flapjack::Gateways::API::EntityCheckNotFound.new(entity, check) if entity_check.nil?
667
- entity_check
668
- end
669
-
670
- def entities_and_checks(entity_name, check)
671
- if entity_name
672
- # backwards-compatible, single entity or entity&check from route
673
- entities = check ? nil : [entity_name]
674
- checks = check ? {entity_name => check} : nil
675
- else
676
- # new and improved bulk API queries
677
- entities = params[:entity]
678
- checks = params[:check]
679
- entities = [entities] unless entities.nil? || entities.is_a?(Array)
680
- # TODO err if checks isn't a Hash (similar rules as in flapjack-diner)
681
- end
682
- [entities, checks]
683
- end
684
-
685
- def bulk_api_check_action(entities, entity_checks, action, params = {})
686
- unless entities.nil? || entities.empty?
687
- entities.each do |entity_name|
688
- entity = find_entity(entity_name)
689
- checks = entity.check_list.sort
690
- checks.each do |check|
691
- action.call( find_entity_check(entity, check) )
692
- end
693
- end
694
- end
695
-
696
- unless entity_checks.nil? || entity_checks.empty?
697
- entity_checks.each_pair do |entity_name, checks|
698
- entity = find_entity(entity_name)
699
- checks = [checks] unless checks.is_a?(Array)
700
- checks.each do |check|
701
- action.call( find_entity_check(entity, check) )
702
- end
703
- end
704
- end
705
- end
706
-
707
- def present_api_results(entities, entity_checks, result_type, &block)
708
- result = []
709
-
710
- unless entities.nil? || entities.empty?
711
- result += entities.collect {|entity_name|
712
- entity = find_entity(entity_name)
713
- yield(Flapjack::Gateways::API::EntityPresenter.new(entity, :redis => redis))
714
- }.flatten(1)
715
- end
716
-
717
- unless entity_checks.nil? || entity_checks.empty?
718
- result += entity_checks.inject([]) {|memo, (entity_name, checks)|
719
- checks = [checks] unless checks.is_a?(Array)
720
- entity = find_entity(entity_name)
721
- memo += checks.collect {|check|
722
- entity_check = find_entity_check(entity, check)
723
- {:entity => entity_name,
724
- :check => check,
725
- result_type.to_sym => yield(Flapjack::Gateways::API::EntityCheckPresenter.new(entity_check))
726
- }
727
- }
728
- }.flatten(1)
729
- end
730
-
731
- result
732
- end
733
-
734
- def find_tags(tags)
735
- halt err(403, "no tags") if tags.nil? || tags.empty?
736
- tags
737
- end
738
-
739
- # NB: casts to UTC before converting to a timestamp
740
- def validate_and_parsetime(value)
741
- return unless value
742
- Time.iso8601(value).getutc.to_i
743
- rescue ArgumentError => e
744
- logger.error "Couldn't parse time from '#{value}'"
745
- nil
746
- end
747
-
748
121
  end
749
122
 
750
123
  end