flapjack-diner 0.15 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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