flapjack 0.8.4 → 0.8.5

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