flapjack-diner 0.15 → 1.0.0.rc1

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.
@@ -3,12 +3,12 @@ require 'json'
3
3
  require 'uri'
4
4
  require 'cgi'
5
5
 
6
- require "flapjack-diner/version"
7
- require "flapjack-diner/argument_validator"
6
+ require 'flapjack-diner/version'
7
+ require 'flapjack-diner/argument_validator'
8
8
 
9
9
  module Flapjack
10
10
  module Diner
11
- SUCCESS_STATUS_CODES = [200, 204]
11
+ SUCCESS_STATUS_CODES = [200, 201, 204]
12
12
 
13
13
  include HTTParty
14
14
 
@@ -21,271 +21,337 @@ module Flapjack
21
21
  # NB: clients will need to handle any exceptions caused by,
22
22
  # e.g., network failures or non-parseable JSON data.
23
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
- path = check.nil? ? "/status/#{escape(entity)}" : "/status/#{escape(entity)}/#{escape(check)}"
35
- perform_get(path)
36
- end
37
-
38
- def bulk_status(options = {})
39
- validate_bulk_params(options)
40
- perform_get('/status', options)
41
- end
42
-
43
- # maybe rename 'create_acknowledgement!' ?
44
- def acknowledge!(entity, check, options = {})
45
- args = options.merge(:check => {entity => check})
46
- validate_bulk_params(args)
47
- perform_post('/acknowledgements', args)
24
+ # 1: Contacts
25
+ def create_contacts(*args)
26
+ ids, params, data = unwrap_ids_and_params(*args)
27
+ data.each do |d|
28
+ validate_params(d) do
29
+ validate :query => [:first_name, :last_name, :email], :as => [:required, :string]
30
+ validate :query => :timezone, :as => :string
31
+ validate :query => :tags, :as => :array_of_strings
32
+ end
33
+ end
34
+ perform_post('/contacts', nil, :contacts => data)
48
35
  end
49
36
 
50
- def bulk_acknowledge!(options = {})
51
- validate_bulk_params(options)
52
- perform_post('/acknowledgements', options)
37
+ def contacts(*ids)
38
+ extract_get('contacts', perform_get('/contacts', ids))
53
39
  end
54
40
 
55
- # maybe rename 'create_test_notifications!' ?
56
- def test_notifications!(entity, check, options = {})
57
- args = options.merge(:check => {entity => check})
58
- validate_bulk_params(args)
59
- perform_post('/test_notifications', args)
41
+ def update_contacts(*args)
42
+ ids, params, data = unwrap_ids_and_params(*args)
43
+ raise "'update_contacts' requires at least one contact id parameter" if ids.nil? || ids.empty?
44
+ validate_params(params) do
45
+ validate :query => [:first_name, :last_name,
46
+ :email, :timezone], :as => :string
47
+ validate :query => :tags, :as => :array_of_strings
48
+ end
49
+ ops = params.inject([]) do |memo, (k,v)|
50
+ case k
51
+ when :add_entity
52
+ memo << {:op => 'add',
53
+ :path => '/contacts/0/links/entities/-',
54
+ :value => v}
55
+ when :remove_entity
56
+ memo << {:op => 'remove',
57
+ :path => "/contacts/0/links/entities/#{v}"}
58
+ # # Not supported yet due to id brokenness
59
+ # when :add_medium
60
+ # memo << {:op => 'add',
61
+ # :path => '/contacts/0/links/media/-',
62
+ # :value => v}
63
+ # when :remove_medium
64
+ # memo << {:op => 'remove',
65
+ # :path => "/contacts/0/links/media/#{v}"}
66
+ when :add_notification_rule
67
+ memo << {:op => 'add',
68
+ :path => '/contacts/0/links/notification_rules/-',
69
+ :value => v}
70
+ when :remove_notification_rule
71
+ memo << {:op => 'remove',
72
+ :path => "/contacts/0/links/notification_rules/#{v}"}
73
+ when :first_name, :last_name, :email, :timezone, :tags
74
+ memo << {:op => 'replace',
75
+ :path => "/contacts/0/#{k.to_s}",
76
+ :value => v}
77
+ end
78
+ memo
79
+ end
80
+ raise "'update_contacts' did not find any valid update fields" if ops.empty?
81
+ perform_patch("/contacts/#{escaped_ids(ids)}", nil, ops)
60
82
  end
