flapjack 0.8.4 → 0.8.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 00e9afa87734c98245ef9b370a726bd5e5e4a9d7
4
- data.tar.gz: 4852d02b41ec46f45bfe2fbb44cd18e4ad95a0a5
3
+ metadata.gz: 0c18eef4f4dd8297baab83f9f8c1ac1eb3972ace
4
+ data.tar.gz: b62c3482d22ddb71af95b7cb5c097fa9ea75bdf4
5
5
  SHA512:
6
- metadata.gz: d938e90fe44d654985e9bba3ec644f43a71bc172efb7e2cce39ae0dd38239fc164741ae4f4d2a57b0d88f85dd87f0ac4a699ea9fa30740ee6224b9186f738b6e
7
- data.tar.gz: f37d7d6a16fd859fb2011cbc48fa5e0f8c82ec994e9238844921f4a49be28b267923beabf98723e1e1a5ccfc824a98d4cb8c3736b3dbe68844ff93e8b15f51a3
6
+ metadata.gz: 0edb5492abaf9467c0960bcf9be3a5c941803354ce6f0e6bb5b2c03c6ac44a691544d173c00da9c37a4ab0e4973a6e5bc5c0bc02f44008701777597a7d5f8c24
7
+ data.tar.gz: 2befbc8f51f15d686e88a5dadc7b2eea9f73f32860a7b77f34fdd8af31e243b4a99262e3d1befcba50f3d2a28f70f5679e91f22178ffbacebba23405a19c50eb
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## Flapjack Changelog
2
2
 
3
+ # 0.8.5 - UNRELEASED
4
+ - Feature: jsonapi rework now supports PATCH, eg for media under contacts gh-253 (@ali-graham)
5
+ - Feature: experimental backbone based /edit_contacts page in the web UI has improvements gh-253 (@ali-graham)
6
+
3
7
  # 0.8.4 - 2014-01-31
4
8
  - Bug: The scheduled maintenance periods table on the checks page is messed up gh-427 (@jswoods)
5
9
  - Bug: api current check state shows check summary and details from last state change gh-431 (@ali-graham)
data/bin/flapper CHANGED
@@ -83,7 +83,7 @@ OptionParser.new do |opts|
83
83
  options.bind_ip = b
84
84
  end
85
85
 
86
- opts.on("-P", "--bind-port [PORT]", String, "PORT for flapper to bind to") do |p|
86
+ opts.on("-P", "--bind-port [PORT]", String, "PORT for flapper to bind to (default: 12345)") do |p|
87
87
  options.bind_port = p.to_i
88
88
  end
89
89
 
@@ -20,7 +20,7 @@ module Flapjack
20
20
 
21
21
  attr_accessor :id, :first_name, :last_name, :email, :media,
22
22
  :media_intervals, :media_rollup_thresholds, :pagerduty_credentials,
23
- :linked_entity_ids
23
+ :linked_entity_ids, :linked_media_ids
24
24
 
25
25
  TAG_PREFIX = 'contact_tag'
26
26
  ALL_MEDIA = ['email', 'sms', 'jabber', 'pagerduty']
@@ -155,6 +155,18 @@ module Flapjack
155
155
  *['subdomain', 'username', 'password'].collect {|f| [f, details[f]]})
156
156
  end
157
157
 
158
+ # returns false if this contact was already in the set for the entity
159
+ def add_entity(entity)
160
+ key = "contacts_for:#{entity.id}"
161
+ @redis.sadd(key, self.id)
162
+ end
163
+
164
+ # returns false if this contact wasn't in the set for the entity
165
+ def remove_entity(entity)
166
+ key = "contacts_for:#{entity.id}"
167
+ @redis.srem(key, self.id)
168
+ end
169
+
158
170
  # NB ideally contacts_for:* keys would scope the entity and check by an
159
171
  # input source, for namespacing purposes
160
172
  def entities(options = {})
@@ -204,7 +216,7 @@ module Flapjack
204
216
  next unless k =~ /^contacts_for:([a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])(?::(\w+))?$/
205
217
 
206
218
  entity_id = $1
207
- check = $2
219
+ check = $2
208
220
 
209
221
  entity_data << {:id => entity_id, :name => redis.hget("entity:#{entity_id}", 'name')}
210
222
 
@@ -466,8 +478,21 @@ module Flapjack
466
478
  "media_intervals" => self.media_intervals,
