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
@@ -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