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 +4 -4
- data/CHANGELOG.md +4 -0
- data/bin/flapper +1 -1
- data/lib/flapjack/data/contact.rb +28 -3
- data/lib/flapjack/data/entity.rb +2 -0
- data/lib/flapjack/data/notification_rule.rb +5 -1
- data/lib/flapjack/gateways/jsonapi.rb +19 -2
- data/lib/flapjack/gateways/jsonapi/contact_methods.rb +187 -253
- data/lib/flapjack/gateways/jsonapi/entity_methods.rb +0 -28
- data/lib/flapjack/gateways/jsonapi/rack/json_params_parser.rb +14 -6
- data/lib/flapjack/gateways/web.rb +1 -0
- data/lib/flapjack/gateways/web/public/img/select2-spinner.gif +0 -0
- data/lib/flapjack/gateways/web/public/js/backbone.jsonapi.js +58 -0
- data/lib/flapjack/gateways/web/public/js/contacts.js +339 -62
- data/lib/flapjack/gateways/web/views/contacts.html.erb +5 -0
- data/lib/flapjack/gateways/web/views/edit_contacts.html.erb +94 -38
- data/lib/flapjack/version.rb +1 -1
- data/spec/lib/flapjack/gateways/jsonapi/contact_methods_spec.rb +24 -536
- data/spec/lib/flapjack/gateways/jsonapi/entity_methods_spec.rb +1 -104
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c18eef4f4dd8297baab83f9f8c1ac1eb3972ace
|
4
|
+
data.tar.gz: b62c3482d22ddb71af95b7cb5c097fa9ea75bdf4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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" => {
|
492
|
+
"links" => {
|
493
|
+
:entities => @linked_entity_ids || [],
|
494
|
+
:media => @linked_media_ids || []
|
495
|
+
}
|
471
496
|
}.to_json
|
472
497
|
end
|
473
498
|
|
data/lib/flapjack/data/entity.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
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
|
-
|
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
|
145
|
-
content_type
|
136
|
+
app.get %r{/contacts(?:/)?([^/]+)?$} do
|
137
|
+
content_type 'application/vnd.api+json'
|
146
138
|
cors_headers
|
147
139
|
|
148
|
-
|
149
|
-
|
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.
|
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
|
-
|
168
|
-
|
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
|
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.
|
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.
|
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
|
-
|
225
|
-
|
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
|
-
|
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
|