61
83
 
62
- def bulk_test_notifications!(options = {})
63
- validate_bulk_params(options)
64
- perform_post('/test_notifications', options)
84
+ def delete_contacts(*ids)
85
+ raise "'delete_contacts' requires at least one contact id parameter" if ids.nil? || ids.empty?
86
+ perform_delete('/contacts', ids)
65
87
  end
66
88
 
67
- def create_scheduled_maintenance!(entity, check, options = {})
68
- args = options.merge( check ? {:check => {entity => check}} : {:entity => entity} )
69
89
 
70
- validate_bulk_params(args) do
71
- validate :query => :start_time, :as => [:required, :time]
72
- validate :query => :duration, :as => [:required, :integer]
90
+ # 2: Media
91
+ def create_contact_media(*args)
92
+ ids, params, data = unwrap_ids_and_params(*args)
93
+ raise "'create_contact_media' requires at least one contact id parameter" if ids.nil? || ids.empty?
94
+ data.each do |d|
95
+ validate_params(d) do
96
+ validate :query => [:type, :address], :as => [:required, :string]
97
+ validate :query => [:interval, :rollup_threshold], :as => [:required, :integer]
98
+ end
73
99
  end
74
-
75
- perform_post('/scheduled_maintenances', args)
100
+ perform_post("/contacts/#{escaped_ids(ids)}/media", nil, :media => data)
76
101
  end
77
102
 
78
- def bulk_create_scheduled_maintenance!(options = {})
79
- validate_bulk_params(options) do
80
- validate :query => :start_time, :as => [:required, :time]
81
- validate :query => :duration, :as => [:required, :integer]
82
- end
83
-
84
- perform_post('/scheduled_maintenances', options)
103
+ def media(*ids)
104
+ extract_get('media', perform_get('/media', ids))
85
105
  end
86
106
 
87
- def delete_scheduled_maintenance!(entity, check, options = {})
88
- args = options.merge( check ? {:check => {entity => check}} : {:entity => entity} )
89
-
90
- validate_bulk_params(args) do
91
- validate :query => :start_time, :as => :required
107
+ def update_media(*args)
108
+ ids, params, data = unwrap_ids_and_params(*args)
109
+ raise "'update_media' requires at least one media id parameter" if ids.nil? || ids.empty?
110
+ validate_params(params) do
111
+ validate :query => :address, :as => :string
112
+ validate :query => [:interval, :rollup_threshold], :as => :integer
92
113
  end
93
-
94
- perform_delete('/scheduled_maintenances', args)
95
- end
96
-
97
- def bulk_delete_scheduled_maintenance!(options = {})
98
- validate_bulk_params(options) do
99
- validate :query => :start_time, :as => :required
114
+ ops = params.inject([]) do |memo, (k,v)|
115
+ case k
116
+ when :address, :interval, :rollup_threshold
117
+ memo << {:op => 'replace',
118
+ :path => "/media/0/#{k.to_s}",
119
+ :value => v}
120
+ end
121
+ memo
100
122
  end
101
-
102
- perform_delete('/scheduled_maintenances', options)
123
+ raise "'update_media' did not find any valid update fields" if ops.empty?
124
+ perform_patch("/media/#{escaped_ids(ids)}", nil, ops)
103
125
  end
104
126
 
105
- def delete_unscheduled_maintenance!(entity, check, options = {})
106
- args = options.merge( check ? {:check => {entity => check}} : {:entity => entity} )
107
- validate_bulk_params(args) do
108
- validate :query => :end_time, :as => :time
109
- end
110
- perform_delete('/unscheduled_maintenances', args)
127
+ def delete_media(*ids)
128
+ raise "'delete_media' requires at least one media id parameter" if ids.nil? || ids.empty?
129
+ perform_delete('/media', ids)
111
130
  end
112
131
 
