flapjack 0.8.10 → 0.8.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/Gemfile +1 -1
  4. data/bin/flapjack +10 -1
  5. data/bin/flapjack-nagios-receiver +1 -2
  6. data/bin/simulate-failed-check +12 -4
  7. data/etc/flapjack_config.yaml.example +2 -1
  8. data/flapjack.gemspec +1 -0
  9. data/lib/flapjack/data/contact.rb +46 -26
  10. data/lib/flapjack/data/entity.rb +28 -0
  11. data/lib/flapjack/data/entity_check.rb +52 -11
  12. data/lib/flapjack/data/event.rb +9 -3
  13. data/lib/flapjack/data/notification_rule.rb +8 -0
  14. data/lib/flapjack/gateways/api.rb +0 -1
  15. data/lib/flapjack/gateways/api/entity_check_presenter.rb +2 -1
  16. data/lib/flapjack/gateways/email.rb +1 -2
  17. data/lib/flapjack/gateways/jabber.rb +3 -3
  18. data/lib/flapjack/gateways/jsonapi.rb +186 -38
  19. data/lib/flapjack/gateways/jsonapi/check_methods.rb +120 -0
  20. data/lib/flapjack/gateways/jsonapi/{entity_check_presenter.rb → check_presenter.rb} +7 -6
  21. data/lib/flapjack/gateways/jsonapi/contact_methods.rb +61 -352
  22. data/lib/flapjack/gateways/jsonapi/entity_methods.rb +117 -248
  23. data/lib/flapjack/gateways/jsonapi/medium_methods.rb +179 -0
  24. data/lib/flapjack/gateways/jsonapi/notification_rule_methods.rb +124 -0
  25. data/lib/flapjack/gateways/jsonapi/pagerduty_credential_methods.rb +128 -0
  26. data/lib/flapjack/gateways/jsonapi/rack/json_params_parser.rb +4 -5
  27. data/lib/flapjack/gateways/jsonapi/report_methods.rb +143 -0
  28. data/lib/flapjack/gateways/web.rb +1 -0
  29. data/lib/flapjack/gateways/web/public/js/backbone.jsonapi.js +165 -101
  30. data/lib/flapjack/gateways/web/public/js/contacts.js +34 -46
  31. data/lib/flapjack/gateways/web/public/js/select2.js +232 -90
  32. data/lib/flapjack/gateways/web/public/js/select2.min.js +4 -4
  33. data/lib/flapjack/gateways/web/views/check.html.erb +11 -2
  34. data/lib/flapjack/processor.rb +6 -6
  35. data/lib/flapjack/version.rb +1 -1
  36. data/spec/lib/flapjack/data/entity_check_spec.rb +1 -1
  37. data/spec/lib/flapjack/data/event_spec.rb +10 -9
  38. data/spec/lib/flapjack/gateways/api/entity_methods_spec.rb +25 -25
  39. data/spec/lib/flapjack/gateways/api_spec.rb +23 -1
  40. data/spec/lib/flapjack/gateways/email_spec.rb +40 -2
  41. data/spec/lib/flapjack/gateways/jabber_spec.rb +1 -1
  42. data/spec/lib/flapjack/gateways/jsonapi/check_methods_spec.rb +134 -0
  43. data/spec/lib/flapjack/gateways/jsonapi/{entity_check_presenter_spec.rb → check_presenter_spec.rb} +17 -17
  44. data/spec/lib/flapjack/gateways/jsonapi/contact_methods_spec.rb +27 -232
  45. data/spec/lib/flapjack/gateways/jsonapi/entity_methods_spec.rb +217 -687
  46. data/spec/lib/flapjack/gateways/jsonapi/medium_methods_spec.rb +232 -0
  47. data/spec/lib/flapjack/gateways/jsonapi/notification_rule_methods_spec.rb +131 -0
  48. data/spec/lib/flapjack/gateways/jsonapi/pagerduty_credential_methods_spec.rb +113 -0
  49. data/spec/lib/flapjack/gateways/jsonapi/report_methods_spec.rb +546 -0
  50. data/spec/lib/flapjack/gateways/jsonapi_spec.rb +10 -1
  51. data/spec/lib/flapjack/gateways/web_spec.rb +1 -0
  52. data/spec/support/jsonapi_helper.rb +62 -0
  53. metadata +36 -8
  54. data/lib/flapjack/gateways/jsonapi/entity_presenter.rb +0 -75
  55. data/spec/lib/flapjack/gateways/jsonapi/entity_presenter_spec.rb +0 -108