467
479
  "media_rollup_thresholds" => self.media_rollup_thresholds,
468
480
  "timezone" => self.timezone.name,
481
+ "tags" => self.tags.to_a
482
+ }.to_json
483
+ end
484
+
485
+ def to_jsonapi(*args)
486
+ { "id" => self.id,
487
+ "first_name" => self.first_name,
488
+ "last_name" => self.last_name,
489
+ "email" => self.email,
490
+ "timezone" => self.timezone.name,
469
491
  "tags" => self.tags.to_a,
470
- "links" => {:entities => @linked_entity_ids || []}
492
+ "links" => {
493
+ :entities => @linked_entity_ids || [],
494
+ :media => @linked_media_ids || []
495
+ }
471
496
  }.to_json
472
497
  end
473
498
 
@@ -32,6 +32,8 @@ module Flapjack
32
32
  raise "Redis connection not set" unless redis = options[:redis]
33
33
  raise "Entity name not provided" unless entity['name'] && !entity['name'].empty?
34
34
 
35
+ #FIXME: should probably raise an exception if trying to create a new entity with the
36
+ # same name or id as an existing entity. (Go away and use update instead.)
35
37
  if entity['id']
36
38
  existing_name = redis.hget("entity:#{entity['id']}", 'name')
37
39
  redis.del("entity_id:#{existing_name}") unless existing_name == entity['name']
@@ -38,7 +38,11 @@ module Flapjack
38
38
  def self.add(rule_data, options = {})
39
39
  raise "Redis connection not set" unless redis = options[:redis]
40
40
 
41
- rule_id = SecureRandom.uuid
41
+ if rule_data[:id] && self.find_by_id(rule_data[:id], :redis => redis)
42
+ errors = ["a notification rule already exists with id '#{rule_data[:id]}'"]
43
+ return errors
44
+ end
45
+ rule_id = rule_data[:id] || SecureRandom.uuid
42
46
  errors = self.add_or_update(rule_data.merge(:id => rule_id), options)
43
47
  return errors unless errors.nil? || errors.empty?
44
48
  self.find_by_id(rule_id, :redis => redis)
@@ -27,7 +27,7 @@ module Flapjack
27
27
 
28
28
  include Flapjack::Utility
29
29
 
30
- JSON_REQUEST_MIME_TYPES = ['application/vnd.api+json', 'application/json']
30
+ JSON_REQUEST_MIME_TYPES = ['application/vnd.api+json', 'application/json', 'application/json-patch+json']
31
31
 
32
32
  class ContactNotFound < RuntimeError
33
33
  attr_reader :contact_id
@@ -36,6 +36,13 @@ module Flapjack
36
36
  end
37
37
  end
38
38
 
39
+ class ContactsNotFound < RuntimeError
40
+ attr_reader :contact_ids
41
+ def initialize(contact_ids)
42
+ @contact_ids = contact_ids
43
+ end
44
+ end
45
+
39
46
  class NotificationRuleNotFound < RuntimeError
40
47
  attr_reader :rule_id
41
48
  def initialize(rule_id)
@@ -106,6 +113,8 @@ module Flapjack
106
113
  case e
107
114
  when Flapjack::Gateways::JSONAPI::ContactNotFound
108
115
  rescue_error.call(404, e, request_info, "could not find contact '#{e.contact_id}'")
116
+ when Flapjack::Gateways::JSONAPI::ContactsNotFound
117
+ rescue_error.call(404, e, request_info, "could not find contacts '" + e.contact_ids.join(', ') + "'")
109
118
  when Flapjack::Gateways::JSONAPI::NotificationRuleNotFound
110
119
  rescue_error.call(404, e, request_info,"could not find notification rule '#{e.rule_id}'")
111
120
  when Flapjack::Gateways::JSONAPI::EntityNotFound
@@ -187,13 +196,21 @@ module Flapjack
187
196
  request.query_string.length > 0) ? "?#{request.query_string}" : ""
188
197
  if logger.debug?
189
198
  logger.debug("Returning #{response.status} for #{request.request_method} " +
190
- "#{request.path_info}#{query_string}, body: #{response.body.join(', ')}")
199
+ "#{request.path_info}#{query_string}, body: #{response.body}")
191
200
  elsif logger.info?
192
201
  logger.info("Returning #{response.status} for #{request.request_method} " +
193
202
  "#{request.path_info}#{query_string}")
194
203
  end
195
204
  end
196
205
 