113
- def bulk_delete_unscheduled_maintenance!(options)
114
- validate_bulk_params(options) do
115
- validate :query => :end_time, :as => :time
132
+ # 2a: Pagerduty credentials
133
+ def create_contact_pagerduty_credentials(*args)
134
+ ids, params, data = unwrap_ids_and_params(*args)
135
+ raise "'create_contact_pagerduty_credentials' requires at least one contact id parameter" if ids.nil? || ids.empty?
136
+ data.each do |d|
137
+ validate_params(d) do
138
+ validate :query => [:service_key, :subdomain, :username, :password], :as => [:required, :string]
139
+ end
116
140
  end
117
- perform_delete('/unscheduled_maintenances', options)
141
+ perform_post("/contacts/#{escaped_ids(ids)}/pagerduty_credentials", nil, :pagerduty_credentials => data)
118
142
  end
119
143
 
120
- def scheduled_maintenances(entity, options = {})
121
- check = options.delete(:check)
122
-
123
- validate_params(options) do
124
- validate :query => [:start_time, :end_time], :as => :time
125
- end
126
-
127
- ec_path = entity_check_path(entity, check)
128
- perform_get("/scheduled_maintenances/#{ec_path}", options)
144
+ def pagerduty_credentials(*ids)
145
+ extract_get('pagerduty_credentials', perform_get('/pagerduty_credentials', ids))
129
146
  end
130
147
 
131
- def bulk_scheduled_maintenances(options = {})
132
- validate_bulk_params(options) do
133
- validate :query => [:start_time, :end_time], :as => :time
148
+ def update_pagerduty_credentials(*args)
149
+ ids, params, data = unwrap_ids_and_params(*args)
150
+ raise "'update_pagerduty_credentials' requires at least one pagerduty_credentials id parameter" if ids.nil? || ids.empty?
151
+ validate_params(params) do
152
+ validate :query => [:service_key, :subdomain, :username, :password], :as => :string
134
153
  end
135
-
136
- perform_get('/scheduled_maintenances', options)
137
- end
138
-
139
- def unscheduled_maintenances(entity, options = {})
140
- check = options.delete(:check)
141
-
142
- validate_params(options) do
143
- validate :query => [:start_time, :end_time], :as => :time
154
+ ops = params.inject([]) do |memo, (k,v)|
155
+ case k
156
+ when :service_key, :subdomain, :username, :password
157
+ memo << {:op => 'replace',
158
+ :path => "/pagerduty_credentials/0/#{k.to_s}",
159
+ :value => v}
160
+ end
161
+ memo
144
162
  end
145
-
146
- ec_path = entity_check_path(entity, check)
147
- perform_get("/unscheduled_maintenances/#{ec_path}", options)
163
+ raise "'update_pagerduty_credentials did not find any valid update fields" if ops.empty?
164
+ perform_patch("/pagerduty_credentials/#{escaped_ids(ids)}", nil, ops)
148
165
  end
149
166
 
150
- def bulk_unscheduled_maintenances(options = {})
151
- validate_bulk_params(options) do
152
- validate :query => [:start_time, :end_time], :as => :time
153
- end
154
-
155
- perform_get('/unscheduled_maintenances', options)
167
+ def delete_pagerduty_credentials(*ids)
168
+ raise "'delete_pagerduty_credentials' requires at least one pagerduty_credentials id parameter" if ids.nil? || ids.empty?
169
+ perform_delete('/pagerduty_credentials', ids)
156
170
  end
157
171
 
158
- def outages(entity, options = {})
159
- check = options.delete(:check)
160
172
 
161
- validate_params(options) do
162
- validate :query => [:start_time, :end_time], :as => :time
173
+ # 3: Notification Rules
174
+ def create_contact_notification_rules(*args)
175
+ ids, params, data = unwrap_ids_and_params(*args)
176
+ raise "'create_contact_notification_rules' requires at least one contact id parameter" if ids.nil? || ids.empty?
177
+ data.each do |d|
178
+ validate_params(d) do
179
+ validate :query => [:entities, :regex_entities, :tags, :regex_tags,
180
+ :unknown_media, :warning_media, :critical_media], :as => :array_of_strings
181
+ validate :query => [:unknown_blackhole, :warning_blackhole, :critical_blackhole],
182
+ :as => :boolean
183
+ end
163
184
  end
164
-
165
- ec_path = entity_check_path(entity, check)
166
- perform_get("/outages/#{ec_path}", options)
185
+ perform_post("/contacts/#{escaped_ids(ids)}/notification_rules", nil, :notification_rules => data)
167
186
  end