@@ -5,9 +5,6 @@ require 'sinatra/base'
5
5
  require 'flapjack/data/entity'
6
6
  require 'flapjack/data/entity_check'
7
7
 
8
- require 'flapjack/gateways/jsonapi/entity_presenter'
9
- require 'flapjack/gateways/jsonapi/entity_check_presenter'
10
-
11
8
  module Flapjack
12
9
 
13
10
  module Gateways
@@ -18,297 +15,169 @@ module Flapjack
18
15
 
19
16
  module Helpers
20
17
 
21
- def find_entity(entity_name)
22
- entity = Flapjack::Data::Entity.find_by_name(entity_name, :redis => redis)
23
- raise Flapjack::Gateways::JSONAPI::EntityNotFound.new(entity_name) if entity.nil?
24
- entity
25
- end
26
-
27
- def find_entity_check(entity, check)
28
- entity_check = Flapjack::Data::EntityCheck.for_entity(entity,
29
- check, :redis => redis)
30
- raise Flapjack::Gateways::JSONAPI::EntityCheckNotFound.new(entity, check) if entity_check.nil?
31
- entity_check
32
- end
33
-
34
- def find_tags(tags)
35
- halt err(403, "no tags") if tags.nil? || tags.empty?
36
- tags
37
- end
38
-
39
- def entities_and_checks(entity_name, check)
40
- if entity_name
41
- # backwards-compatible, single entity or entity&check from route
42
- entities = check ? nil : [entity_name]
43
- checks = check ? {entity_name => check} : nil
44
- else
45
- # new and improved bulk API queries
46
- entities = params[:entity]
47
- checks = params[:check]
48
- entities = [entities] unless entities.nil? || entities.is_a?(Array)
49
- # TODO err if checks isn't a Hash (similar rules as in flapjack-diner)
50
- end
51
- [entities, checks]
52
- end
53
-
54
- def bulk_api_check_action(entities, entity_checks, action, params = {})
55
- unless entities.nil? || entities.empty?
56
- entities.each do |entity_name|
57
- entity = find_entity(entity_name)
58
- checks = entity.check_list.sort
59
- checks.each do |check|
60
- action.call( find_entity_check(entity, check) )
61
- end
62
- end
63
- end
64
-
65
- unless entity_checks.nil? || entity_checks.empty?
66
- entity_checks.each_pair do |entity_name, checks|
67
- entity = find_entity(entity_name)
68
- checks = [checks] unless checks.is_a?(Array)
69
- checks.each do |check|
70
- action.call( find_entity_check(entity, check) )
71
- end
18
+ def checks_for_entity_ids(entity_ids)
19
+ return if entity_ids.nil?
20
+ entity_ids.inject([]) do |memo, entity_id|
21
+ entity = find_entity_by_id(entity_id)
22
+ check_names = entity.check_list.sort
23
+ check_names.each do |check_name|
24
+ memo << find_entity_check(entity, check_name)
72
25
  end
26
+ memo
73
27
  end
74
28
  end
29
+ end
75
30
 
76
- def present_api_results(entities, entity_checks, result_type, &block)
77
- result = []
31
+ def self.registered(app)
32
+ app.helpers Flapjack::Gateways::JSONAPI::Helpers
33
+ app.helpers Flapjack::Gateways::JSONAPI::EntityMethods::Helpers
78
34
 
79
- unless entities.nil? || entities.empty?
80
- result += entities.collect {|entity_name|
81
- entity = find_entity(entity_name)
82
- yield(Flapjack::Gateways::JSONAPI::EntityPresenter.new(entity, :redis => redis))
83
- }.flatten(1)
35
+ # Returns all (/entities) or some (/entities/A,B,C) or one (/entities/A) contact(s)
36
+ # NB: only works with good data -- i.e. entity must have an id
37
+ app.get %r{^/entities(?:/)?([^/]+)?$} do
38
+ requested_entities = if params[:captures] && params[:captures][0]
39
+ params[:captures][0].split(',').uniq
40
+ else
41
+ nil
84
42
  end
