flapjack 0.8.4 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|