flapjack-diner 1.0.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.rubocop.yml +21 -0
  4. data/.rubocop_todo.yml +135 -0
  5. data/Gemfile +7 -0
  6. data/README.md +86 -20
  7. data/flapjack-diner.gemspec +11 -15
  8. data/lib/flapjack-diner.rb +23 -548
  9. data/lib/flapjack-diner/argument_validator.rb +31 -22
  10. data/lib/flapjack-diner/resources/checks.rb +58 -0
  11. data/lib/flapjack-diner/resources/contacts.rb +70 -0
  12. data/lib/flapjack-diner/resources/entities.rb +68 -0
  13. data/lib/flapjack-diner/resources/maintenance_periods.rb +82 -0
  14. data/lib/flapjack-diner/resources/media.rb +61 -0
  15. data/lib/flapjack-diner/resources/notification_rules.rb +66 -0
  16. data/lib/flapjack-diner/resources/notifications.rb +27 -0
  17. data/lib/flapjack-diner/resources/pagerduty_credentials.rb +60 -0
  18. data/lib/flapjack-diner/resources/reports.rb +33 -0
  19. data/lib/flapjack-diner/tools.rb +277 -0
  20. data/lib/flapjack-diner/version.rb +1 -1
  21. data/spec/argument_validator_spec.rb +15 -15
  22. data/spec/flapjack-diner_spec.rb +58 -1275
  23. data/spec/pacts/flapjack-diner-flapjack.json +4522 -0
  24. data/spec/resources/checks_spec.rb +171 -0
  25. data/spec/resources/contacts_spec.rb +297 -0
  26. data/spec/resources/entities_spec.rb +181 -0
  27. data/spec/resources/maintenance_periods_spec.rb +603 -0
  28. data/spec/resources/media_spec.rb +277 -0
  29. data/spec/resources/notification_rules_spec.rb +341 -0
  30. data/spec/resources/notifications_spec.rb +210 -0
  31. data/spec/resources/pagerduty_credentials_spec.rb +243 -0
  32. data/spec/resources/reports_spec.rb +255 -0
  33. data/spec/spec_helper.rb +14 -2
  34. metadata +35 -72
