flapjack-diner 1.4.0 → 2.0.0.a4

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rspec +1 -1
  4. data/README.md +620 -413
  5. data/flapjack-diner.gemspec +1 -1
  6. data/lib/flapjack-diner/argument_validator.rb +77 -7
  7. data/lib/flapjack-diner/configuration.rb +409 -0
  8. data/lib/flapjack-diner/index_range.rb +42 -0
  9. data/lib/flapjack-diner/log_formatter.rb +22 -0
  10. data/lib/flapjack-diner/query.rb +114 -0
  11. data/lib/flapjack-diner/relationships.rb +180 -0
  12. data/lib/flapjack-diner/request.rb +280 -0
  13. data/lib/flapjack-diner/resources.rb +64 -0
  14. data/lib/flapjack-diner/response.rb +91 -0
  15. data/lib/flapjack-diner/tools.rb +47 -251
  16. data/lib/flapjack-diner/utility.rb +16 -0
  17. data/lib/flapjack-diner/version.rb +1 -1
  18. data/lib/flapjack-diner.rb +54 -20
  19. data/spec/argument_validator_spec.rb +87 -28
  20. data/spec/flapjack-diner_spec.rb +42 -64
  21. data/spec/relationships_spec.rb +211 -0
  22. data/spec/resources/checks_spec.rb +219 -79
  23. data/spec/resources/contacts_spec.rb +179 -151
  24. data/spec/resources/events_spec.rb +208 -0
  25. data/spec/resources/maintenance_periods_spec.rb +177 -565
  26. data/spec/resources/media_spec.rb +157 -171
  27. data/spec/resources/metrics_spec.rb +45 -0
  28. data/spec/resources/rules_spec.rb +278 -0
  29. data/spec/resources/states_spec.rb +93 -0
  30. data/spec/resources/statistics_spec.rb +53 -0
  31. data/spec/resources/tags_spec.rb +243 -0
  32. data/spec/spec_helper.rb +16 -0
  33. data/spec/support/fixture_data.rb +541 -0
  34. metadata +33 -31
  35. data/.rubocop.yml +0 -21
  36. data/.rubocop_todo.yml +0 -135
  37. data/lib/flapjack-diner/resources/checks.rb +0 -64
  38. data/lib/flapjack-diner/resources/contacts.rb +0 -70
  39. data/lib/flapjack-diner/resources/entities.rb +0 -68
  40. data/lib/flapjack-diner/resources/maintenance_periods.rb +0 -82
  41. data/lib/flapjack-diner/resources/media.rb +0 -61
  42. data/lib/flapjack-diner/resources/notification_rules.rb +0 -66
  43. data/lib/flapjack-diner/resources/notifications.rb +0 -28
  44. data/lib/flapjack-diner/resources/pagerduty_credentials.rb +0 -59
  45. data/lib/flapjack-diner/resources/reports.rb +0 -33
  46. data/spec/pacts/flapjack-diner-flapjack.json +0 -4515
  47. data/spec/resources/entities_spec.rb +0 -181
  48. data/spec/resources/notification_rules_spec.rb +0 -341
  49. data/spec/resources/notifications_spec.rb +0 -208
  50. data/spec/resources/pagerduty_credentials_spec.rb +0 -237
  51. data/spec/resources/reports_spec.rb +0 -255
@@ -17,6 +17,6 @@ Gem::Specification.new do |gem|
17
17
  gem.require_paths = ['lib']
18
18
  gem.version = Flapjack::Diner::VERSION
19
19
 
20
- gem.add_dependency('httparty', '>= 0.10')
20
+ gem.add_dependency('httparty', '= 0.13.5')
21
21
  gem.add_dependency('json', '>= 1.7.7')
22
22
  end
@@ -31,13 +31,21 @@ module Flapjack
31
31
  false
32
32
  end
33
33
 