206
+ def is_json_request?
207
+ Flapjack::Gateways::JSONAPI::JSON_REQUEST_MIME_TYPES.include?(request.content_type.split(/\s*[;,]\s*/, 2).first.downcase)
208
+ end
209
+
210
+ def is_jsonpatch_request?
211
+ 'application/json-patch+json'.eql?(request.content_type.split(/\s*[;,]\s*/, 2).first.downcase)
212
+ end
213
+
197
214
  register Flapjack::Gateways::JSONAPI::EntityMethods
198
215
 
199
216
  register Flapjack::Gateways::JSONAPI::ContactMethods
@@ -49,6 +49,40 @@ module Flapjack
49
49
  raise Flapjack::Gateways::JSONAPI::ResourceLocked.new(resource) unless semaphore
50
50
  semaphore
51
51
  end
52
+
53
+ def apply_json_patch(object_path, &block)
54
+ ops = params[:ops]
55
+
56
+ if ops.nil? || !ops.is_a?(Array)
57
+ halt err(400, "Invalid JSON-Patch request")
58
+ end
59
+
60
+ ops.each do |operation|
61
+ linked = nil
62
+ property = nil
63
+
64
+ op = operation['op']
65
+ operation['path'] =~ /\A\/#{object_path}\/0\/([^\/]+)(?:\/([^\/]+)(?:\/([^\/]+))?)?\z/
66
+ if 'links'.eql?($1)
67
+ linked = $2
68
+
69
+ value = case op
70
+ when 'add'
71
+ operation['value']
72
+ when 'remove'
73
+ $3
74
+ end
75
+ elsif 'replace'.eql?(op)
76
+ property = $1
77
+ value = $3
78
+ else
79
+ next
80
+ end
81
+
82
+ yield(op, property, linked, value)
83
+ end
84
+ end
85
+
52
86
  end
53
87
 
54
88
  def self.registered(app)
@@ -56,7 +90,7 @@ module Flapjack
56
90
  app.helpers Flapjack::Gateways::JSONAPI::ContactMethods::Helpers
57
91
 
58
92
  app.post '/contacts' do
59
- pass unless Flapjack::Gateways::JSONAPI::JSON_REQUEST_MIME_TYPES.include?(request.content_type)
93
+ pass unless is_json_request?
60
94
  content_type :json
61
95
  cors_headers
62
96
 
@@ -96,96 +130,84 @@ module Flapjack
96
130
  contacts_data.map {|cd| cd['id']}.to_json
97
131
  end
98
132
 
99
- app.post '/contacts_atomic' do
100
- pass unless Flapjack::Gateways::JSONAPI::JSON_REQUEST_MIME_TYPES.include?(request.content_type)
101
- content_type :json
102
-
103
- contacts_data = params[:contacts]
104
- if contacts_data.nil? || !contacts_data.is_a?(Enumerable)
105
- halt err(422, "No valid contacts were submitted")
106
- end
107
-
108
- # stringifying as integer string params are automatically integered,
109
- # but our redis ids are strings
110
- contacts_data_ids = contacts_data.reject {|c| c['id'].nil? }.
111
- map {|co| co['id'].to_s }
112
-
113
- if contacts_data_ids.empty?
114
- halt err(422, "No contacts with IDs were submitted")
115
- end
116
-
117
- semaphore = obtain_semaphore(SEMAPHORE_CONTACT_MASS_UPDATE)
118
-
119
- contacts = Flapjack::Data::Contact.all(:redis => redis)
120
- contacts_h = hashify(*contacts) {|c| [c.id, c] }
121
- contacts_ids = contacts_h.keys
122
133
 
123
- # delete contacts not found in the bulk list
124
- (contacts_ids - contacts_data_ids).each do |contact_to_delete_id|
125
- contact_to_delete = contacts.detect {|c| c.id == contact_to_delete_id }
126
- contact_to_delete.delete!
127
- end
128
-
129
- # add or update contacts found in the bulk list
130
- contacts_data.reject {|cd| cd['id'].nil? }.each do |contact_data|
131
- if contacts_ids.include?(contact_data['id'].to_s)
132
- contacts_h[contact_data['id'].to_s].update(contact_data)
133
- else
134
- Flapjack::Data::Contact.add(contact_data, :redis => redis)
135
- end
136
- end
137
-
138
- semaphore.release
139
- 204
140
- end
141
-
142
- # Returns all the contacts
134
+ # Returns all (/contacts) or some (/contacts/1,2,3) or one (/contact/2) contact(s)
143
135
  # https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts
