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
@@ -0,0 +1,91 @@
1
+ require 'flapjack-diner/utility'
2
+
3
+ module Flapjack
4
+ module Diner
5
+ class Response
6
+ SUCCESS_STATUS_CODES = [200, 201, 204]
7
+
8
+ attr_reader :output, :context, :error
9
+
10
+ def initialize(resp, opts = {})
11
+ @response = resp
12
+ @output = nil
13
+ @context = nil
14
+ @error = nil
15
+ @return_keys_as_strings = Flapjack::Diner.return_keys_as_strings
16
+ end
17
+
18
+ def process
19
+ if 204.eql?(@response.code)
20
+ @output = true
21
+ @context = nil
22
+ @error = nil
23
+ return
24
+ end
25
+ if @response.respond_to?(:parsed_response)
26
+ parsed = @response.parsed_response
27
+ end
28
+ strify = @return_keys_as_strings.is_a?(TrueClass)
29
+ if [200, 201].include?(@response.code)
30
+ @output = handle_data(parsed, strify)
31
+ return
32
+ end
33
+ @error = handle_errors(parsed, strify)
34
+ end
35
+
36
+ private
37
+
38
+ def flatten_jsonapi_data(data)
39
+ ret = nil
40
+ case data
41
+ when Array
42
+ ret = data.inject([]) do |memo, d|
43
+ attrs = d['attributes'] || {}
44
+ d.each_pair do |k, v|
45
+ next if 'attributes'.eql?(k)
46
+ attrs.update(k => v)
47
+ end
48
+ memo += [attrs]
49
+ memo
50
+ end
51
+ when Hash
52
+ ret = data['attributes'] || {}
53
+ data.each_pair do |k, v|
54
+ next if 'attributes'.eql?(k)
55
+ ret.update(k => v)
56
+ end
57
+ else
58
+ ret = data
59
+ end
60
+ ret
61
+ end
62
+
63
+ def handle_data(parsed, strify)
64
+ return parsed if parsed.nil? || !parsed.is_a?(Hash) ||
65
+ !parsed.key?('data')
66
+ @context = {}
67
+ c_incl_key = strify ? 'included' : :included
68
+ if parsed.key?('included')
69
+ incl = flatten_jsonapi_data(parsed['included'])
70
+ @context[c_incl_key] = incl.each_with_object({}) do |i, memo|
71
+ memo[i['type']] ||= {}
72
+ memo[i['type']][i['id']] = strify ? i : Flapjack::Diner::Utility.symbolize(i)
73
+ end
74
+ end
75
+ (%w(relationships meta) & parsed.keys).each do |k|
76
+ c = parsed[k]
77
+ @context[strify ? k : k.to_sym] = (strify ? c : Flapjack::Diner::Utility.symbolize(c))
78
+ end
79
+ ret = flatten_jsonapi_data(parsed['data'])
80
+ strify ? ret : Flapjack::Diner::Utility.symbolize(ret)
81
+ end
82
+
83
+ def handle_errors(parsed, strify)
84
+ return parsed if parsed.nil? || !parsed.is_a?(Hash) ||
85
+ !parsed.key?('errors')
86
+ errs = parsed['errors']
87
+ strify ? errs : Flapjack::Diner::Utility.symbolize(errs)
88
+ end
89
+ end
90
+ end
91
+ end
@@ -1,276 +1,72 @@
1
- require 'uri'
2
-
3
1
  module Flapjack
4
2
  module Diner
5
3
  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}"
4
+ module ClassMethods
5
+ def included_data
6
+ return if context.nil?
7
+ context[return_keys_as_strings ? 'included' : :included]
17
8
  end
18
- logger.info log_msg
19
- end
20
9
 
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))
10
+ def related(record, rel, incl = included_data)
11
+ return if incl.nil?
26
12
 
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
13
+ type = record[return_keys_as_strings ? 'type' : :type]
14
+ return if type.nil?
45
15
 
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)
16
+ res = Flapjack::Diner::Configuration::RESOURCES.values.detect do |r|
17
+ type.eql?(r[:resource])
106
18
  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
19
+ return if res.nil? || res[:relationships].nil?
134
20
 
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
21
+ rel_cfg = res[:relationships][rel.to_sym]
22
+ return if rel_cfg.nil?
146
23
 
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)
24
+ rel_type = rel_cfg[:resource]
25
+ has_data = incl.key?(rel_type)
26
+ case rel_cfg[:number]
27
+ when :singular
28
+ has_data ? singularly_related(record, rel, rel_type, incl) : nil
29
+ else
30
+ has_data ? multiply_related(record, rel, rel_type, incl) : []
31
+ end
162
32
  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
33
 
189
- def patch_add(type, linked, v)
190
- {:op => 'add',
191
- :path => "/#{type}/0/links/#{linked}/-",
192
- :value => v}
193
- end
34
+ private
194
35
 
195
- def patch_remove(type, linked, v)
196
- {:op => 'remove',
197
- :path => "/#{type}/0/links/#{linked}/#{v}"}
198
- end
36
+ def singularly_related(record, rel, type, incl)
37
+ relat, data, id_a, rel = related_accessors(rel)
38
+ return if record[relat].nil? ||
39
+ record[relat][rel].nil? ||
40
+ record[relat][rel][data].nil?
199
41
 
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)
42
+ id = record[relat][rel][data][id_a]
43
+ return if id.nil? || !incl.key?(type)
44
+ incl[type][id]
209
45
  end