85
43
 
86
- unless entity_checks.nil? || entity_checks.empty?
87
- result += entity_checks.inject([]) {|memo, (entity_name, checks)|
88
- checks = [checks] unless checks.is_a?(Array)
89
- entity = find_entity(entity_name)
90
- memo += checks.collect {|check|
91
- entity_check = find_entity_check(entity, check)
92
- {:entity => entity_name,
93
- :check => check,
94
- result_type.to_sym => yield(Flapjack::Gateways::JSONAPI::EntityCheckPresenter.new(entity_check))
95
- }
96
- }
97
- }.flatten(1)
44
+ entities = if requested_entities
45
+ Flapjack::Data::Entity.find_by_ids(requested_entities, :logger => logger, :redis => redis)
46
+ else
47
+ Flapjack::Data::Entity.all(:redis => redis)
98
48
  end
49
+ entities.compact!
99
50
 
100
- result
101
- end
102
-
103
- # NB: casts to UTC before converting to a timestamp
104
- def validate_and_parsetime(value)
105
- return unless value
106
- Time.iso8601(value).getutc.to_i
107
- rescue ArgumentError => e
108
- logger.error "Couldn't parse time from '#{value}'"
109
- nil
110
- end
111
-
112
- end
113
-
114
- # used for backwards-compatible route matching below
115
- ENTITY_CHECK_FRAGMENT = '(?:/([a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])(?:/(.+))?)?'
116
-
117
- def self.registered(app)
118
-
119
- app.helpers Flapjack::Gateways::JSONAPI::EntityMethods::Helpers
51
+ if requested_entities && requested_entities.empty?
52
+ raise Flapjack::Gateways::JSONAPI::EntitiesNotFound.new(requested_entities)
53
+ end
120
54
 
121
- app.get '/entities' do
122
- content_type :json
123
- cors_headers
55
+ linked_contact_ids = Flapjack::Data::Entity.contact_ids_for(entities.map(&:id), :redis => redis)
124
56
 
125
- entities_json = Flapjack::Data::Entity.all(:redis => redis).collect {|e|
126
- presenter = Flapjack::Gateways::JSONAPI::EntityPresenter.new(e, :redis => redis)
127
- id = (e.id.respond_to?(:length) && e.id.length > 0) ? e.id : e.name
128
- {'id' => id, 'name' => e.name, 'checks' => presenter.status }.to_json
129
- }.join(',')
57
+ entities_json = entities.collect {|entity|
58
+ entity.to_jsonapi(:contact_ids => linked_contact_ids[entity.id])
59
+ }.join(",")
130
60
 
131
61
  '{"entities":[' + entities_json + ']}'
132
62
  end
133
63
 
64
+ app.post '/entities' do
65
+ entities = wrapped_params('entities')
66
+ return err(403, "Entity with a nil id detected") if entities.any? {|e| e['id'].nil?}
134
67
 