144
- app.get '/contacts' do
145
- content_type :json
136
+ app.get %r{/contacts(?:/)?([^/]+)?$} do
137
+ content_type 'application/vnd.api+json'
146
138
  cors_headers
147
139
 
148
- contacts = if params[:ids]
149
- Flapjack::Data::Contact.find_by_ids(params[:ids].split(',').uniq, :redis => redis)
140
+ requested_contacts = if params[:captures] && params[:captures][0]
141
+ params[:captures][0].split(',').uniq
142
+ else
143
+ nil
144
+ end
145
+
146
+ #FIXME: do we need to url decode the ids? has rack or some middleware already done this?
147
+ contacts = if requested_contacts
148
+ Flapjack::Data::Contact.find_by_ids(requested_contacts, :logger => logger, :redis => redis)
150
149
  else
151
150
  Flapjack::Data::Contact.all(:redis => redis)
152
151
  end
153
152
  contacts.compact!
154
153
 
154
+ if requested_contacts && contacts.empty?
155
+ raise Flapjack::Gateways::JSONAPI::ContactsNotFound.new(requested_contacts)
156
+ end
157
+
155
158
  linked_entity_data, linked_entity_ids = if contacts.empty?
156
159
  [[], []]
157
160
  else
158
161
  Flapjack::Data::Contact.entities_jsonapi(contacts.map(&:id), :redis => redis)
159
162
  end
160
163
 
164
+ linked_media_data = []
165
+ linked_media_ids = {}
166
+ contacts.each do |contact|
167
+ contact.media.keys.each do |medium|
168
+ id = "#{contact.id}_#{medium}"
169
+ interval = contact.media_intervals[medium].nil? ? nil : contact.media_intervals[medium].to_i
170
+ rollup_threshold = contact.media_rollup_thresholds[medium].nil? ? nil : contact.media_rollup_thresholds[medium].to_i
171
+ linked_media_ids[contact.id] = id
172
+ linked_media_data <<
173
+ { "id" => id,
174
+ "type" => medium,
175
+ "address" => contact.media[medium],
176
+ "interval" => interval,
177
+ "rollup_threshold" => rollup_threshold,
178
+ "contact_id" => contact.id }
179
+ end
180
+ end
181
+
161
182
  contacts_json = contacts.collect {|contact|
162
183
  contact.linked_entity_ids = linked_entity_ids[contact.id]
163
- contact.to_json
184
+ contact.linked_media_ids = linked_media_ids[contact.id]
185
+ contact.to_jsonapi
164
186
  }.join(", ")
165
187
 
166
188
  '{"contacts":[' + contacts_json + ']' +
167
- ( linked_entity_data.empty? ? '}' :
168
- ', "linked": {"entities":' + linked_entity_data.to_json + '}}')
189
+ ',"linked":{"entities":' + linked_entity_data.to_json +
190
+ ',"media":' + linked_media_data.to_json + '}}'
169
191
  end
170
192
 
171
193
  # Returns the core information about the specified contact
172
194
  # https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id
173
195
  app.get '/contacts/:contact_id' do
174
- content_type :json
196
+ content_type 'application/vnd.api+json'
175
197
  cors_headers
176
198
  contact = find_contact(params[:contact_id])
177
199
 
178
200
  entities = contact.entities.map {|e| e[:entity] }
179
201
 
180
- '{"contacts":[' + contact.to_json + ']' +
202
+ '{"contacts":[' + contact.to_jsonapi + ']' +
181
203
  ( entities.empty? ? '}' :
182
204
  ', "linked": {"entities":' + entities.values.to_json + '}}')
183
205
  end
184
206
 
185
207
  # Updates a contact
186
208
  app.put '/contacts/:contact_id' do
187
- content_type :json
188
209
  cors_headers
210
+ content_type :json
189
211
 
190
212
  contacts_data = params[:contacts]
191
213
 
@@ -208,7 +230,43 @@ module Flapjack
208
230
  logger.debug("contact_data: #{contact_data}")
209
231
  contact.update(contact_data)
210
232
 