168
187
 
169
- def bulk_outages(options = {})
170
- validate_bulk_params(options) do
171
- validate :query => [:start_time, :end_time], :as => :time
172
- end
173
-
174
- perform_get('/outages', options)
188
+ def notification_rules(*ids)
189
+ extract_get('notification_rules', perform_get('/notification_rules', ids))
175
190
  end
176
191
 
177
- def downtime(entity, options = {})
178
- check = options.delete(:check)
179
-
180
- validate_params(options) do
181
- validate :query => [:start_time, :end_time], :as => :time
192
+ def update_notification_rules(*args)
193
+ ids, params, data = unwrap_ids_and_params(*args)
194
+ raise "'update_notification_rules' requires at least one notification rule id parameter" if ids.nil? || ids.empty?
195
+ validate_params(params) do
196
+ validate :query => [:entities, :regex_entities, :tags, :regex_tags,
197
+ :unknown_media, :warning_media, :critical_media], :as => :array_of_strings
198
+ validate :query => [:unknown_blackhole, :warning_blackhole, :critical_blackhole],
199
+ :as => :boolean
182
200
  end
183
-
184
- ec_path = entity_check_path(entity, check)
185
-
186
- perform_get("/downtime/#{ec_path}", options)
187
- end
188
-
189
- def bulk_downtime(options = {})
190
- validate_bulk_params(options) do
191
- validate :query => [:start_time, :end_time], :as => :time
201
+ ops = params.inject([]) do |memo, (k,v)|
202
+ case k
203
+ when :entities, :regex_entities, :tags, :regex_tags,
204
+ :time_restrictions, :unknown_media, :warning_media, :critical_media,
205
+ :unknown_blackhole, :warning_blackhole, :critical_blackhole
206
+
207
+ memo << {:op => 'replace',
208
+ :path => "/notification_rules/0/#{k.to_s}",
209
+ :value => v}
210
+ end
211
+ memo
192
212
  end
193
-
194
- perform_get('/downtime', options)
213
+ raise "'update_notification_rules' did not find any valid update fields" if ops.empty?
214
+ perform_patch("/notification_rules/#{escaped_ids(ids)}", nil, ops)
195
215
  end
196
216
 
197
- def entity_tags(entity)
198
- perform_get("/entities/#{escape(entity)}/tags")
217
+ def delete_notification_rules(*ids)
218
+ raise "'delete_notification_rules' requires at least one notification rule id parameter" if ids.nil? || ids.empty?
219
+ perform_delete('/notification_rules', ids)
199
220
  end
200
221
 
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
222
 
234
- def delete_contact_tags!(contact_id, *tags)
235
- perform_delete("/contacts/#{escape(contact_id)}/tags", :tag => tags)
223
+ # 4: Entities & 5: Checks
224
+ def create_entities(*args)
225
+ ids, params, data = unwrap_ids_and_params(*args)
226
+ data.each do |d|
227
+ validate_params(d) do
228
+ validate :query => :id, :as => [:required, :string]
229
+ validate :query => :name, :as => :string
230
+ validate :query => :tags, :as => :array_of_strings
231
+ end
232
+ end
233
+ perform_post('/entities', nil, :entities => data)
236
234
  end
237
235
 
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)
236
+ def entities(*ids)
237
+ extract_get('entities', perform_get('/entities', ids))
241
238
  end
242
239
 
243
- def notification_rules(contact_id)
244
- perform_get("/contacts/#{escape(contact_id)}/notification_rules")
240
+ def update_entities(*args)
241
+ ids, params, data = unwrap_ids_and_params(*args)
242
+ raise "'update_entities' requires at least one entity id parameter" if ids.nil? || ids.empty?
243
+ validate_params(params) do
244
+ validate :query => :name, :as => :string
245
+ validate :query => :tags, :as => :array_of_strings
246
+ end
247
+ ops = params.inject([]) do |memo, (k,v)|
248
+ case k
249
+ when :name, :tags
250
+ memo << {:op => 'replace',
251
+ :path => "/entities/0/#{k.to_s}",
252
+ :value => v}
253
+ when :add_contact
254
+ memo << {:op => 'add',
255
+ :path => '/entities/0/links/contacts/-',
256
+ :value => v}
257
+ when :remove_contact
258
+ memo << {:op => 'remove',
259
+ :path => "/entities/0/links/contacts/#{v}"}
260
+ end
261
+ memo
262
+ end
263
+ raise "'update_entities' did not find any valid update fields" if ops.empty?
264
+ perform_patch("/entities/#{escaped_ids(ids)}", nil, ops)
245
265
  end
