flapjack 0.7.16 → 0.7.17
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.
- data/.gitignore +3 -0
- data/.travis.yml +1 -0
- data/CHANGELOG.md +9 -0
- data/lib/flapjack/data/entity_check.rb +13 -8
- data/lib/flapjack/data/notification_rule.rb +11 -7
- data/lib/flapjack/gateways/api.rb +28 -655
- data/lib/flapjack/gateways/api/contact_methods.rb +342 -0
- data/lib/flapjack/gateways/api/entity_methods.rb +364 -0
- data/lib/flapjack/gateways/api/entity_presenter.rb +6 -6
- data/lib/flapjack/gateways/api/rack/json_params_parser.rb +26 -0
- data/lib/flapjack/version.rb +1 -1
- data/spec/lib/flapjack/data/entity_check_spec.rb +21 -0
- data/spec/lib/flapjack/data/notification_rule_spec.rb +11 -6
- data/spec/lib/flapjack/gateways/api/contact_methods_spec.rb +709 -0
- data/spec/lib/flapjack/gateways/api/entity_check_presenter_spec.rb +1 -3
- data/spec/lib/flapjack/gateways/api/entity_methods_spec.rb +868 -0
- data/spec/lib/flapjack/gateways/api/entity_presenter_spec.rb +13 -12
- data/spec/lib/flapjack/gateways/api_spec.rb +1 -1520
- data/tmp/redis_find_spurious_unknown_states.rb +52 -0
- metadata +12 -6
- data/.rbenv-version +0 -1
- data/tmp/redis_delete_all_keys.rb +0 -11
@@ -0,0 +1,342 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'sinatra/base'
|
4
|
+
|
5
|
+
require 'flapjack/data/contact'
|
6
|
+
require 'flapjack/data/notification_rule'
|
7
|
+
|
8
|
+
module Flapjack
|
9
|
+
|
10
|
+
module Gateways
|
11
|
+
|
12
|
+
class API < Sinatra::Base
|
13
|
+
|
14
|
+
class ContactNotFound < RuntimeError
|
15
|
+
attr_reader :contact_id
|
16
|
+
def initialize(contact_id)
|
17
|
+
@contact_id = contact_id
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class NotificationRuleNotFound < RuntimeError
|
22
|
+
attr_reader :rule_id
|
23
|
+
def initialize(rule_id)
|
24
|
+
@rule_id = rule_id
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module ContactMethods
|
29
|
+
|
30
|
+
module Helpers
|
31
|
+
|
32
|
+
def find_contact(contact_id)
|
33
|
+
contact = Flapjack::Data::Contact.find_by_id(contact_id, :logger => logger, :redis => redis)
|
34
|
+
raise Flapjack::Gateways::API::ContactNotFound.new(contact_id) if contact.nil?
|
35
|
+
contact
|
36
|
+
end
|
37
|
+
|
38
|
+
def find_rule(rule_id)
|
39
|
+
rule = Flapjack::Data::NotificationRule.find_by_id(rule_id, :logger => logger, :redis => redis)
|
40
|
+
raise Flapjack::Gateways::API::NotificationRuleNotFound.new(rule_id) if rule.nil?
|
41
|
+
rule
|
42
|
+
end
|
43
|
+
|
44
|
+
def find_tags(tags)
|
45
|
+
halt err(403, "no tags") if tags.nil? || tags.empty?
|
46
|
+
tags
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.registered(app)
|
52
|
+
|
53
|
+
app.helpers Flapjack::Gateways::API::ContactMethods::Helpers
|
54
|
+
|
55
|
+
app.post '/contacts' do
|
56
|
+
pass unless 'application/json'.eql?(request.content_type)
|
57
|
+
content_type :json
|
58
|
+
|
59
|
+
errors = []
|
60
|
+
|
61
|
+
contacts_data = params[:contacts]
|
62
|
+
if contacts_data.nil? || !contacts_data.is_a?(Enumerable)
|
63
|
+
errors << "No valid contacts were submitted"
|
64
|
+
else
|
65
|
+
# stringifying as integer string params are automatically integered,
|
66
|
+
# but our redis ids are strings
|
67
|
+
contacts_data_ids = contacts_data.reject {|c| c['id'].nil? }.
|
68
|
+
map {|co| co['id'].to_s }
|
69
|
+
|
70
|
+
if contacts_data_ids.empty?
|
71
|
+
errors << "No contacts with IDs were submitted"
|
72
|
+
else
|
73
|
+
contacts = Flapjack::Data::Contact.all(:redis => redis)
|
74
|
+
contacts_h = hashify(*contacts) {|c| [c.id, c] }
|
75
|
+
contacts_ids = contacts_h.keys
|
76
|
+
|
77
|
+
# delete contacts not found in the bulk list
|
78
|
+
(contacts_ids - contacts_data_ids).each do |contact_to_delete_id|
|
79
|
+
contact_to_delete = contacts.detect {|c| c.id == contact_to_delete_id }
|
80
|
+
contact_to_delete.delete!
|
81
|
+
end
|
82
|
+
|
83
|
+
# add or update contacts found in the bulk list
|
84
|
+
contacts_data.reject {|cd| cd['id'].nil? }.each do |contact_data|
|
85
|
+
if contacts_ids.include?(contact_data['id'].to_s)
|
86
|
+
contacts_h[contact_data['id'].to_s].update(contact_data)
|
87
|
+
else
|
88
|
+
Flapjack::Data::Contact.add(contact_data, :redis => redis)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
errors.empty? ? 204 : err(403, *errors)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns all the contacts
|
97
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts
|
98
|
+
app.get '/contacts' do
|
99
|
+
content_type :json
|
100
|
+
|
101
|
+
Flapjack::Data::Contact.all(:redis => redis).to_json
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns the core information about the specified contact
|
105
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id
|
106
|
+
app.get '/contacts/:contact_id' do
|
107
|
+
content_type :json
|
108
|
+
|
109
|
+
contact = find_contact(params[:contact_id])
|
110
|
+
contact.to_json
|
111
|
+
end
|
112
|
+
|
113
|
+
# Lists this contact's notification rules
|
114
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_notification_rules
|
115
|
+
app.get '/contacts/:contact_id/notification_rules' do
|
116
|
+
content_type :json
|
117
|
+
|
118
|
+
contact = find_contact(params[:contact_id])
|
119
|
+
contact.notification_rules.to_json
|
120
|
+
end
|
121
|
+
|
122
|
+
# Get the specified notification rule for this user
|
123
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_notification_rules_id
|
124
|
+
app.get '/notification_rules/:id' do
|
125
|
+
content_type :json
|
126
|
+
|
127
|
+
rule = find_rule(params[:id])
|
128
|
+
rule.to_json
|
129
|
+
end
|
130
|
+
|
131
|
+
# Creates a notification rule for a contact
|
132
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-post_contacts_id_notification_rules
|
133
|
+
app.post '/notification_rules' do
|
134
|
+
content_type :json
|
135
|
+
|
136
|
+
if params[:id]
|
137
|
+
halt err(403, "post cannot be used for update, do a put instead")
|
138
|
+
end
|
139
|
+
|
140
|
+
logger.debug("post /notification_rules data: ")
|
141
|
+
logger.debug(params.inspect)
|
142
|
+
|
143
|
+
contact = find_contact(params[:contact_id])
|
144
|
+
|
145
|
+
rule_data = hashify(:entities, :entity_tags,
|
146
|
+
:warning_media, :critical_media, :time_restrictions,
|
147
|
+
:warning_blackhole, :critical_blackhole) {|k| [k, params[k]]}
|
148
|
+
|
149
|
+
rule_or_errors = contact.add_notification_rule(rule_data, :logger => logger)
|
150
|
+
|
151
|
+
unless rule_or_errors.respond_to?(:critical_media)
|
152
|
+
halt err(403, *rule_or_errors)
|
153
|
+
end
|
154
|
+
rule_or_errors.to_json
|
155
|
+
end
|
156
|
+
|
157
|
+
# Updates a notification rule
|
158
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-put_notification_rules_id
|
159
|
+
app.put('/notification_rules/:id') do
|
160
|
+
content_type :json
|
161
|
+
|
162
|
+
logger.debug("put /notification_rules/#{params[:id]} data: ")
|
163
|
+
logger.debug(params.inspect)
|
164
|
+
|
165
|
+
rule = find_rule(params[:id])
|
166
|
+
contact = find_contact(rule.contact_id)
|
167
|
+
|
168
|
+
rule_data = hashify(:entities, :entity_tags,
|
169
|
+
:warning_media, :critical_media, :time_restrictions,
|
170
|
+
:warning_blackhole, :critical_blackhole) {|k| [k, params[k]]}
|
171
|
+
|
172
|
+
errors = rule.update(rule_data, :logger => logger)
|
173
|
+
|
174
|
+
unless errors.nil? || errors.empty?
|
175
|
+
halt err(403, *errors)
|
176
|
+
end
|
177
|
+
rule.to_json
|
178
|
+
end
|
179
|
+
|
180
|
+
# Deletes a notification rule
|
181
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-put_notification_rules_id
|
182
|
+
app.delete('/notification_rules/:id') do
|
183
|
+
logger.debug("delete /notification_rules/#{params[:id]}")
|
184
|
+
rule = find_rule(params[:id])
|
185
|
+
logger.debug("rule to delete: #{rule.inspect}, contact_id: #{rule.contact_id}")
|
186
|
+
contact = find_contact(rule.contact_id)
|
187
|
+
contact.delete_notification_rule(rule)
|
188
|
+
status 204
|
189
|
+
end
|
190
|
+
|
191
|
+
# Returns the media of a contact
|
192
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_media
|
193
|
+
app.get '/contacts/:contact_id/media' do
|
194
|
+
content_type :json
|
195
|
+
|
196
|
+
contact = find_contact(params[:contact_id])
|
197
|
+
|
198
|
+
media = contact.media
|
199
|
+
media_intervals = contact.media_intervals
|
200
|
+
media_addr_int = hashify(*media.keys) {|k|
|
201
|
+
[k, {'address' => media[k],
|
202
|
+
'interval' => media_intervals[k] }]
|
203
|
+
}
|
204
|
+
media_addr_int.to_json
|
205
|
+
end
|
206
|
+
|
207
|
+
# Returns the specified media of a contact
|
208
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_media_media
|
209
|
+
app.get('/contacts/:contact_id/media/:id') do
|
210
|
+
content_type :json
|
211
|
+
|
212
|
+
contact = find_contact(params[:contact_id])
|
213
|
+
media = contact.media[params[:id]]
|
214
|
+
if media.nil?
|
215
|
+
halt err(403, "no #{params[:id]} for contact '#{params[:contact_id]}'")
|
216
|
+
end
|
217
|
+
interval = contact.media_intervals[params[:id]]
|
218
|
+
if interval.nil?
|
219
|
+
halt err(403, "no #{params[:id]} interval for contact '#{params[:contact_id]}'")
|
220
|
+
end
|
221
|
+
{'address' => media,
|
222
|
+
'interval' => interval}.to_json
|
223
|
+
end
|
224
|
+
|
225
|
+
# Creates or updates a media of a contact
|
226
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-put_contacts_id_media_media
|
227
|
+
app.put('/contacts/:contact_id/media/:id') do
|
228
|
+
content_type :json
|
229
|
+
|
230
|
+
contact = find_contact(params[:contact_id])
|
231
|
+
errors = []
|
232
|
+
if params[:address].nil?
|
233
|
+
errors << "no address for '#{params[:id]}' media"
|
234
|
+
end
|
235
|
+
|
236
|
+
halt err(403, *errors) unless errors.empty?
|
237
|
+
|
238
|
+
contact.set_address_for_media(params[:id], params[:address])
|
239
|
+
contact.set_interval_for_media(params[:id], params[:interval])
|
240
|
+
|
241
|
+
{'address' => contact.media[params[:id]],
|
242
|
+
'interval' => contact.media_intervals[params[:id]]}.to_json
|
243
|
+
end
|
244
|
+
|
245
|
+
# delete a media of a contact
|
246
|
+
app.delete('/contacts/:contact_id/media/:id') do
|
247
|
+
contact = find_contact(params[:contact_id])
|
248
|
+
contact.remove_media(params[:id])
|
249
|
+
status 204
|
250
|
+
end
|
251
|
+
|
252
|
+
# Returns the timezone of a contact
|
253
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-get_contacts_id_timezone
|
254
|
+
app.get('/contacts/:contact_id/timezone') do
|
255
|
+
content_type :json
|
256
|
+
|
257
|
+
contact = find_contact(params[:contact_id])
|
258
|
+
contact.timezone.name.to_json
|
259
|
+
end
|
260
|
+
|
261
|
+
# Sets the timezone of a contact
|
262
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-put_contacts_id_timezone
|
263
|
+
app.put('/contacts/:contact_id/timezone') do
|
264
|
+
content_type :json
|
265
|
+
|
266
|
+
contact = find_contact(params[:contact_id])
|
267
|
+
contact.timezone = params[:timezone]
|
268
|
+
contact.timezone.name.to_json
|
269
|
+
end
|
270
|
+
|
271
|
+
# Removes the timezone of a contact
|
272
|
+
# https://github.com/flpjck/flapjack/wiki/API#wiki-put_contacts_id_timezone
|
273
|
+
app.delete('/contacts/:contact_id/timezone') do
|
274
|
+
contact = find_contact(params[:contact_id])
|
275
|
+
contact.timezone = nil
|
276
|
+
status 204
|
277
|
+
end
|
278
|
+
|
279
|
+
app.post '/contacts/:contact_id/tags' do
|
280
|
+
content_type :json
|
281
|
+
|
282
|
+
tags = find_tags(params[:tag])
|
283
|
+
contact = find_contact(params[:contact_id])
|
284
|
+
contact.add_tags(*tags)
|
285
|
+
contact.tags.to_json
|
286
|
+
end
|
287
|
+
|
288
|
+
app.post '/contacts/:contact_id/entity_tags' do
|
289
|
+
content_type :json
|
290
|
+
contact = find_contact(params[:contact_id])
|
291
|
+
contact.entities.map {|e| e[:entity]}.each do |entity|
|
292
|
+
next unless tags = params[:entity][entity.name]
|
293
|
+
entity.add_tags(*tags)
|
294
|
+
end
|
295
|
+
contact_ent_tag = hashify(*contact.entities(:tags => true)) {|et|
|
296
|
+
[et[:entity].name, et[:tags]]
|
297
|
+
}
|
298
|
+
contact_ent_tag.to_json
|
299
|
+
end
|
300
|
+
|
301
|
+
app.delete '/contacts/:contact_id/tags' do
|
302
|
+
tags = find_tags(params[:tag])
|
303
|
+
contact = find_contact(params[:contact_id])
|
304
|
+
contact.delete_tags(*tags)
|
305
|
+
status 204
|
306
|
+
end
|
307
|
+
|
308
|
+
app.delete '/contacts/:contact_id/entity_tags' do
|
309
|
+
contact = find_contact(params[:contact_id])
|
310
|
+
contact.entities.map {|e| e[:entity]}.each do |entity|
|
311
|
+
next unless tags = params[:entity][entity.name]
|
312
|
+
entity.delete_tags(*tags)
|
313
|
+
end
|
314
|
+
status 204
|
315
|
+
end
|
316
|
+
|
317
|
+
app.get '/contacts/:contact_id/tags' do
|
318
|
+
content_type :json
|
319
|
+
|
320
|
+
contact = find_contact(params[:contact_id])
|
321
|
+
contact.tags.to_json
|
322
|
+
end
|
323
|
+
|
324
|
+
app.get '/contacts/:contact_id/entity_tags' do
|
325
|
+
content_type :json
|
326
|
+
|
327
|
+
contact = find_contact(params[:contact_id])
|
328
|
+
contact_ent_tag = hashify(*contact.entities(:tags => true)) {|et|
|
329
|
+
[et[:entity].name, et[:tags]]
|
330
|
+
}
|
331
|
+
contact_ent_tag.to_json
|
332
|
+
end
|
333
|
+
|
334
|
+
end
|
335
|
+
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|
339
|
+
|
340
|
+
end
|
341
|
+
|
342
|
+
end
|
@@ -0,0 +1,364 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'sinatra/base'
|
4
|
+
|
5
|
+
require 'flapjack/data/entity'
|
6
|
+
require 'flapjack/data/entity_check'
|
7
|
+
|
8
|
+
require 'flapjack/gateways/api/entity_presenter'
|
9
|
+
require 'flapjack/gateways/api/entity_check_presenter'
|
10
|
+
|
11
|
+
module Flapjack
|
12
|
+
|
13
|
+
module Gateways
|
14
|
+
|
15
|
+
class API < Sinatra::Base
|
16
|
+
|
17
|
+
class EntityCheckNotFound < RuntimeError
|
18
|
+
attr_reader :entity, :check
|
19
|
+
def initialize(entity, check)
|
20
|
+
@entity = entity
|
21
|
+
@check = check
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class EntityNotFound < RuntimeError
|
26
|
+
attr_reader :entity
|
27
|
+
def initialize(entity)
|
28
|
+
@entity = entity
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module EntityMethods
|
33
|
+
|
34
|
+
module Helpers
|
35
|
+
|
36
|
+
def find_entity(entity_name)
|
37
|
+
entity = Flapjack::Data::Entity.find_by_name(entity_name, :redis => redis)
|
38
|
+
raise Flapjack::Gateways::API::EntityNotFound.new(entity_name) if entity.nil?
|
39
|
+
entity
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_entity_check(entity, check)
|
43
|
+
entity_check = Flapjack::Data::EntityCheck.for_entity(entity,
|
44
|
+
check, :redis => redis)
|
45
|
+
raise Flapjack::Gateways::API::EntityCheckNotFound.new(entity, check) if entity_check.nil?
|
46
|
+
entity_check
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_tags(tags)
|
50
|
+
halt err(403, "no tags") if tags.nil? || tags.empty?
|
51
|
+
tags
|
52
|
+
end
|
53
|
+
|
54
|
+
def entities_and_checks(entity_name, check)
|
55
|
+
if entity_name
|
56
|
+
# backwards-compatible, single entity or entity&check from route
|
57
|
+
entities = check ? nil : [entity_name]
|
58
|
+
checks = check ? {entity_name => check} : nil
|
59
|
+
else
|
60
|
+
# new and improved bulk API queries
|
61
|
+
entities = params[:entity]
|
62
|
+
checks = params[:check]
|
63
|
+
entities = [entities] unless entities.nil? || entities.is_a?(Array)
|
64
|
+
# TODO err if checks isn't a Hash (similar rules as in flapjack-diner)
|
65
|
+
end
|
66
|
+
[entities, checks]
|
67
|
+
end
|
68
|
+
|
69
|
+
def bulk_api_check_action(entities, entity_checks, action, params = {})
|
70
|
+
unless entities.nil? || entities.empty?
|
71
|
+
entities.each do |entity_name|
|
72
|
+
entity = find_entity(entity_name)
|
73
|
+
checks = entity.check_list.sort
|
74
|
+
checks.each do |check|
|
75
|
+
action.call( find_entity_check(entity, check) )
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
unless entity_checks.nil? || entity_checks.empty?
|
81
|
+
entity_checks.each_pair do |entity_name, checks|
|
82
|
+
entity = find_entity(entity_name)
|
83
|
+
checks = [checks] unless checks.is_a?(Array)
|
84
|
+
checks.each do |check|
|
85
|
+
action.call( find_entity_check(entity, check) )
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def present_api_results(entities, entity_checks, result_type, &block)
|
92
|
+
result = []
|
93
|
+
|
94
|
+
unless entities.nil? || entities.empty?
|
95
|
+
result += entities.collect {|entity_name|
|
96
|
+
entity = find_entity(entity_name)
|
97
|
+
yield(Flapjack::Gateways::API::EntityPresenter.new(entity, :redis => redis))
|
98
|
+
}.flatten(1)
|
99
|
+
end
|
100
|
+
|
101
|
+
unless entity_checks.nil? || entity_checks.empty?
|
102
|
+
result += entity_checks.inject([]) {|memo, (entity_name, checks)|
|
103
|
+
checks = [checks] unless checks.is_a?(Array)
|
104
|
+
entity = find_entity(entity_name)
|
105
|
+
memo += checks.collect {|check|
|
106
|
+
entity_check = find_entity_check(entity, check)
|
107
|
+
{:entity => entity_name,
|
108
|
+
:check => check,
|
109
|
+
result_type.to_sym => yield(Flapjack::Gateways::API::EntityCheckPresenter.new(entity_check))
|
110
|
+
}
|
111
|
+
}
|
112
|
+
}.flatten(1)
|
113
|
+
end
|
114
|
+
|
115
|
+
result
|
116
|
+
end
|
117
|
+
|
118
|
+
# NB: casts to UTC before converting to a timestamp
|
119
|
+
def validate_and_parsetime(value)
|
120
|
+
return unless value
|
121
|
+
Time.iso8601(value).getutc.to_i
|
122
|
+
rescue ArgumentError => e
|
123
|
+
logger.error "Couldn't parse time from '#{value}'"
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
# used for backwards-compatible route matching below
|
130
|
+
ENTITY_CHECK_FRAGMENT = '(?:/([a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])(?:/(.+))?)?'
|
131
|
+
|
132
|
+
def self.registered(app)
|
133
|
+
|
134
|
+
app.helpers Flapjack::Gateways::API::EntityMethods::Helpers
|
135
|
+
|
136
|
+
app.get '/entities' do
|
137
|
+
content_type :json
|
138
|
+
ret = Flapjack::Data::Entity.all(:redis => redis).sort_by(&:name).collect {|e|
|
139
|
+
presenter = Flapjack::Gateways::API::EntityPresenter.new(e, :redis => redis)
|
140
|
+
{'id' => e.id, 'name' => e.name, 'checks' => presenter.status }
|
141
|
+
}
|
142
|
+
ret.to_json
|
143
|
+
end
|
144
|
+
|
145
|
+
app.get '/checks/:entity' do
|
146
|
+
content_type :json
|
147
|
+
entity = find_entity(params[:entity])
|
148
|
+
entity.check_list.to_json
|
149
|
+
end
|
150
|
+
|
151
|
+
app.get %r{/status#{ENTITY_CHECK_FRAGMENT}} do
|
152
|
+
content_type :json
|
153
|
+
|
154
|
+
captures = params[:captures] || []
|
155
|
+
entity_name = captures[0]
|
156
|
+
check = captures[1]
|
157
|
+
|
158
|
+
entities, checks = entities_and_checks(entity_name, check)
|
159
|
+
|
160
|
+
results = present_api_results(entities, checks, 'status') {|presenter|
|
161
|
+
presenter.status
|
162
|
+
}
|
163
|
+
|
164
|
+
if entity_name
|
165
|
+
# compatible with previous data format
|
166
|
+
results = results.collect {|status_h| status_h[:status]}
|
167
|
+
(check ? results.first : results).to_json
|
168
|
+
else
|
169
|
+
# new and improved data format which reflects the request param structure
|
170
|
+
results.to_json
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
app.get %r{/((?:outages|(?:un)?scheduled_maintenances|downtime))#{ENTITY_CHECK_FRAGMENT}} do
|
175
|
+
action = params[:captures][0].to_sym
|
176
|
+
entity_name = params[:captures][1]
|
177
|
+
check = params[:captures][2]
|
178
|
+
|
179
|
+
entities, checks = entities_and_checks(entity_name, check)
|
180
|
+
|
181
|
+
start_time = validate_and_parsetime(params[:start_time])
|
182
|
+
end_time = validate_and_parsetime(params[:end_time])
|
183
|
+
|
184
|
+
results = present_api_results(entities, checks, action) {|presenter|
|
185
|
+
presenter.send(action, start_time, end_time)
|
186
|
+
}
|
187
|
+
|
188
|
+
if check
|
189
|
+
# compatible with previous data format
|
190
|
+
results.first[action].to_json
|
191
|
+
elsif entity_name
|
192
|
+
# compatible with previous data format
|
193
|
+
rename = {:unscheduled_maintenances => :unscheduled_maintenance,
|
194
|
+
:scheduled_maintenances => :scheduled_maintenance}
|
195
|
+
drop = [:entity]
|
196
|
+
results.collect{|r|
|
197
|
+
r.inject({}) {|memo, (k, v)|
|
198
|
+
if new_k = rename[k]
|
199
|
+
memo[new_k] = v
|
200
|
+
elsif !drop.include?(k)
|
201
|
+
memo[k] = v
|
202
|
+
end
|
203
|
+
memo
|
204
|
+
}
|
205
|
+
}.to_json
|
206
|
+
else
|
207
|
+
# new and improved data format which reflects the request param structure
|
208
|
+
results.to_json
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# create a scheduled maintenance period for a check on an entity
|
213
|
+
app.post %r{/scheduled_maintenances#{ENTITY_CHECK_FRAGMENT}} do
|
214
|
+
|
215
|
+
captures = params[:captures] || []
|
216
|
+
entity_name = captures[0]
|
217
|
+
check = captures[1]
|
218
|
+
|
219
|
+
entities, checks = entities_and_checks(entity_name, check)
|
220
|
+
|
221
|
+
start_time = validate_and_parsetime(params[:start_time])
|
222
|
+
halt( err(403, "start time must be provided") ) unless start_time
|
223
|
+
|
224
|
+
act_proc = proc {|entity_check|
|
225
|
+
entity_check.create_scheduled_maintenance(:start_time => start_time,
|
226
|
+
:duration => params[:duration].to_i, :summary => params[:summary])
|
227
|
+
}
|
228
|
+
|
229
|
+
bulk_api_check_action(entities, checks, act_proc)
|
230
|
+
status 204
|
231
|
+
end
|
232
|
+
|
233
|
+
# create an acknowledgement for a service on an entity
|
234
|
+
# NB currently, this does not acknowledge a specific failure event, just
|
235
|
+
# the entity-check as a whole
|
236
|
+
app.post %r{/acknowledgements#{ENTITY_CHECK_FRAGMENT}} do
|
237
|
+
captures = params[:captures] || []
|
238
|
+
entity_name = captures[0]
|
239
|
+
check = captures[1]
|
240
|
+
|
241
|
+
entities, checks = entities_and_checks(entity_name, check)
|
242
|
+
|
243
|
+
dur = params[:duration] ? params[:duration].to_i : nil
|
244
|
+
duration = (dur.nil? || (dur <= 0)) ? (4 * 60 * 60) : dur
|
245
|
+
summary = params[:summary]
|
246
|
+
|
247
|
+
opts = {'duration' => duration}
|
248
|
+
opts['summary'] = summary if summary
|
249
|
+
|
250
|
+
act_proc = proc {|entity_check|
|
251
|
+
Flapjack::Data::Event.create_acknowledgement(
|
252
|
+
entity_check.entity_name, entity_check.check,
|
253
|
+
:summary => params[:summary],
|
254
|
+
:duration => duration,
|
255
|
+
:redis => redis)
|
256
|
+
}
|
257
|
+
|
258
|
+
bulk_api_check_action(entities, checks, act_proc)
|
259
|
+
status 204
|
260
|
+
end
|
261
|
+
|
262
|
+
app.delete %r{/((?:un)?scheduled_maintenances)} do
|
263
|
+
action = params[:captures][0]
|
264
|
+
|
265
|
+
# no backwards-compatible mode here, it's a new method
|
266
|
+
entities, checks = entities_and_checks(nil, nil)
|
267
|
+
|
268
|
+
act_proc = case action
|
269
|
+
when 'scheduled_maintenances'
|
270
|
+
start_time = validate_and_parsetime(params[:start_time])
|
271
|
+
halt( err(403, "start time must be provided") ) unless start_time
|
272
|
+
opts = {}
|
273
|
+
opts[:start_time] = start_time.to_i
|
274
|
+
proc {|entity_check| entity_check.delete_scheduled_maintenance(opts) }
|
275
|
+
when 'unscheduled_maintenances'
|
276
|
+
end_time = validate_and_parsetime(params[:end_time])
|
277
|
+
opts = {}
|
278
|
+
opts[:end_time] = end_time.to_i if end_time
|
279
|
+
proc {|entity_check| entity_check.end_unscheduled_maintenance(opts) }
|
280
|
+
end
|
281
|
+
|
282
|
+
bulk_api_check_action(entities, checks, act_proc)
|
283
|
+
status 204
|
284
|
+
end
|
285
|
+
|
286
|
+
app.post %r{/test_notifications#{ENTITY_CHECK_FRAGMENT}} do
|
287
|
+
captures = params[:captures] || []
|
288
|
+
entity_name = captures[0]
|
289
|
+
check = captures[1]
|
290
|
+
|
291
|
+
entities, checks = entities_and_checks(entity_name, check)
|
292
|
+
|
293
|
+
act_proc = proc {|entity_check|
|
294
|
+
summary = params[:summary] ||
|
295
|
+
"Testing notifications to all contacts interested in entity #{entity_check.entity.name}"
|
296
|
+
Flapjack::Data::Event.test_notifications(
|
297
|
+
entity_check.entity_name, entity_check.check,
|
298
|
+
:summary => summary,
|
299
|
+
:redis => redis)
|
300
|
+
}
|
301
|
+
|
302
|
+
bulk_api_check_action(entities, checks, act_proc)
|
303
|
+
status 204
|
304
|
+
end
|
305
|
+
|
306
|
+
app.post '/entities' do
|
307
|
+
pass unless 'application/json'.eql?(request.content_type)
|
308
|
+
|
309
|
+
errors = []
|
310
|
+
ret = nil
|
311
|
+
|
312
|
+
# FIXME should scan for invalid records before making any changes, fail early
|
313
|
+
|
314
|
+
entities = params[:entities]
|
315
|
+
unless entities
|
316
|
+
logger.debug("no entities object found in the following supplied JSON:")
|
317
|
+
logger.debug(request.body)
|
318
|
+
return err(403, "No entities object received")
|
319
|
+
end
|
320
|
+
return err(403, "The received entities object is not an Enumerable") unless entities.is_a?(Enumerable)
|
321
|
+
return err(403, "Entity with a nil id detected") unless entities.any? {|e| !e['id'].nil?}
|
322
|
+
|
323
|
+
entities.each do |entity|
|
324
|
+
unless entity['id']
|
325
|
+
errors << "Entity not imported as it has no id: #{entity.inspect}"
|
326
|
+
next
|
327
|
+
end
|
328
|
+
Flapjack::Data::Entity.add(entity, :redis => redis)
|
329
|
+
end
|
330
|
+
errors.empty? ? 204 : err(403, *errors)
|
331
|
+
end
|
332
|
+
|
333
|
+
app.post '/entities/:entity/tags' do
|
334
|
+
content_type :json
|
335
|
+
|
336
|
+
tags = find_tags(params[:tag])
|
337
|
+
entity = find_entity(params[:entity])
|
338
|
+
entity.add_tags(*tags)
|
339
|
+
entity.tags.to_json
|
340
|
+
end
|
341
|
+
|
342
|
+
app.delete '/entities/:entity/tags' do
|
343
|
+
tags = find_tags(params[:tag])
|
344
|
+
entity = find_entity(params[:entity])
|
345
|
+
entity.delete_tags(*tags)
|
346
|
+
status 204
|
347
|
+
end
|
348
|
+
|
349
|
+
app.get '/entities/:entity/tags' do
|
350
|
+
content_type :json
|
351
|
+
|
352
|
+
entity = find_entity(params[:entity])
|
353
|
+
entity.tags.to_json
|
354
|
+
end
|
355
|
+
|
356
|
+
end
|
357
|
+
|
358
|
+
end
|
359
|
+
|
360
|
+
end
|
361
|
+
|
362
|
+
end
|
363
|
+
|
364
|
+
end
|