flapjack-diner 1.4.0 → 2.0.0.a4

Sign up to get free protection for your applications and to get access to all the features.
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