246
266
 
247
- def notification_rule(rule_id)
248
- perform_get("/notification_rules/#{escape(rule_id)}")
249
- end
267
+ ['entities', 'checks'].each do |data_type|
250
268
 
251
- def create_notification_rule!(rule)
252
- perform_post('/notification_rules', rule)
253
- end
269
+ define_method("create_scheduled_maintenances_#{data_type}") do |*args|
270
+ ids, params, data = unwrap_ids_and_params(*args)
271
+ raise "'create_scheduled_maintenances_#{data_type}' requires at least one #{data_type} id parameter" if ids.nil? || ids.empty?
272
+ data.each do |d|
273
+ validate_params(d) do
274
+ validate :query => :start_time, :as => [:required, :time]
275
+ validate :query => :duration, :as => [:required, :integer]
276
+ validate :query => :summary, :as => :string
277
+ end
278
+ end
279
+ perform_post("/scheduled_maintenances/#{data_type}", ids,
280
+ :scheduled_maintenances => data)
281
+ end
254
282
 
255
- def update_notification_rule!(rule_id, rule)
256
- perform_put("/notification_rules/#{escape(rule_id)}", rule)
257
- end
283
+ define_method("create_unscheduled_maintenances_#{data_type}") do |*args|
284
+ ids, params, data = unwrap_ids_and_params(*args)
285
+ raise "'create_unscheduled_maintenances_#{data_type}' requires at least one #{data_type} id parameter" if ids.nil? || ids.empty?
286
+ data.each do |d|
287
+ validate_params(d) do
288
+ validate :query => :duration, :as => [:required, :integer]
289
+ validate :query => :summary, :as => :string
290
+ end
291
+ end
292
+ perform_post("/unscheduled_maintenances/#{data_type}", ids,
293
+ :unscheduled_maintenances => data)
294
+ end
258
295
 
259
- def delete_notification_rule!(rule_id)
260
- perform_delete("/notification_rules/#{escape(rule_id)}")
261
- end
296
+ define_method("create_test_notifications_#{data_type}") do |*args|
297
+ ids, params, data = unwrap_ids_and_params(*args)
298
+ raise "'create_test_notifications_#{data_type}' requires at least one #{data_type} id parameter" if ids.nil? || ids.empty?
299
+ data.each do |d|
300
+ validate_params(d) do
301
+ validate :query => :summary, :as => :string
302
+ end
303
+ end
304
+ perform_post("/test_notifications/#{data_type}", ids,
305
+ :test_notifications => data)
306
+ end
262
307
 
263
- def contact_media(contact_id)
264
- perform_get("/contacts/#{escape(contact_id)}/media")
265
- end
308
+ define_method("update_unscheduled_maintenances_#{data_type}") do |*args|
309
+ ids, params, data = unwrap_ids_and_params(*args)
310
+ raise "'update_unscheduled_maintenances_#{data_type}' requires at least one #{data_type} id parameter" if ids.nil? || ids.empty?
311
+ validate_params(params) do
312
+ validate :query => :end_time, :as => :time
313
+ end
314
+ ops = params.inject([]) do |memo, (k,v)|
315
+ case k
316
+ when :end_time
317
+ memo << {:op => 'replace',
318
+ :path => "/unscheduled_maintenances/0/#{k.to_s}",
319
+ :value => v}
320
+ end
321
+ memo
322
+ end
323
+ raise "'update_unscheduled_maintenances_#{data_type}' did not find any valid update fields" if ops.empty?
324
+ perform_patch("/unscheduled_maintenances/#{data_type}", ids, ops)
325
+ end
266
326
 