135
- app.get %r{/status#{ENTITY_CHECK_FRAGMENT}} do
136
- content_type :json
137
-
138
- captures = params[:captures] || []
139
- entity_name = captures[0]
140
- check = captures[1]
141
-
142
- entities, checks = entities_and_checks(entity_name, check)
143
-
144
- results = present_api_results(entities, checks, 'status') {|presenter|
145
- presenter.status
68
+ entity_ids = entities.collect{|entity_data|
69
+ Flapjack::Data::Entity.add(entity_data, :redis => redis)
70
+ entity_data['id']
146
71
  }
147
72
 
148
- if entity_name
149
- # compatible with previous data format
150
- results = results.collect {|status_h| status_h[:status]}
151
- check ? results.first.to_json : "[" + results.map {|r| r.to_json }.join(',') + "]"
152
- else
153
- # new and improved data format which reflects the request param structure
154
- "[" + results.map {|r| r.to_json }.join(',') + "]"
155
- end
73
+ response.headers['Location'] = "#{request.base_url}/entities/#{entity_ids.join(',')}"
74
+ status 201
75
+ entity_ids.to_json
156
76
  end
157
77
 
158
- app.get %r{/((?:outages|(?:un)?scheduled_maintenances|downtime))#{ENTITY_CHECK_FRAGMENT}} do
159
- action = params[:captures][0].to_sym
160
- entity_name = params[:captures][1]
161
- check = params[:captures][2]
162
-
163
- entities, checks = entities_and_checks(entity_name, check)
164
-
165
- start_time = validate_and_parsetime(params[:start_time])
166
- end_time = validate_and_parsetime(params[:end_time])
167
-
168
- results = present_api_results(entities, checks, action) {|presenter|
169
- presenter.send(action, start_time, end_time)
170
- }
171
-
172
- if check
173
- # compatible with previous data format
174
- results.first[action].to_json
175
- elsif entity_name
176
- # compatible with previous data format
177
- rename = {:unscheduled_maintenances => :unscheduled_maintenance,
178
- :scheduled_maintenances => :scheduled_maintenance}
179
- drop = [:entity]
180
- results.collect{|r|
181
- r.inject({}) {|memo, (k, v)|
182
- if new_k = rename[k]
183
- memo[new_k] = v
184
- elsif !drop.include?(k)
185
- memo[k] = v
78
+ app.patch '/entities/:id' do
79
+ params[:id].split(',').collect {|entity_id|
80
+ find_entity_by_id(entity_id)
81
+ }.each do |entity|
82
+ apply_json_patch('entities') do |op, property, linked, value|
83
+ case op
84
+ when 'replace'
85
+ if ['name'].include?(property)
86
+ # # Name change not supported in Flapjack v1.x, too many changes required
87
+ # entity.update(property => value)
186
88
  end
187
- memo
188
- }
189
- }.to_json
190
- else
191
- # new and improved data format which reflects the request param structure
192
- results.to_json
89
+ when 'add'
90
+ if 'contacts'.eql?(linked)
91
+ contact = Flapjack::Data::Contact.find_by_id(value, :redis => redis)
92
+ contact.add_entity(entity) unless contact.nil?
93
+ end
94
+ when 'remove'
95
+ if 'contacts'.eql?(linked)
96
+ contact = Flapjack::Data::Contact.find_by_id(value, :redis => redis)
97
+ contact.remove_entity(entity) unless contact.nil?
98
+ end
99
+ end
100
+ end
193
101
  end
102
+
103
+ status 204
194
104
  end
195
105
 
196
106
  # create a scheduled maintenance period for a check on an entity
197
- app.post %r{/scheduled_maintenances#{ENTITY_CHECK_FRAGMENT}} do
198
-
199
- captures = params[:captures] || []
200
- entity_name = captures[0]
201
- check = captures[1]
202
-
203
- entities, checks = entities_and_checks(entity_name, check)
204
-
205
- start_time = validate_and_parsetime(params[:start_time])
206
- halt( err(403, "start time must be provided") ) unless start_time
207
-
208
- act_proc = proc {|entity_check|
209
- entity_check.create_scheduled_maintenance(start_time,
210
- params[:duration].to_i, :summary => params[:summary])
211
- }
107
+ app.post %r{^/scheduled_maintenances/entities/([^/]+)$} do
108
+ scheduled_maintenances = wrapped_params('scheduled_maintenances')
109
+ checks_for_entity_ids(params[:captures][0].split(',')).each do |check|
110
+ scheduled_maintenances.each do |wp|
111
+ start_time = validate_and_parsetime(wp['start_time'])
112
+ halt( err(403, "start time must be provided") ) unless start_time
113
+ check.create_scheduled_maintenance(start_time,
114
+ wp['duration'].to_i, :summary => wp['summary'])
115
+ end
116
+ end
212
117
 
213
- bulk_api_check_action(entities, checks, act_proc)
214
118
  status 204
215
119
  end
216
120
 
217
121
  # create an acknowledgement for a service on an entity
218
122
  # NB currently, this does not acknowledge a specific failure event, just
219
123
  # the entity-check as a whole
220
- app.post %r{/acknowledgements#{ENTITY_CHECK_FRAGMENT}} do
221
- captures = params[:captures] || []
222
- entity_name = captures[0]
223
- check = captures[1]
224
-
225
- entities, checks = entities_and_checks(entity_name, check)
226
-
227
- dur = params[:duration] ? params[:duration].to_i : nil
228
- duration = (dur.nil? || (dur <= 0)) ? (4 * 60 * 60) : dur
229
- summary = params[:summary]
230
-
231
- opts = {'duration' => duration}
232
- opts['summary'] = summary if summary
233
-
234
- act_proc = proc {|entity_check|
235
- Flapjack::Data::Event.create_acknowledgement(
236
- entity_check.entity_name, entity_check.check,
237
- :summary => params[:summary],
238
- :duration => duration,
239
- :redis => redis)
240
- }
124
+ app.post %r{^/unscheduled_maintenances/entities/([^/]+)$} do
125
+ unscheduled_maintenances = wrapped_params('unscheduled_maintenances', false)
126
+ checks_for_entity_ids(params[:captures][0].split(',')).each do |check|
127
+ unscheduled_maintenances.each do |wp|
128
+ dur = wp['duration'] ? wp['duration'].to_i : nil
129
+ duration = (dur.nil? || (dur <= 0)) ? (4 * 60 * 60) : dur
130
+ summary = wp['summary']
131
+
132
+ opts = {:duration => duration}
133
+ opts[:summary] = summary if summary
134
+
135
+ Flapjack::Data::Event.create_acknowledgement(
136
+ check.entity_name, check.check, {:redis => redis}.merge(opts))
137
+ end
138
+ end
241
139
 
242
- bulk_api_check_action(entities, checks, act_proc)
243
140
  status 204
244
141
  end
245
142
 
246
- app.delete %r{/((?:un)?scheduled_maintenances)} do
247
- action = params[:captures][0]
248
-
249
- # no backwards-compatible mode here, it's a new method
250
- entities, checks = entities_and_checks(nil, nil)
251
-
252
- act_proc = case action
253
- when 'scheduled_maintenances'
254
- start_time = validate_and_parsetime(params[:start_time])
255
- halt( err(403, "start time must be provided") ) unless start_time
256
- opts = {}
257
- proc {|entity_check| entity_check.end_scheduled_maintenance(start_time.to_i) }
258
- when 'unscheduled_maintenances'
259
- end_time = validate_and_parsetime(params[:end_time]) || Time.now
260
- proc {|entity_check| entity_check.end_unscheduled_maintenance(end_time.to_i) }
143
+ app.patch %r{^/unscheduled_maintenances/entities/([^/]+)$} do
144
+ checks_for_entity_ids( params[:captures][0].split(',') ).each do |check|
145
+ apply_json_patch('unscheduled_maintenances') do |op, property, linked, value|
146
+ case op
147
+ when 'replace'
148
+ if ['end_time'].include?(property)
149
+ end_time = validate_and_parsetime(value)
150
+ check.end_unscheduled_maintenance(end_time.to_i)
151
+ end
152
+ end
153
+ end
261
154
  end
262
-
263
- bulk_api_check_action(entities, checks, act_proc)
264
155
  status 204
265
156
  end
266
157
 
267
- app.post %r{/test_notifications#{ENTITY_CHECK_FRAGMENT}} do
268
- captures = params[:captures] || []
269
- entity_name = captures[0]
270
- check = captures[1]
271
-
272
- entities, checks = entities_and_checks(entity_name, check)
273
-
274
- act_proc = proc {|entity_check|
275
- summary = params[:summary] ||
276
- "Testing notifications to all contacts interested in entity #{entity_check.entity.name}"
277
- Flapjack::Data::Event.test_notifications(
278
- entity_check.entity_name, entity_check.check,
279
- :summary => summary,
280
- :redis => redis)
281
- }
158
+ app.delete %r{^/scheduled_maintenances/entities/([^/]+)$} do
159
+ start_time = validate_and_parsetime(params[:start_time])
160
+ halt( err(403, "start time must be provided") ) unless start_time
282
161
 
283
- bulk_api_check_action(entities, checks, act_proc)
162
+ checks_for_entity_ids(params[:captures][0].split(',')).each do |check|
163
+ check.end_scheduled_maintenance(start_time.to_i)
164
+ end
284
165
  status 204
285
166
  end
286
167
 
287
- app.post '/entities' do
288
- pass unless Flapjack::Gateways::JSONAPI::JSON_REQUEST_MIME_TYPES.include?(request.content_type)
289
-
290
- errors = []
291
- ret = nil
292
-
293
- # FIXME should scan for invalid records before making any changes, fail early
294
-
295
- entities = params[:entities]
296
- unless entities
297
- logger.debug("no entities object found in the following supplied JSON:")
298
- logger.debug(request.body)
299
- return err(403, "No entities object received")
300
- end
301
- return err(403, "The received entities object is not an Enumerable") unless entities.is_a?(Enumerable)
302
- return err(403, "Entity with a nil id detected") unless entities.any? {|e| !e['id'].nil?}
303
-
304
- entities.each do |entity|
305
- unless entity['id']
306
- errors << "Entity not imported as it has no id: #{entity.inspect}"
307
- next
168
+ app.post %r{^/test_notifications/entities/([^/]+)$} do
169
+ test_notifications = wrapped_params('test_notifications', false)
170
+ checks_for_entity_ids(params[:captures][0].split(',')).each do |check|
171
+ test_notifications.each do |wp|
172
+ summary = wp['summary'] ||
173
+ "Testing notifications to all contacts interested in entity #{check.entity.name}"
174
+ Flapjack::Data::Event.test_notifications(
175
+ check.entity_name, check.check,
176
+ :summary => summary,
177
+ :redis => redis)
308
178
  end
309
- Flapjack::Data::Entity.add(entity, :redis => redis)
310
179
  end
311
- errors.empty? ? 204 : err(403, *errors)
180
+ status 204
312
181
  end
313
182
 
314
183
  end
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'sinatra/base'
4
+
5
+ require 'flapjack/data/contact'
6
+
7
+ module Flapjack
8
+
9
+ module Gateways
10
+
11
+ class JSONAPI < Sinatra::Base
12
+
13
+ module MediumMethods
14
+
15
+ SEMAPHORE_CONTACT_MASS_UPDATE = 'contact_mass_update'
16
+
17
+ module Helpers
18
+
19
+ def obtain_semaphore(resource)
20
+ semaphore = nil
21
+ strikes = 0
22
+ begin
23
+ semaphore = Flapjack::Data::Semaphore.new(resource, :redis => redis, :expiry => 30)
24
+ rescue Flapjack::Data::Semaphore::ResourceLocked
25
+ strikes += 1
26
+ raise Flapjack::Gateways::JSONAPI::ResourceLocked.new(resource) unless strikes < 3
27
+ sleep 1
28
+ retry
29
+ end
30
+ raise Flapjack::Gateways::JSONAPI::ResourceLocked.new(resource) unless semaphore
31
+ semaphore
32
+ end
33
+
34
+ # TODO validate that media type exists in redis
35
+ def split_media_ids(media_ids)
36
+
37
+ contact_cache = {}
38
+
39
+ media_ids.split(',').uniq.collect do |m_id|
40
+ m_id =~ /\A(.+)_(email|sms|jabber)\z/
41
+
42
+ contact_id = $1
43
+ media_type = $2
44
+ halt err(422, "Could not get contact_id from media_id") if contact_id.nil?
45
+ halt err(422, "Could not get media type from media_id") if media_type.nil?
46
+
47
+ contact_cache[contact_id] ||= find_contact(contact_id)
48
+
49
+ {:contact => contact_cache[contact_id], :type => media_type}
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ def self.registered(app)
56
+ app.helpers Flapjack::Gateways::JSONAPI::Helpers
57
+ app.helpers Flapjack::Gateways::JSONAPI::MediumMethods::Helpers
58
+
59
+ # Creates media records for a contact
60
+ app.post '/contacts/:contact_id/media' do
61
+ media_data = params[:media]
62
+
63
+ if media_data.nil? || !media_data.is_a?(Enumerable)
64
+ halt err(422, "No valid media were submitted")
65
+ end
66
+
67
+ unless media_data.all? {|m| m['id'].nil? }
68
+ halt err(422, "Media creation cannot include IDs")
69
+ end
70
+
71
+ semaphore = obtain_semaphore(SEMAPHORE_CONTACT_MASS_UPDATE)
72
+ contact = Flapjack::Data::Contact.find_by_id(params[:contact_id], :redis => redis)
73
+ if contact.nil?
74
+ semaphore.release
75
+ halt err(422, "Contact id:'#{params[:contact_id]}' could not be loaded")
76
+ end
77
+
78
+ media_data.each do |medium_data|
79
+ type = medium_data['type']
80
+ contact.set_address_for_media(type, medium_data['address'])
81
+ contact.set_interval_for_media(type, medium_data['interval'])
82
+ contact.set_rollup_threshold_for_media(type, medium_data['rollup_threshold'])
83
+ medium_data[:id] = "#{contact.id}_#{type}"
84
+ medium_data[:links] = {:contacts => [contact.id]}
85
+ end
86
+
87
+ semaphore.release
88
+
89
+ status 201
90
+
91
+ '{"media":' + media_data.to_json + '}'
92
+ end
93
+
94
+ # get one or more media records; media ids are, for Flapjack
95
+ # v1, composed of "#{contact.id}_#{media_type}"
96
+ app.get %r{^/media(?:/)?([^/]+)?$} do
97
+ media_list_cache = {}
98
+ contact_media = if params[:captures] && params[:captures][0]
99
+ split_media_ids(params[:captures][0])
100
+ else
101
+ Flapjack::Data::Contact.all(:redis => redis).collect do |contact|
102
+ media_list_cache[contact.id] ||= contact.media_list
103
+ media_list_cache[contact.id].collect do |media_type|
104
+ {:contact => contact, :type => media_type}
105
+ end
106
+ end.flatten(1)
107
+ end
108
+
109
+ media_data = contact_media.inject([]) do |memo, contact_media_type|
110
+ contact = contact_media_type[:contact]
111
+ media_type = contact_media_type[:type]
112
+
113
+ media_list_cache[contact.id] ||= contact.media_list
114
+ if media_list_cache[contact.id].include?(media_type)
115
+ medium_id = "#{contact.id}_#{media_type}"
116
+ memo <<
117
+ {:id => medium_id,
118
+ :type => media_type,
119
+ :address => contact.media[media_type],
120
+ :interval => contact.media_intervals[media_type],
121
+ :rollup_threshold => contact.media_rollup_thresholds[media_type],
122
+ :links => {:contacts => [contact.id]}}
123
+ end
124
+
125
+ memo
126
+ end
127
+
128
+ '{"media":' + media_data.to_json + '}'
129
+ end
130
+
131
+ # update one or more media records; media ids are, for Flapjack
132
+ # v1, composed of "#{contact.id}_#{media_type}"
133
+ app.patch '/media/:id' do
134
+ media_list_cache = {}
135
+ split_media_ids(params[:id]).each do |contact_media_type|
136
+ contact = contact_media_type[:contact]
137
+ media_type = contact_media_type[:type]
138
+ media_list_cache[contact.id] ||= contact.media_list
139
+ next unless media_list_cache[contact.id].include?(media_type)
140
+ apply_json_patch('media') do |op, property, linked, value|
141
+ if 'replace'.eql?(op)
142
+ case property
143
+ when 'address'
144
+ contact.set_address_for_media(media_type, value)
145
+ when 'interval'
146
+ contact.set_interval_for_media(media_type, value)
147
+ when 'rollup_threshold'
148
+ contact.set_rollup_threshold_for_media(media_type, value)
149
+ end
150
+ end
151
+ end
152
+ end
153
+
154
+ status 204
155
+ end
156
+
157
+ # delete one or more media records; media ids are, for Flapjack
158
+ # v1, composed of "#{contact.id}_#{media_type}"
159
+ app.delete '/media/:id' do
160
+ media_list_cache = {}
161
+ split_media_ids(params[:id]).each do |contact_media_type|
162
+ contact = contact_media_type[:contact]
163
+ media_type = contact_media_type[:type]
164
+ media_list_cache[contact.id] ||= contact.media_list
165
+ next unless media_list_cache[contact.id].include?(media_type)
166
+ contact.remove_media(media_type)
167
+ end
168
+ status 204
169
+ end
170
+
171
+ end
172
+
173
+ end
174
+
175
+ end
176
+
177
+ end
178
+
179
+ end