211
- contact.to_json
233
+ contact.to_jsonapi
234
+ end
235
+
236
+ # TODO this should build up all data, verify entities exist, etc.
237
+ # before applying any changes
238
+ # TODO generalise JSON-Patch data parsing code
239
+ app.patch '/contacts/:contact_id' do
240
+ pass unless is_jsonpatch_request?
241
+ content_type :json
242
+ cors_headers
243
+
244
+ contact = find_contact(params[:contact_id])
245
+
246
+ apply_json_patch('contacts') do |op, property, linked, value|
247
+ case op
248
+ when 'replace'
249
+ if ['first_name', 'last_name', 'email'].include?(property)
250
+ contact.update(property => value)
251
+ end
252
+ when 'add'
253
+ logger.debug "patch add operation. linked: #{linked}"
254
+ if 'entities'.eql?(linked)
255
+ entity = Flapjack::Data::Entity.find_by_id(value, :redis => redis)
256
+ logger.debug "adding this entity: #{entity}"
257
+ contact.add_entity(entity) unless entity.nil?
258
+ end
259
+ when 'remove'
260
+ if 'entities'.eql?(linked)
261
+ entity = Flapjack::Data::Entity.find_by_id(value, :redis => redis)
262
+ contact.remove_entity(entity) unless entity.nil?
263
+ end
264
+ end
265
+ end
266
+
267
+ # will need to be 200 and return contact.to_jsonapi
268
+ # if updated_at changes, or Etag, when those are introduced
269
+ status 204
212
270
  end
213
271
 
214
272
  # Deletes a contact
@@ -221,17 +279,85 @@ module Flapjack
221
279
  status 204
222
280
  end
223
281
 
224
- # Lists this contact's notification rules
225
- # https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_notification_rules
226
- app.get '/contacts/:contact_id/notification_rules' do
282
+ app.post '/media' do
283
+ pass unless is_json_request?
227
284
  content_type :json
228
285
  cors_headers
229
286
 
230
- "[" + find_contact(params[:contact_id]).notification_rules.map {|r| r.to_json }.join(',') + "]"
287
+ media_data = params[:media]
288
+
289
+ if media_data.nil? || !media_data.is_a?(Enumerable)
290
+ halt err(422, "No valid media were submitted")
291
+ end
292
+
293
+ unless media_data.all? {|m| m['id'].nil? }
294
+ halt err(422, "Media creation cannot include IDs")
295
+ end
296
+
297
+ semaphore = obtain_semaphore(SEMAPHORE_CONTACT_MASS_UPDATE)
298
+
299
+ contacts = media_data.inject({}) {|memo, medium_data|
300
+ contact_id = medium_data['contact_id']
301
+ if contact_id.nil?
302
+ semaphore.release
303
+ halt err(422, "Media data must include 'contact_id'")
304
+ end
305
+ next memo if memo.has_key?(contact_id)
306
+ contact = Flapjack::Data::Contact.find_by_id(contact_id, :redis => redis)
307
+ if contact.nil?
308
+ semaphore.release
309
+ halt err(422, "Contact id:'#{contact_id}' could not be loaded")
310
+ end
311
+ memo[contact_id] = contact
312
+ memo
313
+ }
314
+
315
+ media_data.each do |medium_data|
316
+ contact = contacts[medium_data['contact_id']]
317
+ type = medium_data['type']
318
+ contact.set_address_for_media(type, medium_data['address'])
319
+ contact.set_interval_for_media(type, medium_data['interval'])
320
+ contact.set_rollup_threshold_for_media(type, medium_data['rollup_threshold'])
321
+ medium_data['id'] = "#{contact.id}_#{type}"
322
+ end
323
+
324
+ semaphore.release
325
+
326
+ '{"media":' + media_data.to_json + '}'
327
+ end
328
+
329
+ app.patch '/media/:media_id' do
330
+ pass unless is_jsonpatch_request?
331
+ content_type :json
332
+ cors_headers
333
+
334
+ media_id = params[:media_id]
335
+ media_id =~ /\A(.+)_(email|sms|jabber)\z/
336
+
337
+ contact_id = $1
338
+ type = $2
339
+
340
+ halt err(422, "Could not get contact_id from media_id") if contact_id.nil?
341
+ halt err(422, "Could not get type from media_id") if type.nil?
342
+
343
+ contact = find_contact(contact_id)
344
+
345
+ apply_json_patch('media') do |op, property, linked, value|
346
+ if 'replace'.eql?(op)
347
+ case property
348
+ when 'address'
349
+ contact.set_address_for_media(type, value)
350
+ when 'interval'
351
+ contact.set_interval_for_media(type, value)
352
+ when 'rollup_threshold'
353
+ contact.set_rollup_threshold_for_media(type, value)
354
+ end
355
+ end
356
+ end
357
+
358
+ status 204
231
359
  end