267
- def contact_medium(contact_id, media_type)
268
- perform_get("/contacts/#{escape(contact_id)}/media/#{escape(media_type)}")
269
- end
327
+ define_method("delete_scheduled_maintenances_#{data_type}") do |*args|
328
+ ids, params, data = unwrap_ids_and_params(*args)
329
+ raise "'delete_scheduled_maintenances_#{data_type}' requires at least one #{data_type} id parameter" if ids.nil? || ids.empty?
330
+ validate_params(params) do
331
+ validate :query => :start_time, :as => [:required, :time]
332
+ end
333
+ perform_delete("/scheduled_maintenances/#{data_type}", ids, params)
334
+ end
270
335
 
271
- def update_contact_medium!(contact_id, media_type, media)
272
- perform_put("/contacts/#{escape(contact_id)}/media/#{escape(media_type)}", media)
273
336
  end
274
337
 
275
- def delete_contact_medium!(contact_id, media_type)
276
- perform_delete("/contacts/#{escape(contact_id)}/media/#{escape(media_type)}")
277
- end
278
338
 
279
- def contact_timezone(contact_id)
280
- perform_get("/contacts/#{escape(contact_id)}/timezone")
281
- end
339
+ # 6: Reports
282
340
 
283
- def update_contact_timezone!(contact_id, timezone)
284
- perform_put("/contacts/#{escape(contact_id)}/timezone", :timezone => timezone)
285
- end
341
+ ['entities', 'checks'].each do |data_type|
342
+ define_method("status_report_#{data_type}") do |*ids|
343
+ extract_get('status_reports', perform_get("/status_report/#{data_type}", ids))
344
+ end
286
345
 
287
- def delete_contact_timezone!(contact_id)
288
- perform_delete("/contacts/#{escape(contact_id)}/timezone")
346
+ ['scheduled_maintenance', 'unscheduled_maintenance', 'downtime', 'outage'].each do |report_type|
347
+ define_method("#{report_type}_report_#{data_type}") do |*args|
348
+ ids, params, data = unwrap_ids_and_params(*args)
349
+ validate_params(params) do
350
+ validate :query => [:start_time, :end_time], :as => :time
351
+ end
352
+ extract_get("#{report_type}_reports", perform_get("/#{report_type}_report/#{data_type}", ids, params))
353
+ end
354
+ end
289
355
  end
290
356
 
291
357
  def last_error
@@ -294,46 +360,46 @@ module Flapjack
294
360
 
295
361
  private
296
362
 
297
- def perform_get(path, params = nil)
298
- req_uri = build_uri(path, params)
363
+ def extract_get(name, result)
364
+ (result.nil? || result.is_a?(TrueClass)) ? result : result[name]
365
+ end
366
+
367
+ def perform_get(path, ids = [], data = [])
368
+ req_uri = build_uri(path, ids, data)
299
369
  logger.info "GET #{req_uri}" if logger
300
370
  response = get(req_uri.request_uri)
301
371
  handle_response(response)
302
372
  end
303
373
 
304
- def perform_post(path, body = {})
305
- req_uri = build_uri(path)
374
+ def perform_post(path, ids = [], data = [])
375
+ req_uri = build_uri(path, ids)
306
376
  if logger
307
377
  log_post = "POST #{req_uri}"
308
- log_post << "\n Params: #{body.inspect}" if body
378
+ log_post << "\n Params: #{data.inspect}" if data
309
379
  logger.info log_post
310
380
  end
311
- opts = body ? {:body => prepare_nested_query(body).to_json, :headers => {'Content-Type' => 'application/json'}} : {}
381
+ opts = data ? {:body => prepare_nested_query(data).to_json, :headers => {'Content-Type' => 'application/vnd.api+json'}} : {}
312
382
  response = post(req_uri.request_uri, opts)
313
383
  handle_response(response)
314
384
  end
315
385
 
316
- def perform_put(path, body = {})
317
- req_uri = build_uri(path)
386
+ def perform_patch(path, ids = [], data = [])
387
+ req_uri = build_uri(path, ids)
318
388
  if logger
319
- log_put = "PUT #{req_uri}"
320
- log_put << "\n Params: #{body.inspect}" if body
321
- logger.info log_put
389
+ log_patch = "PATCH #{req_uri}"
390
+ log_patch << "\n Params: #{data.inspect}" if data
391
+ logger.info log_patch
322
392
  end
