flapjack-diner 0.12

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/flapjack-diner/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Ali Graham"]
6
+ gem.email = ["ali.graham@bulletproof.net"]
7
+ gem.summary = %q{Access the API of a Flapjack system monitoring server}
8
+ gem.description = %q{Wraps raw API calls to a Flapjack server API with friendlier ruby methods.}
9
+ gem.homepage = 'https://github.com/flpjck/flapjack-diner'
10
+
11
+ gem.files = `git ls-files`.split($\) - ['Gemfile.lock']
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "flapjack-diner"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Flapjack::Diner::VERSION
17
+
18
+ gem.add_dependency('httparty', '>= 0.10')
19
+ gem.add_dependency('json', '>= 1.7.7')
20
+
21
+ gem.add_development_dependency('bundler')
22
+ gem.add_development_dependency('rake')
23
+ gem.add_development_dependency('rspec', '>= 2.0.0')
24
+ gem.add_development_dependency('simplecov')
25
+ gem.add_development_dependency('webmock')
26
+ end
@@ -0,0 +1,455 @@
1
+ require 'httparty'
2
+ require 'json'
3
+ require 'uri'
4
+ require 'cgi'
5
+
6
+ require "flapjack-diner/version"
7
+ require "flapjack-diner/argument_validator"
8
+
9
+ module Flapjack
10
+ module Diner
11
+ SUCCESS_STATUS_CODES = [200, 204]
12
+
13
+ include HTTParty
14
+
15
+ format :json
16
+
17
+ class << self
18
+
19
+ attr_accessor :logger
20
+
21
+ # NB: clients will need to handle any exceptions caused by,
22
+ # e.g., network failures or non-parseable JSON data.
23
+
24
+ def entities
25
+ perform_get('/entities')
26
+ end
27
+
28
+ def checks(entity)
29
+ perform_get("/checks/#{escape(entity)}")
30
+ end
31
+
32
+ def status(entity, options = {})
33
+ check = options.delete(:check)
34
+ args = options.merge( check ? {:check => {entity => check}} : {:entity => entity} )
35
+ validate_bulk_params(args)
36
+ perform_get('/status', args)
37
+ end
38
+
39
+ def bulk_status(options = {})
40
+ validate_bulk_params(options)
41
+ perform_get('/status', options)
42
+ end
43
+
44
+ # maybe rename 'create_acknowledgement!' ?
45
+ def acknowledge!(entity, check, options = {})
46
+ args = options.merge(:check => {entity => check})
47
+ validate_bulk_params(args)
48
+ perform_post('/acknowledgements', args)
49
+ end
50
+
51
+ def bulk_acknowledge!(options = {})
52
+ validate_bulk_params(options)
53
+ perform_post('/acknowledgements', options)
54
+ end
55
+
56
+ # maybe rename 'create_test_notifications!' ?
57
+ def test_notifications!(entity, check, options = {})
58
+ args = options.merge(:check => {entity => check})
59
+ validate_bulk_params(args)
60
+ perform_post('/test_notifications', args)
61
+ end
62
+
63
+ def bulk_test_notifications!(options = {})
64
+ validate_bulk_params(options)
65
+ perform_post('/test_notifications', options)
66
+ end
67
+
68
+ def create_scheduled_maintenance!(entity, check, options = {})
69
+ args = options.merge( check ? {:check => {entity => check}} : {:entity => entity} )
70
+
71
+ validate_bulk_params(args) do
72
+ validate :query => :start_time, :as => [:required, :time]
73
+ validate :query => :duration, :as => [:required, :integer]
74
+ end
75
+
76
+ perform_post('/scheduled_maintenances', args)
77
+ end
78
+
79
+ def bulk_create_scheduled_maintenance!(options = {})
80
+ validate_bulk_params(options) do
81
+ validate :query => :start_time, :as => [:required, :time]
82
+ validate :query => :duration, :as => [:required, :integer]
83
+ end
84
+
85
+ perform_post('/scheduled_maintenances', options)
86
+ end
87
+
88
+ def delete_scheduled_maintenance!(entity, check, options = {})
89
+ args = options.merge( check ? {:check => {entity => check}} : {:entity => entity} )
90
+
91
+ validate_bulk_params(args) do
92
+ validate :query => :start_time, :as => :required
93
+ end
94
+
95
+ perform_delete('/scheduled_maintenances', args)
96
+ end
97
+
98
+ def bulk_delete_scheduled_maintenance!(options = {})
99
+ validate_bulk_params(options) do
100
+ validate :query => :start_time, :as => :required
101
+ end
102
+
103
+ perform_delete('/scheduled_maintenances', options)
104
+ end
105
+
106
+ def delete_unscheduled_maintenance!(entity, check, options = {})
107
+ args = options.merge( check ? {:check => {entity => check}} : {:entity => entity} )
108
+ validate_bulk_params(args) do
109
+ validate :query => :end_time, :as => :time
110
+ end
111
+ perform_delete('/unscheduled_maintenances', args)
112
+ end
113
+
114
+ def bulk_delete_unscheduled_maintenance!(options)
115
+ validate_bulk_params(options) do
116
+ validate :query => :end_time, :as => :time
117
+ end
118
+ perform_delete('/unscheduled_maintenances', options)
119
+ end
120
+
121
+ def scheduled_maintenances(entity, options = {})
122
+ check = options.delete(:check)
123
+ args = options.merge( check ? {:check => {entity => check}} : {:entity => entity} )
124
+
125
+ validate_bulk_params(args) do
126
+ validate :query => [:start_time, :end_time], :as => :time
127
+ end
128
+
129
+ perform_get('/scheduled_maintenances', args)
130
+ end
131
+
132
+ def bulk_scheduled_maintenances(options = {})
133
+ validate_bulk_params(options) do
134
+ validate :query => [:start_time, :end_time], :as => :time
135
+ end
136
+
137
+ perform_get('/scheduled_maintenances', options)
138
+ end
139
+
140
+ def unscheduled_maintenances(entity, options = {})
141
+ check = options.delete(:check)
142
+ args = options.merge( check ? {:check => {entity => check}} : {:entity => entity} )
143
+
144
+ validate_bulk_params(args) do
145
+ validate :query => [:start_time, :end_time], :as => :time
146
+ end
147
+
148
+ perform_get('/unscheduled_maintenances', args)
149
+ end
150
+
151
+ def bulk_unscheduled_maintenances(options = {})
152
+ validate_bulk_params(options) do
153
+ validate :query => [:start_time, :end_time], :as => :time
154
+ end
155
+
156
+ perform_get('/unscheduled_maintenances', options)
157
+ end
158
+
159
+ def outages(entity, options = {})
160
+ check = options.delete(:check)
161
+ args = options.merge( check ? {:check => {entity => check}} : {:entity => entity} )
162
+
163
+ validate_bulk_params(args) do
164
+ validate :query => [:start_time, :end_time], :as => :time
165
+ end
166
+
167
+ perform_get('/outages', args)
168
+ end
169
+
170
+ def bulk_outages(options = {})
171
+ validate_bulk_params(options) do
172
+ validate :query => [:start_time, :end_time], :as => :time
173
+ end
174
+
175
+ perform_get('/outages', options)
176
+ end
177
+
178
+ def downtime(entity, options = {})
179
+ check = options.delete(:check)
180
+ args = options.merge( check ? {:check => {entity => check}} : {:entity => entity} )
181
+
182
+ validate_bulk_params(args) do
183
+ validate :query => [:start_time, :end_time], :as => :time
184
+ end
185
+
186
+ perform_get('/downtime', args)
187
+ end
188
+
189
+ def bulk_downtime(options = {})
190
+ validate_bulk_params(options) do
191
+ validate :query => [:start_time, :end_time], :as => :time
192
+ end
193
+
194
+ perform_get('/downtime', options)
195
+ end
196
+
197
+ def entity_tags(entity)
198
+ perform_get("/entities/#{escape(entity)}/tags")
199
+ end
200
+
201
+ def add_entity_tags!(entity, *tags)
202
+ perform_post("/entities/#{escape(entity)}/tags", :tag => tags)
203
+ end
204
+
205
+ def delete_entity_tags!(entity, *tags)
206
+ perform_delete("/entities/#{escape(entity)}/tags", :tag => tags)
207
+ end
208
+
209
+ def contacts
210
+ perform_get('/contacts')
211
+ end
212
+
213
+ def contact(contact_id)
214
+ perform_get("/contacts/#{escape(contact_id)}")
215
+ end
216
+
217
+ def contact_tags(contact_id)
218
+ perform_get("/contacts/#{escape(contact_id)}/tags")
219
+ end
220
+
221
+ def contact_entitytags(contact_id)
222
+ perform_get("/contacts/#{escape(contact_id)}/entity_tags")
223
+ end
224
+
225
+ def add_contact_tags!(contact_id, *tags)
226
+ perform_post("/contacts/#{escape(contact_id)}/tags", :tag => tags)
227
+ end
228
+
229
+ # TODO better checking of provided data
230
+ def add_contact_entitytags!(contact_id, entity_tags = {})
231
+ perform_post("/contacts/#{escape(contact_id)}/entity_tags", :entity => entity_tags)
232
+ end
233
+
234
+ def delete_contact_tags!(contact_id, *tags)
235
+ perform_delete("/contacts/#{escape(contact_id)}/tags", :tag => tags)
236
+ end
237
+
238
+ # TODO better checking of provided data
239
+ def delete_contact_entitytags!(contact_id, entity_tags = {})
240
+ perform_delete("/contacts/#{escape(contact_id)}/entity_tags", :entity => entity_tags)
241
+ end
242
+
243
+ def notification_rules(contact_id)
244
+ perform_get("/contacts/#{escape(contact_id)}/notification_rules")
245
+ end
246
+
247
+ def notification_rule(rule_id)
248
+ perform_get("/notification_rules/#{escape(rule_id)}")
249
+ end
250
+
251
+ def create_notification_rule!(rule)
252
+ perform_post('/notification_rules', rule)
253
+ end
254
+
255
+ def update_notification_rule!(rule_id, rule)
256
+ perform_put("/notification_rules/#{escape(rule_id)}", rule)
257
+ end
258
+
259
+ def delete_notification_rule!(rule_id)
260
+ perform_delete("/notification_rules/#{escape(rule_id)}")
261
+ end
262
+
263
+ def contact_media(contact_id)
264
+ perform_get("/contacts/#{escape(contact_id)}/media")
265
+ end
266
+
267
+ def contact_medium(contact_id, media_type)
268
+ perform_get("/contacts/#{escape(contact_id)}/media/#{escape(media_type)}")
269
+ end
270
+
271
+ def update_contact_medium!(contact_id, media_type, media)
272
+ perform_put("/contacts/#{escape(contact_id)}/media/#{escape(media_type)}", media)
273
+ end
274
+
275
+ def delete_contact_medium!(contact_id, media_type)
276
+ perform_delete("/contacts/#{escape(contact_id)}/media/#{escape(media_type)}")
277
+ end
278
+
279
+ def contact_timezone(contact_id)
280
+ perform_get("/contacts/#{escape(contact_id)}/timezone")
281
+ end
282
+
283
+ def update_contact_timezone!(contact_id, timezone)
284
+ perform_put("/contacts/#{escape(contact_id)}/timezone", :timezone => timezone)
285
+ end
286
+
287
+ def delete_contact_timezone!(contact_id)
288
+ perform_delete("/contacts/#{escape(contact_id)}/timezone")
289
+ end
290
+
291
+ def last_error
292
+ @last_error
293
+ end
294
+
295
+ private
296
+
297
+ def perform_get(path, params = nil)
298
+ req_uri = build_uri(path, params)
299
+ logger.info "GET #{req_uri}" if logger
300
+ response = get(req_uri.request_uri)
301
+ handle_response(response)
302
+ end
303
+
304
+ def perform_post(path, body = {})
305
+ req_uri = build_uri(path)
306
+ if logger
307
+ log_post = "POST #{req_uri}"
308
+ log_post << "\n Params: #{body.inspect}" if body
309
+ logger.info log_post
310
+ end
311
+ opts = body ? {:body => prepare_nested_query(body).to_json, :headers => {'Content-Type' => 'application/json'}} : {}
312
+ response = post(req_uri.request_uri, opts)
313
+ handle_response(response)
314
+ end
315
+
316
+ def perform_put(path, body = {})
317
+ req_uri = build_uri(path)
318
+ if logger
319
+ log_put = "PUT #{req_uri}"
320
+ log_put << "\n Params: #{body.inspect}" if body
321
+ logger.info log_put
322
+ end
323
+ opts = body ? {:body => prepare_nested_query(body).to_json, :headers => {'Content-Type' => 'application/json'}} : {}
324
+ response = put(req_uri.request_uri, opts)
325
+ handle_response(response)
326
+ end
327
+
328
+ def perform_delete(path, body = nil)
329
+ req_uri = build_uri(path)
330
+ if logger
331
+ log_delete = "DELETE #{req_uri}"
332
+ log_delete << "\n Params: #{body.inspect}" if body
333
+ logger.info log_delete
334
+ end
335
+ opts = body ? {:body => prepare_nested_query(body).to_json, :headers => {'Content-Type' => 'application/json'}} : {}
336
+ response = delete(req_uri.request_uri, opts)
337
+ handle_response(response)
338
+ end
339
+
340
+ def handle_response(response)
341
+ response_body = response.body
342
+ response_start = response_body ? response_body[0..300] : nil
343
+ if logger
344
+ logger.info " Response Code: #{response.code}#{response.message ? response.message : ''}"
345
+ logger.info " Response Body: #{response_start}" if response_start
346
+ end
347
+ parsed_response = response.respond_to?(:parsed_response) ? response.parsed_response : nil
348
+ unless SUCCESS_STATUS_CODES.include?(response.code)
349
+ self.last_error = {'status_code' => response.code}.merge(parsed_response)
350
+ return nil
351
+ end
352
+ return true unless (response.code == 200) && parsed_response
353
+ parsed_response
354
+ end
355
+
356
+ def validate_bulk_params(query = {}, &validation)
357
+ errors = []
358
+
359
+ entities = query[:entity]
360
+ checks = query[:check]
361
+
362
+ if entities && !entities.is_a?(String) &&
363
+ (!entities.is_a?(Array) || !entities.all? {|e| e.is_a?(String)})
364
+ raise ArgumentError.new("Entity argument must be a String, or an Array of Strings")
365
+ end
366
+
367
+ if checks && (!checks.is_a?(Hash) || !checks.all? {|k, v|
368
+ k.is_a?(String) && (v.is_a?(String) || (v.is_a?(Array) && v.all?{|vv| vv.is_a?(String)}))
369
+ })
370
+ raise ArgumentError.new("Check argument must be a Hash with keys String, values either String or Array of Strings")
371
+ end
372
+
373
+ if entities.nil? && checks.nil?
374
+ raise ArgumentError.new("Entity and/or check arguments must be provided")
375
+ end
376
+
377
+ ArgumentValidator.new(query).instance_eval(&validation) if block_given?
378
+ end
379
+
380
+ # copied from Rack::Utils -- builds the query string for GETs
381
+ def build_nested_query(value, prefix = nil)
382
+ if value.respond_to?(:iso8601)
383
+ raise ArgumentError, "value must be a Hash" if prefix.nil?
384
+ "#{prefix}=#{escape(value.iso8601)}"
385
+ else
386
+ case value
387
+ when Array
388
+ value.map { |v|
389
+ build_nested_query(v, "#{prefix}[]")
390
+ }.join("&")
391
+ when Hash
392
+ value.map { |k, v|
393
+ build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
394
+ }.join("&")
395
+ when String, Integer
396
+ raise ArgumentError, "value must be a Hash" if prefix.nil?
397
+ "#{prefix}=#{escape(value.to_s)}"
398
+ else
399
+ prefix
400
+ end
401
+ end
402
+ end
403
+
404
+ def escape(s)
405
+ URI.encode_www_form_component(s)
406
+ end
407
+
408
+ # used for the JSON data hashes in POST, PUT, DELETE
409
+ def prepare_nested_query(value)
410
+ if value.respond_to?(:iso8601)
411
+ value.iso8601
412
+ else
413
+ case value
414
+ when Array
415
+ value.map { |v| prepare_nested_query(v) }
416
+ when Hash
417
+ value.inject({}) do |memo, (k, v)|
418
+ memo[k] = prepare_nested_query(v)
419
+ memo
420
+ end
421
+ when Integer, TrueClass, FalseClass, NilClass
422
+ value
423
+ else
424
+ value.to_s
425
+ end
426
+ end
427
+ end
428
+
429
+ def protocol_host_port
430
+ self.base_uri =~ /^(?:(https?):\/\/)?([a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])(?::(\d+))?/i
431
+ protocol = ($1 || 'http').downcase
432
+ host = $2
433
+ port = $3
434
+
435
+ if port.nil? || port.to_i < 1 || port.to_i > 65535
436
+ port = 'https'.eql?(protocol) ? 443 : 80
437
+ else
438
+ port = port.to_i
439
+ end
440
+
441
+ [protocol, host, port]
442
+ end
443
+
444
+ def build_uri(path, params = nil)
445
+ pr, ho, po = protocol_host_port
446
+ URI::HTTP.build(:protocol => pr, :host => ho, :port => po,
447
+ :path => path, :query => (params.nil? || params.empty? ? nil : build_nested_query(params)))
448
+ end
449
+
450
+ def last_error=(error)
451
+ @last_error = error
452
+ end
453
+ end
454
+ end
455
+ end