232
360
 
233
- # Get the specified notification rule for this user
234
- # https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_notification_rules_id
235
361
  app.get '/notification_rules/:id' do
236
362
  content_type :json
237
363
  cors_headers
@@ -242,7 +368,6 @@ module Flapjack
242
368
  end
243
369
 
244
370
  # Creates a notification rule or rules for a contact
245
- # https://github.com/flpjck/flapjack/wiki/API#wiki-post_contacts_id_notification_rules
246
371
  app.post '/notification_rules' do
247
372
  content_type :json
248
373
  cors_headers
@@ -253,10 +378,6 @@ module Flapjack
253
378
  halt err(422, "No valid notification rules were submitted")
254
379
  end
255
380
 
256
- if rules_data.any? {|rule| rule['id']}
257
- halt err(422, "ID fields may not be generated by you. Remove IDs and POST again")
258
- end
259
-
260
381
  errors = []
261
382
  rules_data.each do |rule_data|
262
383
  errors << Flapjack::Data::NotificationRule.prevalidate_data(symbolize(rule_data), {:logger => logger})
@@ -298,7 +419,6 @@ module Flapjack
298
419
  end
299
420
 
300
421
  # Updates a notification rule
301
- # https://github.com/flpjck/flapjack/wiki/API#wiki-put_notification_rules_id
302
422
  app.put('/notification_rules/:id') do
303
423
  content_type :json
304
424
  cors_headers
@@ -338,7 +458,6 @@ module Flapjack
338
458
  end
339
459
 
340
460
  # Deletes a notification rule
341
- # https://github.com/flpjck/flapjack/wiki/API#wiki-put_notification_rules_id
342
461
  app.delete('/notification_rules/:id') do
343
462
  cors_headers
344
463
  rule = find_rule(params[:id])
@@ -348,191 +467,6 @@ module Flapjack
348
467
  status 204
349
468
  end
350
469
 