323
- opts = body ? {:body => prepare_nested_query(body).to_json, :headers => {'Content-Type' => 'application/json'}} : {}
324
- response = put(req_uri.request_uri, opts)
393
+ opts = data ? {:body => prepare_nested_query(data).to_json,
394
+ :headers => {'Content-Type' => 'application/json-patch+json'}} : {}
395
+ response = patch(req_uri.request_uri, opts)
325
396
  handle_response(response)
326
397
  end
327
398
 
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)
399
+ def perform_delete(path, ids = [], data = [])
400
+ req_uri = build_uri(path, ids, data)
401
+ logger.info "DELETE #{req_uri}" if logger
402
+ response = delete(req_uri.request_uri)
337
403
  handle_response(response)
338
404
  end
339
405
 
@@ -354,38 +420,10 @@ module Flapjack
354
420
  parsed_response
355
421
  end
356
422
 
357
- def validate_bulk_params(query = {}, &validation)
358
- errors = []
359
-
360
- entities = query[:entity]
361
- checks = query[:check]
362
-
363
- if entities && !entities.is_a?(String) &&
364
- (!entities.is_a?(Array) || !entities.all? {|e| e.is_a?(String)})
365
- raise ArgumentError.new("Entity argument must be a String, or an Array of Strings")
366
- end
367
-
368
- if checks && (!checks.is_a?(Hash) || !checks.all? {|k, v|
369
- k.is_a?(String) && (v.is_a?(String) || (v.is_a?(Array) && v.all?{|vv| vv.is_a?(String)}))
370
- })
371
- raise ArgumentError.new("Check argument must be a Hash with keys String, values either String or Array of Strings")
372
- end
373
-
374
- if entities.nil? && checks.nil?
375
- raise ArgumentError.new("Entity and/or check arguments must be provided")
376
- end
377
-
378
- validate_params(query, &validation)
379
- end
380
-
381
423
  def validate_params(query = {}, &validation)
382
424
  ArgumentValidator.new(query).instance_eval(&validation) if block_given?
383
425
  end
384
426
 
385
- def entity_check_path(entity, check)
386
- check.nil? ? "#{escape(entity)}" : "#{escape(entity)}/#{escape(check)}"
387
- end
388
-
389
427
  # copied from Rack::Utils -- builds the query string for GETs
390
428
  def build_nested_query(value, prefix = nil)
391
429
  if value.respond_to?(:iso8601)
@@ -410,10 +448,36 @@ module Flapjack
410
448
  end
411
449
  end
412
450
 
451
+ def escaped_ids(ids = [])
452
+ ids.collect{|id| CGI.escape(id.to_s)}.join(',')
453
+ end
454
+
413
455
  def escape(s)
414
456
  URI.encode_www_form_component(s)
415
457
  end
416
458
 
459
+ def unwrap_ids_and_params(*args)
460
+ ids = []
461
+ params = {}
462
+ data = []
463
+
464
+ args.each do |arg|
465
+ case arg
466
+ when Array
467
+ raise "Array arguments may only contain data Hashes" unless arg.all? {|a| a.is_a?(Hash)}
468
+ data += arg
469
+ when Hash
470
+ params.update(arg)
471
+ when String, Integer
472
+ ids << arg.to_s
473
+ else
474
+ raise "Arguments must be a Hash (parameters), String/Integer (ids), or Arrays of Hashes (data)"
475
+ end
476
+ end
477
+
478
+ [ids, params, data]
479
+ end
480
+
417
481
  # used for the JSON data hashes in POST, PUT, DELETE
418
482
  def prepare_nested_query(value)
419
483
  if value.respond_to?(:iso8601)
@@ -450,8 +514,11 @@ module Flapjack
450
514
  [protocol, host, port]
451
515
  end
452
516
 
453
- def build_uri(path, params = nil)
517
+ def build_uri(path, ids = [], params = [])
454
518
  pr, ho, po = protocol_host_port
519
+ if !ids.nil? && !ids.empty?
520
+ path += '/' + escaped_ids(ids)
521
+ end
455
522
  URI::HTTP.build(:protocol => pr, :host => ho, :port => po,
456
523
  :path => path, :query => (params.nil? || params.empty? ? nil : build_nested_query(params)))
457
524
  end