@@ -0,0 +1,66 @@
1
+ require 'httparty'
2
+ require 'json'
3
+ require 'uri'
4
+
5
+ require 'flapjack-diner/version'
6
+ require 'flapjack-diner/argument_validator'
7
+
8
+ module Flapjack
9
+ module Diner
10
+ module Resources
11
+ module NotificationRules
12
+ def create_contact_notification_rules(*args)
13
+ ids, data = unwrap_ids(*args), unwrap_create_data(*args)
14
+ raise "'create_contact_notification_rules' requires at least one " \
15
+ 'contact id parameter' if ids.nil? || ids.empty?
16
+ validate_params(data) do
17
+ validate :query => STRING_PARAMS, :as => :array_of_strings
18
+ validate :query => BOOLEAN_PARAMS, :as => :boolean
19
+ end
20
+ perform_post("/contacts/#{escaped_ids(ids)}/notification_rules",
21
+ nil, :notification_rules => data)
22
+ end
23
+
24
+ def notification_rules(*ids)
25
+ perform_get('notification_rules', '/notification_rules', ids)
26
+ end
27
+
28
+ def update_notification_rules(*args)
29
+ ids, params = unwrap_ids(*args), unwrap_params(*args)
30
+ raise "'update_notification_rules' requires at least one " \
31
+ 'notification rule id parameter' if ids.nil? || ids.empty?
32
+ validate_params(params) do
33
+ validate :query => STRING_PARAMS, :as => :array_of_strings
34
+ validate :query => BOOLEAN_PARAMS, :as => :boolean
35
+ end
36
+ perform_patch("/notification_rules/#{escaped_ids(ids)}", nil,
37
+ update_notification_rules_ops(params))
38
+ end
39
+
40
+ def delete_notification_rules(*ids)
41
+ raise "'delete_notification_rules' requires at least one " \
42
+ 'notification rule id parameter' if ids.nil? || ids.empty?
43
+ perform_delete('/notification_rules', ids)
44
+ end
45
+
46
+ private
47
+
48
+ STRING_PARAMS = [:entities, :regex_entities, :tags, :regex_tags,
49
+ :unknown_media, :warning_media, :critical_media]
50
+ BOOLEAN_PARAMS = [:unknown_blackhole, :warning_blackhole,
51
+ :critical_blackhole]
52
+ OTHER_PARAMS = [:time_restrictions]
53
+
54
+ def update_notification_rules_ops(params)
55
+ ops = params.each_with_object([]) do |(k, v), memo|
56
+ next unless (STRING_PARAMS + BOOLEAN_PARAMS + OTHER_PARAMS).include?(k)
57
+ memo << patch_replace('notification_rules', k, v)
58
+ end
59
+ raise "'update_notification_rules' did not find any valid update " \
60
+ 'fields' if ops.empty?
61
+ ops
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,27 @@
1
+ require 'httparty'
2
+ require 'json'
3
+ require 'uri'
4
+
5
+ require 'flapjack-diner/version'
6
+ require 'flapjack-diner/argument_validator'
7
+
8
+ module Flapjack
9
+ module Diner
10
+ module Resources
11
+ module Notifications
12
+ %w(entities checks).each do |data_type|
13
+ define_method("create_test_notifications_#{data_type}") do |*args|
14
+ ids, data = unwrap_ids(*args), unwrap_create_data(*args)
15
+ raise "'create_test_notifications_#{data_type}' requires at " \
16
+ "least one #{data_type} id parameter" if ids.nil? || ids.empty?
17
+ validate_params(data) do
18
+ validate :query => :summary, :as => :string
19
+ end
20
+ perform_post("/test_notifications/#{data_type}", ids,
21
+ :test_notifications => data)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,60 @@
1
+ require 'httparty'
2
+ require 'json'
3
+ require 'uri'
4
+
5
+ require 'flapjack-diner/version'
6
+ require 'flapjack-diner/argument_validator'
7
+
8
+ module Flapjack
9
+ module Diner
10
+ module Resources
11
+ module PagerdutyCredentials
12
+ def create_contact_pagerduty_credentials(*args)
13
+ ids, data = unwrap_ids(*args), unwrap_create_data(*args)
14
+ raise "'create_contact_pagerduty_credentials' requires at least " \
15
+ 'one contact id parameter' if ids.nil? || ids.empty?
16
+ validate_params(data) do
17
+ validate :query => [:service_key], :as => [:required, :string]
18
+ end
19
+ perform_post("/contacts/#{escaped_ids(ids)}/pagerduty_credentials",
20
+ nil, :pagerduty_credentials => data)
21
+ end
22
+
23
+ def pagerduty_credentials(*ids)
24
+ perform_get('pagerduty_credentials', '/pagerduty_credentials', ids)
25
+ end
26
+
27
+ def update_pagerduty_credentials(*args)
28
+ ids, params = unwrap_ids(*args), unwrap_params(*args)
29
+ raise "'update_pagerduty_credentials' requires at least one " \
30
+ ' pagerduty_credentials id parameter' if ids.nil? || ids.empty?
31
+ validate_params(params) do
32
+ validate :query => [:service_key, :subdomain,
33
+ :username, :password], :as => :string
34
+ end
35
+ perform_patch("/pagerduty_credentials/#{escaped_ids(ids)}",
36
+ nil, update_pagerduty_credentials_ops(params))
37
+ end
38
+
39
+ def delete_pagerduty_credentials(*ids)
40
+ raise "'delete_pagerduty_credentials' requires at least one " \
41
+ 'pagerduty_credentials id parameter' if ids.nil? || ids.empty?
42
+ perform_delete('/pagerduty_credentials', ids)
43
+ end
44
+
45
+ private
46
+
47
+ def update_pagerduty_credentials_ops(params)
48
+ ops = params.each_with_object([]) do |(k, v), memo|
49
+ next unless [:service_key, :subdomain,
50
+ :username, :password].include?(k)
51
+ memo << patch_replace('pagerduty_credentials', k, v)
52
+ end
53
+ raise "'update_pagerduty_credentials' did not find any valid " \
54
+ 'update fields' if ops.empty?
55
+ ops
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,33 @@
1
+ require 'httparty'
2
+ require 'json'
3
+ require 'uri'
4
+
5
+ require 'flapjack-diner/version'
6
+ require 'flapjack-diner/argument_validator'
7
+
8
+ module Flapjack
9
+ module Diner
10
+ module Resources
11
+ module Reports
12
+ %w(entities checks).each do |data_type|
13
+ define_method("status_report_#{data_type}") do |*ids|
14
+ perform_get('status_reports', "/status_report/#{data_type}", ids)
15
+ end
16
+
17
+ %w(scheduled_maintenance unscheduled_maintenance
18
+ downtime outage).each do |report_type|
19
+ define_method("#{report_type}_report_#{data_type}") do |*args|
20
+ ids, params = unwrap_ids(*args), unwrap_params(*args)
21
+ validate_params(params) do
22
+ validate :query => [:start_time, :end_time], :as => :time
23
+ end
24
+ perform_get("#{report_type}_reports",
25
+ "/#{report_type}_report/#{data_type}",
26
+ ids, params)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,277 @@
1
+ require 'uri'
2
+
3
+ module Flapjack
4
+ module Diner
5
+ module Tools
6
+ SUCCESS_STATUS_CODES = [200, 201, 204]
7
+
8
+ attr_accessor :last_error
9
+
10
+ private
11
+
12
+ def log_request(method_type, req_uri, data = nil)
13
+ return if logger.nil? || req_uri.nil?
14
+ log_msg = "#{method_type} #{req_uri}"
15
+ unless %w(GET DELETE).include?(method_type) || data.nil?
16
+ log_msg << "\n Body: #{data.inspect}"
17
+ end
18
+ logger.info log_msg
19
+ end
20
+
21
+ def perform_get(name, path, ids = [], data = nil)
22
+ @last_error = nil
23
+ req_uri = build_uri(path, ids, data)
24
+ log_request('GET', req_uri, data)
25
+ handled = handle_response(get(req_uri.request_uri))
26
+
27
+ result = (!handled.nil? && handled.is_a?(Hash)) ? handled[name] :
28
+ handled
29
+
30
+ return_keys_as_strings.is_a?(TrueClass) ? result : symbolize(result)
31
+ end
32
+
33
+ def perform_post(path, ids = [], data = nil)
34
+ @last_error = nil
35
+ req_uri = build_uri(path, ids)
36
+ log_request('POST', req_uri, data)
37
+ opts = if data.nil?
38
+ {}
39
+ else
40
+ {:body => prepare_nested_query(data).to_json,
41
+ :headers => {'Content-Type' => 'application/vnd.api+json'}}
42
+ end
43
+ handle_response(post(req_uri.request_uri, opts))
44
+ end
45
+
46
+ def perform_patch(path, ids = [], data = nil)
47
+ @last_error = nil
48
+ req_uri = build_uri(path, ids)
49
+ log_request('PATCH', req_uri, data)
50
+ opts = if data.nil?
51
+ {}
52
+ else
53
+ {:body => prepare_nested_query(data).to_json,
54
+ :headers => {'Content-Type' => 'application/json-patch+json'}}
55
+ end
56
+ handle_response(patch(req_uri.request_uri, opts))
57
+ end
58
+
59
+ def perform_delete(path, ids = [], data = nil)
60
+ @last_error = nil
61
+ req_uri = build_uri(path, ids, data)
62
+ log_request('DELETE', req_uri, data)
63
+ handle_response(delete(req_uri.request_uri))
64
+ end
65
+
66
+ def log_response(response)
67
+ return if logger.nil? || !response.respond_to?(:code)
68
+ response_message = " Response Code: #{response.code}"
69
+ unless response.message.nil? || (response.message.eql?(''))
70
+ response_message << " #{response.message}"
71
+ end
72
+ logger.info response_message
73
+ return if response.body.nil?
74
+ logger.info " Response Body: #{response.body[0..300]}"
75
+ end
76
+
77
+ def handle_response(response)
78
+ log_response(response)
79
+ return true if 204.eql?(response.code)
80
+ parsed = if response.respond_to?(:parsed_response)
81
+ response.parsed_response
82
+ else
83
+ nil
84
+ end
85
+ return parsed if [200, 201].include?(response.code)
86
+ @last_error = handle_error(response.code, parsed)
87
+ nil
88
+ end
89
+
90
+ def handle_error(code, parsed)
91
+ case parsed
92
+ when Hash
93
+ err = {'status_code' => code}.merge(parsed)
94
+ return_keys_as_strings.is_a?(TrueClass) ? err : symbolize(err)
95
+ else
96
+ parsed
97
+ end
98
+ end
99
+
100
+ def validate_params(query = {}, &validation)
101
+ return unless block_given?
102
+ case query
103
+ when Array
104
+ query.each do |q|
105
+ ArgumentValidator.new(q).instance_eval(&validation)
106
+ end
107
+ else
108
+ ArgumentValidator.new(query).instance_eval(&validation)
109
+ end
110
+ end
111
+
112
+ # copied from Rack::Utils -- builds the query string for GETs
113
+ def build_nested_query(value, prefix = nil)
114
+ case value
115
+ when Array
116
+ build_array_query(value, prefix)
117
+ when Hash
118
+ build_hash_query(value, prefix)
119
+ else
120
+ build_data_query(value, prefix)
121
+ end
122
+ end
123
+
124
+ def build_array_query(value, prefix)
125
+ value.map {|v| build_nested_query(v, "#{prefix}[]") }.join('&')
126
+ end
127
+
128
+ def build_hash_query(value, prefix)
129
+ value.map do |k, v|
130
+ data = prefix ? "#{prefix}[#{escape(k)}]" : escape(k)
131
+ build_nested_query(v, data)
132
+ end.join('&')
133
+ end
134
+
135
+ def build_data_query(value, prefix)
136
+ if value.respond_to?(:iso8601)
137
+ raise(ArgumentError, 'Value must be a Hash') if prefix.nil?
138
+ "#{prefix}=#{escape(value.iso8601)}"
139
+ elsif value.is_a?(String) || value.is_a?(Integer)
140
+ raise(ArgumentError, 'Value must be a Hash') if prefix.nil?
141
+ "#{prefix}=#{escape(value.to_s)}"
142
+ else
143
+ prefix
144
+ end
145
+ end
146
+
147
+ def escaped_ids(ids = [])
148
+ ids.map {|id| URI.escape(id.to_s) }.join(',')
149
+ end
150
+
151
+ def escape(s)
152
+ URI.encode_www_form_component(s)
153
+ end
154
+
155
+ def unwrap_ids(*args)
156
+ args.select {|a| a.is_a?(String) || a.is_a?(Integer) }
157
+ end
158
+
159
+ def unwrap_params(*args)
160
+ args.each_with_object({}) do |e, a|
161
+ a.update(symbolize(e)) if e.is_a?(Hash)
162
+ end
163
+ end
164
+
165
+ def unwrap_data(*args)
166
+ args.select {|a| a.is_a?(Array) && a.all? {|av| av.is_a?(Hash) } }
167
+ .reduce([], &:'+')
168
+ end
169
+
170
+ def unwrap_create_data(*args)
171
+ data_h = args.select {|a| a.is_a?(Hash) }
172
+ data_a = unwrap_data(*args)
173
+
174
+ raise 'Create data may be passed as a Hash or an Array of Hashes, ' \
175
+ 'not both' unless data_h.empty? || data_a.empty?
176
+
177
+ data = data_h
178
+ data = data_a if data.empty?
179
+ data = nil if data.empty?
180
+ data
181
+ end
182
+
183
+ def patch_replace(type, k, v)
184
+ {:op => 'replace',
185
+ :path => "/#{type}/0/#{k}",
186
+ :value => v}
187
+ end
188
+
189
+ def patch_add(type, linked, v)
190
+ {:op => 'add',
191
+ :path => "/#{type}/0/links/#{linked}/-",
192
+ :value => v}
193
+ end
194
+
195
+ def patch_remove(type, linked, v)
196
+ {:op => 'remove',
197
+ :path => "/#{type}/0/links/#{linked}/#{v}"}
198
+ end
199
+
200
+ # used for the JSON data hashes in POST, PUT, DELETE
201
+ def prepare_nested_query(value)
202
+ case value
203
+ when Array
204
+ prepare_array_query(value)
205
+ when Hash
206
+ prepare_hash_query(value)
207
+ else
208
+ prepare_data_query(value)
209
+ end
210
+ end
211
+
212
+ def prepare_array_query(value)
213
+ value.map {|v| prepare_nested_query(v) }
214
+ end
215
+
216
+ def prepare_hash_query(value)
217
+ value.each_with_object({}) do |(k, v), a|
218
+ a[k] = prepare_nested_query(v)
219
+ end
220
+ end
221
+
222
+ def prepare_data_query(value)
223
+ if value.respond_to?(:iso8601)
224
+ value.iso8601
225
+ else
226
+ case value
227
+ when Integer, TrueClass, FalseClass, NilClass
228
+ value
229
+ else
230
+ value.to_s
231
+ end
232
+ end
233
+ end
234
+
235
+ def normalise_port(port_str, protocol)
236
+ if port_str.nil? || port_str.to_i < 1 || port_str.to_i > 65_535
237
+ 'https'.eql?(protocol) ? 443 : 80
238
+ else
239
+ port_str.to_i
240
+ end
241
+ end
242
+
243
+ def protocol_host_port
244
+ %r{^(?:(?<protocol>https?)://)
245
+ (?<host>[a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])
246
+ (?::(?<port>\d+))?
247
+ }ix =~ base_uri
248
+
249
+ protocol = protocol.nil? ? 'http' : protocol.downcase
250
+ [protocol, host, normalise_port(port, protocol)]
251
+ end
252
+
253
+ def build_uri(path, ids = [], params = [])
254
+ pr, ho, po = protocol_host_port
255
+ path += '/' + escaped_ids(ids) unless ids.nil? || ids.empty?
256
+ query = if params.nil? || params.empty?
257
+ nil
258
+ else
259
+ build_nested_query(params)
260
+ end
261
+ URI::HTTP.build(:protocol => pr, :host => ho, :port => po,
262
+ :path => path, :query => query)
263
+ end
264
+
265
+ def symbolize(obj)
266
+ case obj
267
+ when Hash
268
+ obj.each_with_object({}) {|(k, v), a| a[k.to_sym] = symbolize(v) }
269
+ when Array
270
+ obj.each_with_object([]) {|e, a| a << symbolize(e) }
271
+ else
272
+ obj
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end