351
- # Returns the media of a contact
352
- # https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_media
353
- app.get '/contacts/:contact_id/media' do
354
- content_type :json
355
- cors_headers
356
-
357
- contact = find_contact(params[:contact_id])
358
-
359
- media = contact.media
360
- media_intervals = contact.media_intervals
361
- media_rollup_thresholds = contact.media_rollup_thresholds
362
- media_addr_int = hashify(*media.keys) {|k|
363
- [k, {'address' => media[k],
364
- 'interval' => media_intervals[k],
365
- 'rollup_threshold' => media_rollup_thresholds[k] }]
366
- }
367
- media_addr_int.to_json
368
- end
369
-
370
- # Returns the specified media of a contact
371
- # https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_media_media
372
- app.get('/contacts/:contact_id/media/:id') do
373
- content_type :json
374
- cors_headers
375
-
376
- contact = find_contact(params[:contact_id])
377
- media = contact.media[params[:id]]
378
- if media.nil?
379
- halt err(404, "no #{params[:id]} for contact '#{params[:contact_id]}'")
380
- end
381
- interval = contact.media_intervals[params[:id]]
382
- # FIXME: does erroring when no interval found make sense?
383
- if interval.nil?
384
- halt err(403, "no #{params[:id]} interval for contact '#{params[:contact_id]}'")
385
- end
386
- rollup_threshold = contact.media_rollup_thresholds[params[:id]]
387
- {'address' => media,
388
- 'interval' => interval,
389
- 'rollup_threshold' => rollup_threshold }.to_json
390
- end
391
-
392
- # Creates or updates a media of a contact
393
- # https://github.com/flpjck/flapjack/wiki/API#wiki-put_contacts_id_media_media
394
- app.put('/contacts/:contact_id/media/:id') do
395
- content_type :json
396
- cors_headers
397
-
398
- contact = find_contact(params[:contact_id])
399
- errors = []
400
-
401
- if 'pagerduty'.eql?(params[:id])
402
- errors = [:service_key, :subdomain, :username, :password].inject([]) do |memo, pdp|
403
- memo << "no #{pdp.to_s} for 'pagerduty' media" if params[pdp].nil?
404
- memo
405
- end
406
-
407
- halt err(422, *errors) unless errors.empty?
408
-
409
- contact.set_pagerduty_credentials('service_key' => params[:service_key],
410
- 'subdomain' => params[:subdomain],
411
- 'username' => params[:username],
412
- 'password' => params[:password])
413
-
414
- contact.pagerduty_credentials.to_json
415
- else
416
- if params[:address].nil?
417
- errors << "no address for '#{params[:id]}' media"
418
- end
419
-
420
- halt err(422, *errors) unless errors.empty?
421
-
422
- contact.set_address_for_media(params[:id], params[:address])
423
- contact.set_interval_for_media(params[:id], params[:interval])
424
- contact.set_rollup_threshold_for_media(params[:id], params[:rollup_threshold])
425
-
426
- {'address' => contact.media[params[:id]],
427
- 'interval' => contact.media_intervals[params[:id]],
428
- 'rollup_threshold' => contact.media_rollup_thresholds[params[:id]]}.to_json
429
- end
430
- end
431
-
432
- # delete a media of a contact
433
- app.delete('/contacts/:contact_id/media/:id') do
434
- cors_headers
435
- contact = find_contact(params[:contact_id])
436
- contact.remove_media(params[:id])
437
- status 204
438
- end
439
-
440
- # Returns the timezone of a contact
441
- # https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_timezone
442
- app.get('/contacts/:contact_id/timezone') do
443
- content_type :json
444
- cors_headers
445
-
446
- contact = find_contact(params[:contact_id])
447
- contact.timezone.name.to_json
448
- end
449
-
450
- # Sets the timezone of a contact
451
- # https://github.com/flpjck/flapjack/wiki/API#wiki-put_contacts_id_timezone
452
- app.put('/contacts/:contact_id/timezone') do
453
- content_type :json
454
- cors_headers
455
-
456
- contact = find_contact(params[:contact_id])
457
- contact.timezone = params[:timezone]
458
- contact.timezone.name.to_json
459
- end
460
-
461
- # Removes the timezone of a contact
462
- # https://github.com/flpjck/flapjack/wiki/API#wiki-put_contacts_id_timezone
463
- app.delete('/contacts/:contact_id/timezone') do
464
- cors_headers
465
- contact = find_contact(params[:contact_id])
466
- contact.timezone = nil
467
- status 204
468
- end
469
-
470
- app.post '/contacts/:contact_id/tags' do
471
- content_type :json
472
- cors_headers
473
-
474
- tags = find_tags(params[:tags])
475
- contact = find_contact(params[:contact_id])
476
- contact.add_tags(*tags)
477
- '{"tags":' +
478
- contact.tags.to_json +
479
- '}'
480
- end
481
-
482
- app.post '/contacts/:contact_id/entity_tags' do
483
- content_type :json
484
- cors_headers
485
- contact = find_contact(params[:contact_id])
486
- contact.entities.map {|e| e[:entity]}.each do |entity|
487
- next unless tags = params[:entity][entity.name]
488
- entity.add_tags(*tags)
489
- end
490
- contact_ent_tag = hashify(*contact.entities(:tags => true)) {|et|
491
- [et[:entity].name, et[:tags]]
492
- }
493
- contact_ent_tag.to_json
494
- end
495
-
496
- app.delete '/contacts/:contact_id/tags' do
497
- cors_headers
498
- tags = find_tags(params[:tags])
499
- contact = find_contact(params[:contact_id])
500
- contact.delete_tags(*tags)
501
- status 204
502
- end
503
-
504
- app.delete '/contacts/:contact_id/entity_tags' do
505
- cors_headers
506
- contact = find_contact(params[:contact_id])
507
- contact.entities.map {|e| e[:entity]}.each do |entity|
508
- next unless tags = params[:entity][entity.name]
509
- entity.delete_tags(*tags)
510
- end
511
- status 204
512
- end
513
-
514
- app.get '/contacts/:contact_id/tags' do
515
- content_type :json
516
- cors_headers
517
-
518
- contact = find_contact(params[:contact_id])
519
- '{"tags":' +
520
- contact.tags.to_json +
521
- '}'
522
-
523
- end
524
-
525
- app.get '/contacts/:contact_id/entity_tags' do
526
- content_type :json
527
- cors_headers
528
-
529
- contact = find_contact(params[:contact_id])
530
- contact_ent_tag = hashify(*contact.entities(:tags => true)) {|et|
531
- [et[:entity].name, et[:tags]]
532
- }
533
- contact_ent_tag.to_json
534
- end
535
-
536
470
  end
537
471
 
538
472
  end