34
+ def uuid(*elements)
35
+ elements.each do |element|
36
+ target = @query[element]
37
+ next if target.nil? || (target =~ /^#{Flapjack::Diner::UUID_RE}$/i)
38
+ @errors << "'#{target}' must be a RFC 4122-compliant UUID."
39
+ end
40
+ end
41
+
34
42
  def time(*elements)
35
43
  elements.each do |element|
36
44
  target = @query[element]
37
45
  next if target.nil? || target.respond_to?(:iso8601) ||
38
46
  (target.is_a?(String) && valid_time_str?(target))
39
- @errors << "'#{target}' should be a time object or ISO " \
40
- '8601-formatted string.'
47
+ @errors << "'#{target}' must be a time object or ISO " \
48
+ '8601-formatted String.'
41
49
  end
42
50
  end
43
51
 
@@ -45,23 +53,85 @@ module Flapjack
45
53
  elements.each do |element|
46
54
  target = @query[element]
47
55
  next if target.nil? || [TrueClass, FalseClass].include?(target.class)
48
- @errors << "'#{target}' should be 'true' or 'false'."
56
+ @errors << "'#{target}' must be 'true' or 'false'."
57
+ end
58
+ end
59
+
60
+ def positive_integer(*elements)
61
+ elements.each do |element|
62
+ target = @query[element]
63
+ next if target.nil? || (target.is_a?(Integer) && (target > 0))
64
+ @errors << "'#{target}' must be an Integer greater than 0."
65
+ end
66
+ end
67
+
68
+ def non_empty_string(*elements)
69
+ elements.each do |element|
70
+ target = @query[element]
71
+ next if target.nil? || (target.is_a?(String) && !target.empty?)
72
+ @errors << "'#{target}' must be a non-empty String."
49
73
  end
50
74
  end
51
75
 
52
76
  def array_of_strings(*elements)
77
+ elements.each do |element|
78
+ target = @query[element]
79
+ next if target.nil? || (target.is_a?(Array) && !target.empty? &&
80
+ target.all? {|t| t.is_a?(String) && !t.empty? })
81
+ @errors << "'#{target}' must be an Array of non-empty Strings."
82
+ end
83
+ end
84
+
85
+ def string_or_array_of_strings(*elements)
86
+ elements.each do |element|
87
+ target = @query[element]
88
+ next if target.nil? || (target.is_a?(String) && !target.empty?) ||
89
+ (target.is_a?(Array) && !target.empty? &&
90
+ target.all? {|t| t.is_a?(String) && !t.empty? })
91
+ @errors << "'#{target}' must be a non-empty String, or an Array of non-empty Strings."
92
+ end
93
+ end
94
+
95
+ def uuid_or_array_of_uuids(*elements)
96
+ elements.each do |element|
97
+ target = @query[element]
98
+ next if target.nil? || (target.is_a?(String) && (target =~ /^#{Flapjack::Diner::UUID_RE}$/i)) ||
99
+ (target.is_a?(Array) && !target.empty? &&
100
+ target.all? {|t| t.is_a?(String) && (t =~ /^#{Flapjack::Diner::UUID_RE}$/i)})
101
+ @errors << "'#{target}' must be a RFC 4122-compliant UUID, or an Array of RFC 4122-compliant UUIDs."
102
+ end
103
+ end
104
+
105
+ def hash(*elements)
106
+ elements.each do |element|
107
+ target = @query[element]
108
+ next if target.nil? || (target.is_a?(Hash) &&
109
+ target.keys.all? {|t| (t.is_a?(String) && !t.empty?) || t.is_a?(Symbol) })
110
+ @errors << "'#{target}' must be a Hash with String or Symbol keys."
111
+ end
112
+ end
113
+
114
+ def singular_link_uuid(*elements)
115
+ elements.each do |element|
116
+ target = @query[element]
117
+ next if target.nil? || (target.is_a?(String) &&
118
+ (target =~ /^#{Flapjack::Diner::UUID_RE}$/i))
119
+ @errors << "'#{target}' association must be a RFC 4122-compliant UUID."
120
+ end
121
+ end
122
+
123
+ def multiple_link_uuid(*elements)
53
124
  elements.each do |element|
54
125
  target = @query[element]
55
126
  next if target.nil? || (target.is_a?(Array) &&
56
- target.all? {|t| t.is_a?(String) })
57
- @errors << "'#{target}' should be an Array of Strings."
127
+ target.all? {|t| t =~ /^#{Flapjack::Diner::UUID_RE}$/i })
128
+ @errors << "'#{target}' association must be an Array of RFC 4122-compliant UUIDs."
58
129
  end
59
130
  end
60
131
 
61
132
  def required(*elements)
62
133
  elements.each do |element|
63
- next unless @query[element].nil?
64
- @errors << "'#{element}' is required."
134
+ @errors << "'#{element}' is required." if @query[element].nil?
65
135
  end
66
136
  end
67
137
 
@@ -0,0 +1,409 @@
1
+ require 'flapjack-diner/request'
2
+ require 'flapjack-diner/response'
3
+
4
+ require 'flapjack-diner/argument_validator'
5
+
6
+ module Flapjack
7
+ module Diner
8
+ module Configuration
9
+
10
+ RESOURCES_GET = {
11
+ [:fields, :sort, :include] => :string_or_array_of_strings,
12
+ :filter => :hash,
13
+ [:page, :per_page] => :positive_integer
14
+ }
15
+
16
+ # relationships are extracted from flapjack data models'
17
+ # "jsonapi_associations" class methods
18
+
19
+ RESOURCES = {
20
+ :acknowledgements => {
21
+ :resource => 'acknowledgement',
22
+ :requests => {
23
+ :post => {
24
+ :duration => :positive_integer,
25
+ :summary => :non_empty_string,
26
+ [:check, :tag] => :singular_link_uuid
27
+ },
28
+ },
29
+ :request_validations => {
30
+ :post => proc {
31
+ # _events_validate_association(data, 'acknowledgement')
32
+ }
33
+ },
34
+ :relationships => {
35
+ :check => {
36
+ :post => true,
37
+ :number => :singular, :link => false, :includable => false,
38
+ :resource => 'check'
39
+ },
40
+ :tag => {
41
+ :post => true,
42
+ :number => :singular, :link => false, :includable => false,
43
+ :resource => 'tag'
44
+ }
45
+ }
46
+ },
47
+
48
+ :checks => {
49
+ :resource => 'check',
50
+ :requests => {
51
+ :post => {
52
+ :id => :uuid,
53
+ :name => [:required, :non_empty_string],
54
+ :enabled => :boolean,
55
+ :tags => :multiple_link_uuid
56
+ },
57
+ :get => RESOURCES_GET,
58
+ :patch => {
59
+ :id => [:required, :uuid],
60
+ :name => :non_empty_string,
61
+ :enabled => :boolean,
62
+ :tags => :multiple_link_uuid
63
+ },
64
+ :delete => {}
65
+ },
66
+ :relationships => {
67
+ :alerting_media => {
68
+ :get => true,
69
+ :number => :multiple, :link => true, :includable => true,
70
+ :resource => 'medium'
71
+ },
72
+ :contacts => {
73
+ :get => true,
74
+ :number => :multiple, :link => true, :includable => true,
75
+ :resource => 'contact'
76
+ },
77
+ :current_scheduled_maintenances => {
78
+ :get => true,
79
+ :number => :multiple, :link => true, :includable => true,
80
+ :resource => 'scheduled_maintenance'
81
+ },
82
+ :current_state => {
83
+ :get => true,
84
+ :number => :singular, :link => true, :includable => true,
85
+ :resource => 'state'
86
+ },
87
+ :current_unscheduled_maintenance => {
88
+ :get => true,
89
+ :number => :singular, :link => true, :includable => true,
90
+ :resource => 'unscheduled_maintenance'
91
+ },
92
+ :latest_notifications => {
93
+ :get => true,
94
+ :number => :multiple, :link => true, :includable => true,
95
+ :resource => 'state'
96
+ },
97
+ :scheduled_maintenances => {
98
+ :get => true,
99
+ :number => :multiple, :link => true, :includable => false,
100
+ :resource => 'scheduled_maintenance'
101
+ },
102
+ :states => {
103
+ :get => true,
104
+ :number => :multiple, :link => true, :includable => false,
105
+ :resource => 'state'
106
+ },
107
+ :tags => {
108
+ :post => true, :get => true, :patch => true, :delete => true,
109
+ :number => :multiple, :link => true, :includable => true,
110
+ :resource => 'tag'
111
+ },
112
+ :unscheduled_maintenances => {
113
+ :get => true,
114
+ :number => :multiple, :link => true, :includable => false,
115
+ :resource => 'unscheduled_maintenance'
116
+ }
117
+ }
118
+ },
119
+
120
+ :contacts => {
121
+ :resource => 'contact',
122
+ :requests => {
123
+ :post => {
124
+ :id => :uuid,
125
+ :name => [:required, :non_empty_string],
126
+ :timezone => :non_empty_string,
127
+ :tags => :multiple_link_uuid
128
+ },
129
+ :get => RESOURCES_GET,
130
+ :patch => {
131
+ :id => [:required, :uuid],
132
+ [:name, :timezone] => :non_empty_string
133
+ },
134
+ :delete => {}
135
+ },
136
+ :relationships => {
137
+ :checks => {
138
+ :get => true,
139
+ :number => :multiple, :link => true, :includable => true,
140
+ :resource => 'check'
141
+ },
142
+ :media => {
143
+ :get => true,
144
+ :number => :multiple, :link => true, :includable => true,
145
+ :resource => 'medium'
146
+ },
147
+ :rules => {
148
+ :get => :true,
149
+ :number => :multiple, :link => true, :includable => true,
150
+ :resource => 'rule'
151
+ },
152
+ :tags => {
153
+ :post => true, :get => true, :patch => true, :delete => true,
154
+ :number => :multiple, :link => true, :includable => true,
155
+ :resource => 'tag'
156
+ }
157
+ }
158
+ },
159
+
160
+ :media => {
161
+ :resource => 'medium',
162
+ :requests => {
163
+ :post => {
164
+ :id => :uuid,
165
+ :transport => [:non_empty_string, :required],
166
+ [:address, :pagerduty_subdomain,
167
+ :pagerduty_token] => :non_empty_string,
168
+ [:interval, :rollup_threshold,
169
+ :pagerduty_ack_duration] => :positive_integer,
170
+ :contact => [:singular_link_uuid, :required],
171
+ :rules => :multiple_link_uuid
172
+ },
173
+ :get => RESOURCES_GET,
174
+ :patch => {
175
+ :id => [:uuid, :required],
176
+ [:address, :pagerduty_subdomain,
177
+ :pagerduty_token] => :non_empty_string,
178
+ [:interval, :rollup_threshold,
179
+ :pagerduty_ack_duration] => :positive_integer,
180
+ :rules => :multiple_link_uuid
181
+ },
182
+ :delete => {}
183
+ },
184
+ :relationships => {
185
+ :alerting_checks => {
186
+ :get => true,
187
+ :number => :multiple, :link => true, :includable => true,
188
+ :resource => 'check'
189
+ },
190
+ :contact => {
191
+ :post => true, :get => true,
192
+ :number => :singular, :link => true, :includable => true,
193
+ :resource => 'contact'
194
+ },
195
+ :rules => {
196
+ :post => true, :get => true, :patch => true, :delete => true,
197
+ :number => :multiple, :link => true, :includable => true,
198
+ :resource => 'rule'
199
+ }
200
+ }
201
+ },
202
+
203
+ :metrics => {
204
+ :resource => 'metrics',
205
+ :requests => {
206
+ :get => {
207
+ :fields => :string_or_array_of_strings
208
+ }
209
+ }
210
+ },
211
+
212
+ :rules => {
213
+ :resource => 'rule',
214
+ :requests => {
215
+ :post => {
216
+ :id => :uuid,
217
+ :name => :string,
218
+ :enabled => :boolean,
219
+ :blackhole => :boolean,
220
+ :strategy => :string,
221
+ :conditions_list => :string,
222
+ :time_restriction_ical => :string,
223
+ :contact => [:singular_link_uuid, :required],
224
+ [:media, :tags] => :multiple_link_uuid
225
+ },
226
+ :get => RESOURCES_GET,
227
+ :patch => {
228
+ :id => [:uuid, :required],
229
+ :name => :string,
230
+ :enabled => :boolean,
231
+ :blackhole => :boolean,
232
+ :strategy => :string,
233
+ :conditions_list => :string,
234
+ :time_restriction_ical => :string,
235
+ [:media, :tags] => :multiple_link_uuid
236
+ },
237
+ :delete => {}
238
+ },
239
+ :relationships => {
240
+ :contact => {
241
+ :post => true, :get => true,
242
+ :number => :singular, :link => true, :includable => true,
243
+ :resource => 'contact'
244
+ },
245
+ :media => {
246
+ :post => true, :get => true, :patch => true, :delete => true,
247
+ :number => :multiple, :link => true, :includable => true,
248
+ :resource => 'medium'
249
+ },
250
+ :tags => {
251
+ :post => true, :get => true, :patch => true, :delete => true,
252
+ :number => :multiple, :link => true, :includable => true,
253
+ :resource => 'tag'
254
+ }
255
+ }
256
+ },
257
+
258
+ :scheduled_maintenances => {
259
+ :resource => 'scheduled_maintenance',
260
+ :requests => {
261
+ :post => {
262
+ :id => :uuid,
263
+ [:start_time, :end_time] => [:required, :time],
264
+ :summary => :non_empty_string,
265
+ [:check, :tag] => :singular_link_uuid
266
+ },
267
+ :request_validations => {
268
+ :post => proc {
269
+ # _maintenance_periods_validate_association(data, 'scheduled maintenance period')
270
+ }
271
+ },
272
+ :patch => {
273
+ :id => [:required, :uuid],
274
+ [:start_time, :end_time] => :time
275
+ },
276
+ :delete => {}
277
+ },
278
+ :relationships => {
279
+ :check => {
280
+ :post => true, :get => true,
281
+ :number => :singular, :link => true, :includable => true,
282
+ :resource => 'check'
283
+ },
284
+ :tag => {
285
+ :post => true,
286
+ :number => :singular, :link => false, :includable => false,
287
+ :resource => 'tag'
288
+ }
289
+ }
290
+ },
291
+
292
+ :states => {
293
+ :resource => 'state',
294
+ :requests => {
295
+ :get => RESOURCES_GET
296
+ },
297
+ :relationships => {
298
+ :check => {
299
+ :get => true,
300
+ :number => :singular, :link => true, :includable => true,
301
+ :resource => 'check'
302
+ }
303
+ }
304
+ },
305
+
306
+ :statistics => {
307
+ :resource => 'statistic',
308
+ :requests => {
309
+ :get => RESOURCES_GET
310
+ }
311
+ },
312
+
313
+ :tags => {
314
+ :resource => 'tag',
315
+ :requests => {
316
+ :post => {
317
+ :id => :uuid,
318
+ :name => [:required, :non_empty_string],
319
+ [:checks, :contacts, :rules] => :multiple_link_uuid
320
+ },
321
+ :get => RESOURCES_GET,
322
+ :patch => {
323
+ :id => [:required, :uuid],
324
+ :name => :non_empty_string,
325
+ [:checks, :contacts, :rules] => :multiple_link_uuid
326
+ },
327
+ :delete => {}
328
+ },
329
+ :relationships => {
330
+ :checks => {
331
+ :post => true, :get => true, :patch => true, :delete => true,
332
+ :number => :multiple, :link => true, :includable => true,
333
+ :resource => 'check'
334
+ },
335
+ :contacts => {
336
+ :post => true, :get => true, :patch => true, :delete => true,
337
+ :number => :multiple, :link => true, :includable => true,
338
+ :resource => 'contact'
339
+ },
340
+ :rules => {
341
+ :post => true, :get => true, :patch => true, :delete => true,
342
+ :number => :multiple, :link => true, :includable => true,
343
+ :resource => 'rule'
344
+ },
345
+ :scheduled_maintenances => {
346
+ :get => true,
347
+ :number => :multiple, :link => true, :includable => false,
348
+ :resource => 'scheduled_maintenance'
349
+ },
350
+ :states => {
351
+ :get => true,
352
+ :number => :multiple, :link => true, :includable => false,
353
+ :resource => 'state'
354
+ },
355
+ :unscheduled_maintenances => {
356
+ :get => true,
357
+ :number => :multiple, :link => true, :includable => false,
358
+ :resource => 'unscheduled_maintenance'
359
+ }
360
+ }
361
+ },
362
+
363
+ :test_notifications => {
364
+ :resource => 'test_notification',
365
+ :requests => {
366
+ :post => {
367
+ :summary => :non_empty_string,
368
+ [:check, :tag] => :singular_link_uuid
369
+ }
370
+ },
371
+ :request_validations => {
372
+ :post => proc {
373
+ # _events_validate_association(data, 'test notification')
374
+ }
375
+ },
376
+ :relationships => {
377
+ :check => {
378
+ :post => true,
379
+ :number => :singular, :link => false, :includable => false,
380
+ :resource => 'check'
381
+ },
382
+ :tag => {
383
+ :post => true,
384
+ :number => :singular, :link => false, :includable => false,
385
+ :resource => 'tag'
386
+ }
387
+ }
388
+ },
389
+
390
+ :unscheduled_maintenances => {
391
+ :resource => 'unscheduled_maintenance',
392
+ :requests => {
393
+ :patch => {
394
+ :id => [:required, :uuid],
395
+ :end_time => [:required, :time]
396
+ }
397
+ },
398
+ :relationships => {
399
+ :check => {
400
+ :get => true,
401
+ :number => :singular, :link => false, :includable => true,
402
+ :resource => 'check'
403
+ }
404
+ }
405
+ }
406
+ }
407
+ end
408
+ end
409
+ end
@@ -0,0 +1,42 @@
1
+ # fairly similar to the object in Zermelo that this ends up being translated to
2
+ # by Flapjack's JSONAPI, although slightly limited in scope
3
+
4
+ module Flapjack
5
+ module Diner
6
+ class IndexRange
7
+ attr_reader :start, :finish
8
+
9
+ def initialize(start, finish, opts = {})
10
+ value_types = [Float, Date, Time, DateTime]
11
+ [start, finish].each do |v|
12
+ raise "Values must be #{value_types.join('/')}" unless v.nil? || value_types.any? {|vt| v.is_a?(vt)}
13
+ end
14
+ if !start.nil? && !finish.nil? && (start > finish)
15
+ raise "Start of range must be <= finish"
16
+ end
17
+ if start.nil? && finish.nil?
18
+ raise "Range must be bounded on at least one side"
19
+ end
20
+ @start = start
21
+ @finish = finish
22
+ end
23
+
24
+ # NB
25
+ def to_s
26
+ start_s = case @start
27
+ when Date, Time, DateTime
28
+ @start.iso8601
29
+ else
30
+ @start.to_s
31
+ end
32
+ finish_s = case @finish
33
+ when Date, Time, DateTime
34
+ @finish.iso8601
35
+ else
36
+ @finish.to_s
37
+ end
38
+ "#{start_s}..#{finish_s}"
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,22 @@
1
+ module Flapjack
2
+ module Diner
3
+ class LogFormatter #:nodoc:
4
+ TAG_NAME = Flapjack::Diner.name
5
+
6
+ attr_accessor :level, :logger, :current_time
7
+
8
+ def initialize(logger, level)
9
+ @logger = logger
10
+ @level = level.to_sym
11
+ end
12
+
13
+ def format(request, response)
14
+ current_time = Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
15
+ http_method = request.http_method.name.split("::").last.upcase
16
+ path = request.path.to_s
17
+ content_length = response.respond_to?(:headers) ? response.headers['Content-Length'] : response['Content-Length']
18
+ @logger.send @level, "[#{TAG_NAME}] [#{current_time}] #{response.code} \"#{http_method} #{path}\" #{content_length || '-'} "
19
+ end
20
+ end
21
+ end
22
+ end