210
- end
211
-
212
- def prepare_array_query(value)
213
- value.map {|v| prepare_nested_query(v) }
214
- end
215
46
 
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
47
+ def multiply_related(record, rel, type, incl)
48
+ relat, data, id_a, rel = related_accessors(rel)
49
+ return [] if record[relat].nil? ||
50
+ record[relat][rel].nil? ||
51
+ record[relat][rel][data].nil? ||
52
+ record[relat][rel][data].empty?
221
53
 
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
54
+ ids = record[relat][rel][data].map {|m| m[id_a] }
55
+ return [] if ids.empty? || !incl.key?(type)
56
+ incl[type].values_at(*ids)
232
57
  end
233
- end
234
58
 
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
59
+ def related_accessors(*args)
60
+ acc = [:relationships, :data, :id]
61
+ return (acc + args).map(&:to_s) if return_keys_as_strings
62
+ (acc + args.map(&:to_sym))
240
63
  end
241
64
  end
242
65
 
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
66
+ def self.included(base)
67
+ base.extend ClassMethods
68
+ # base.class_eval do
69
+ # end
274
70
  end
275
71
  end
276
72
  end
@@ -0,0 +1,16 @@
1
+ module Flapjack
2
+ module Diner
3
+ module Utility
4
+ def self.symbolize(obj)
5
+ case obj
6
+ when Hash
7
+ obj.each_with_object({}) {|(k, v), a| a[k.to_sym] = symbolize(v) }
8
+ when Array
9
+ obj.each_with_object([]) {|e, a| a << symbolize(e) }
10
+ else
11
+ obj
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,5 +1,5 @@
1
1
  module Flapjack
2
2
  module Diner
3
- VERSION = '1.4.0'
3
+ VERSION = '2.0.0.a4'
4
4
  end
5
5
  end
@@ -1,41 +1,75 @@
1
1
  require 'httparty'
2
2
  require 'json'
3
3
 
4
+ require 'flapjack-diner/log_formatter'
4
5
  require 'flapjack-diner/version'
5
6
  require 'flapjack-diner/argument_validator'
7
+ require 'flapjack-diner/index_range'
6
8
 
7
- %w(contacts media pagerduty_credentials notification_rules entities checks
8
- maintenance_periods notifications reports).each do |resource|
9
+ require 'flapjack-diner/resources'
10
+ require 'flapjack-diner/relationships'
11
+ require 'flapjack-diner/tools'
9
12
 
10
- require "flapjack-diner/resources/#{resource}"
11
- end
13
+ # HTTParty master contains a non-hacky way of doing this, but 0.13.5 doesn't
14
+ module HTTParty
15
+ module Logger
16
+ def self.build(logger, level, formatter)
17
+ level ||= :info
18
+ formatter ||= :apache
12
19
 
13
- require 'flapjack-diner/tools'
20
+ case formatter
21
+ when :'flapjack-diner'
22
+ Flapjack::Diner::LogFormatter.new(logger, level)
23
+ when :curl
24
+ Logger::CurlLogger.new(logger, level)
25
+ else
26
+ Logger::ApacheLogger.new(logger, level)
27
+ end
28
+ end
29
+ end
30
+ end
14
31
 
15
32
  # NB: clients will need to handle any exceptions caused by,
16
33
  # e.g., network failures or non-parseable JSON data.
17
-
18
34
  module Flapjack
35
+
19
36
  # Top level module for Flapjack::Diner API consumer.
20
37
  module Diner
21
- include HTTParty
38
+ UUID_RE = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"
22
39
 
40
+ include HTTParty
23
41
  format :json
24
42
 
25
43
  class << self
26
- attr_accessor :logger, :return_keys_as_strings
27
-
28
- include Flapjack::Diner::Resources::Contacts
29
- include Flapjack::Diner::Resources::Media
30
- include Flapjack::Diner::Resources::PagerdutyCredentials
31
- include Flapjack::Diner::Resources::NotificationRules
32
- include Flapjack::Diner::Resources::Entities
33
- include Flapjack::Diner::Resources::Checks
34
- include Flapjack::Diner::Resources::MaintenancePeriods
35
- include Flapjack::Diner::Resources::Notifications
36
- include Flapjack::Diner::Resources::Reports
37
-
38
- include Flapjack::Diner::Tools
44
+ attr_accessor :return_keys_as_strings
45
+
46
+ # redefine HTTParty logger methods for getter/setter style
47
+ alias :original_logger :logger
48
+ def logger=(lgr)
49
+ original_logger(lgr, :info, :'flapjack-diner')
50
+ end
51
+ def logger
52
+ default_options[:logger]
53
+ end
54
+
55
+ def output
56
+ return if !instance_variable_defined?('@response') || @response.nil?
57
+ @response.output
58
+ end
59
+
60
+ def context
61
+ return if !instance_variable_defined?('@response') || @response.nil?
62
+ @response.context
63
+ end
64
+
65
+ def error
66
+ return if !instance_variable_defined?('@response') || @response.nil?
67
+ @response.error
68
+ end
39
69
  end
70
+
71
+ include Flapjack::Diner::Resources
72
+ include Flapjack::Diner::Relationships
73
+ include Flapjack::Diner::Tools
40
74
  end
41
75
  end