spectre-core 1.11.0 → 1.12.0

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.
data/lib/spectre/http.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require_relative '../spectre'
2
+
1
3
  require 'net/http'
2
4
  require 'openssl'
3
5
  require 'json'
@@ -5,7 +7,6 @@ require 'securerandom'
5
7
  require 'logger'
6
8
  require 'ostruct'
7
9
 
8
-
9
10
  module Spectre
10
11
  module Http
11
12
  DEFAULT_HTTP_CONFIG = {
@@ -19,11 +20,16 @@ module Spectre
19
20
  'headers' => nil,
20
21
  'query' => nil,
21
22
  'content_type' => '',
22
- }
23
+ }.freeze
23
24
 
24
25
  @@modules = []
25
26
 
26
27
  class SpectreHttpRequest < Spectre::DslClass
28
+ class Headers
29
+ CONTENT_TYPE = 'Content-Type'
30
+ UNIQUE_HEADERS = [CONTENT_TYPE].freeze
31
+ end
32
+
27
33
  def initialize request
28
34
  @__req = request
29
35
  end
@@ -40,25 +46,30 @@ module Spectre
40
46
  @__req['path'] = url_path
41
47
  end
42
48
 
49
+ def timeout seconds
50
+ @__req['timeout'] = seconds
51
+ end
52
+
43
53
  def header name, value
44
- @__req['headers'] = [] if not @__req['headers']
54
+ @__req['headers'] ||= []
45
55
  @__req['headers'].append [name, value.to_s.strip]
46
56
  end
47
57
 
48
58
  def param name, value
49
- @__req['query'] = [] if not @__req['query']
59
+ @__req['query'] ||= []
50
60
  @__req['query'].append [name, value.to_s.strip]
51
61
  end
52
62
 
53
63
  def content_type media_type
54
- @__req['headers'] = [] if not @__req['headers']
55
- @__req['headers'].append ['Content-Type', media_type]
64
+ @__req['content_type'] = media_type
56
65
  end
57
66
 
58
67
  def json data
59
68
  data = data.to_h if data.is_a? OpenStruct
60
69
  body JSON.pretty_generate(data)
61
- content_type 'application/json'
70
+
71
+ # TODO: Only set content type, if not explicitly set
72
+ content_type('application/json')
62
73
  end
63
74
 
64
75
  def body body_content
@@ -77,9 +88,12 @@ module Spectre
77
88
  @__req['auth'] = method
78
89
  end
79
90
 
91
+ def no_auth!
92
+ @__req['auth'] = 'none'
93
+ end
94
+
80
95
  def certificate path
81
96
  @__req['cert'] = path
82
- use_ssl!
83
97
  end
84
98
 
85
99
  def use_ssl!
@@ -101,7 +115,8 @@ module Spectre
101
115
  end
102
116
 
103
117
  def [] key
104
- return nil if not @headers.has_key?(key.downcase)
118
+ return nil unless @headers.key?(key.downcase)
119
+
105
120
  @headers[key.downcase].first
106
121
  end
107
122
 
@@ -111,52 +126,30 @@ module Spectre
111
126
  end
112
127
 
113
128
  class SpectreHttpResponse
114
- def initialize res
115
- @res = res
116
- @data = nil
117
- @headers = SpectreHttpHeader.new @res[:headers]
118
- end
119
-
120
- def code
121
- @res[:code]
122
- end
123
-
124
- def message
125
- @res[:message]
126
- end
129
+ attr_reader :code, :message, :headers, :body
127
130
 
128
- def headers
129
- @headers
130
- end
131
-
132
- def body
133
- @res[:body]
131
+ def initialize net_res
132
+ @code = net_res.code.to_i
133
+ @message = net_res.message
134
+ @body = net_res.body
135
+ @headers = SpectreHttpHeader.new(net_res.to_hash)
136
+ @json_data = nil
134
137
  end
135
138
 
136
139
  def json
137
- return nil if not @res[:body]
138
-
139
- if @data == nil
140
+ if !@body.nil? and @json_data.nil?
140
141
  begin
141
- @data = JSON.parse(@res[:body], object_class: OpenStruct)
142
- rescue
143
- raise "Body content is not a valid JSON:\n#{@res[:body]}"
142
+ @json_data = JSON.parse(@body, object_class: OpenStruct)
143
+ rescue JSON::ParserError
144
+ raise "Body content is not a valid JSON:\n#{@body}"
144
145
  end
145
146
  end
146
147
 
147
- @data
148
+ @json_data
148
149
  end
149
150
 
150
151
  def success?
151
- @res[:code] < 400
152
- end
153
-
154
- def to_s
155
- @res.to_s
156
- end
157
-
158
- def pretty
159
- @res.pretty
152
+ @code < 400
160
153
  end
161
154
  end
162
155
 
@@ -172,35 +165,38 @@ module Spectre
172
165
  http(name, secure: true, &block)
173
166
  end
174
167
 
175
- def http name, secure: nil, &block
168
+ def http name, secure: false, &block
176
169
  req = {}
177
170
 
178
171
  if @@http_cfg.key? name
179
- req.merge! @@http_cfg[name]
180
- raise "No `base_url' set for HTTP client '#{name}'. Check your HTTP config in your environment." if !req['base_url']
172
+ req.deep_merge! @@http_cfg[name].deep_clone
173
+ raise "No `base_url' set for HTTP client '#{name}'. Check your HTTP config in your environment." unless req['base_url']
181
174
  else
182
175
  req['base_url'] = name
183
176
  end
184
177
 
185
- req['user_ssl'] = secure if secure != nil
178
+ req['use_ssl'] = secure unless secure.nil?
186
179
 
187
- SpectreHttpRequest.new(req).instance_eval(&block) if block_given?
180
+ SpectreHttpRequest.new(req)._evaluate(&block) if block_given?
188
181
 
189
182
  invoke(req)
190
183
  end
191
184
 
192
185
  def request
193
186
  raise 'No request has been invoked yet' unless @@request
187
+
194
188
  @@request
195
189
  end
196
190
 
197
191
  def response
198
192
  raise 'There is no response. No request has been invoked yet.' unless @@response
193
+
199
194
  @@response
200
195
  end
201
196
 
202
197
  def register mod
203
198
  raise 'Module must not be nil' unless mod
199
+
204
200
  @@modules << mod
205
201
  end
206
202
 
@@ -211,7 +207,7 @@ module Spectre
211
207
 
212
208
  begin
213
209
  json = JSON.parse(str)
214
- json.obfuscate!(@@secure_keys) if not @@debug
210
+ json.obfuscate!(@@secure_keys) unless @@debug
215
211
 
216
212
  if pretty
217
213
  str = JSON.pretty_generate(json)
@@ -225,14 +221,14 @@ module Spectre
225
221
  str
226
222
  end
227
223
 
228
- def is_secure? key
229
- @@secure_keys.any? { |x| key.to_s.downcase.include? x.downcase }
224
+ def secure? key
225
+ @@secure_keys.any? { |x| key.to_s.downcase.include? x.downcase }
230
226
  end
231
227
 
232
228
  def header_to_s headers
233
229
  s = ''
234
230
  headers.each_header.each do |header, value|
235
- value = '*****' if is_secure?(header) and not @@debug
231
+ value = '*****' if secure?(header) and not @@debug
236
232
  s += "#{header.to_s.ljust(30, '.')}: #{value.to_s}\n"
237
233
  end
238
234
  s
@@ -241,36 +237,39 @@ module Spectre
241
237
  def invoke req
242
238
  @@request = nil
243
239
 
244
- if req['cert'] or req['use_ssl']
245
- scheme = 'https'
246
- else
247
- scheme = 'http'
248
- end
240
+ # Build URI
249
241
 
242
+ scheme = req['use_ssl'] ? 'https' : 'http'
250
243
  base_url = req['base_url']
251
244
 
252
- if not base_url.match /http(?:s)?:\/\//
245
+ unless base_url.match /http(?:s)?:\/\//
253
246
  base_url = scheme + '://' + base_url
254
247
  end
255
248
 
256
249
  if req['path']
257
- base_url = base_url + '/' if not base_url.end_with? '/'
250
+ base_url = base_url + '/' unless base_url.end_with? '/'
258
251
  base_url += req['path']
259
252
  end
260
253
 
261
254
  uri = URI(base_url)
262
255
 
263
- raise "'#{uri}' is not a valid uri" if not uri.host
256
+ raise "'#{uri}' is not a valid uri" unless uri.host
257
+
258
+ # Build query parameters
264
259
 
265
260
  uri.query = URI.encode_www_form(req['query']) unless not req['query'] or req['query'].empty?
266
261
 
262
+ # Create HTTP client
263
+
267
264
  net_http = Net::HTTP.new(uri.host, uri.port)
265
+ net_http.read_timeout = req['timeout'] || 180
268
266
 
269
267
  if uri.scheme == 'https'
270
268
  net_http.use_ssl = true
271
269
 
272
270
  if req.key? 'cert'
273
271
  raise "Certificate '#{req['cert']}' does not exist" unless File.exists? req['cert']
272
+
274
273
  net_http.verify_mode = OpenSSL::SSL::VERIFY_PEER
275
274
  net_http.ca_file = req['cert']
276
275
  else
@@ -278,6 +277,8 @@ module Spectre
278
277
  end
279
278
  end
280
279
 
280
+ # Create HTTP Request
281
+
281
282
  net_req = Net::HTTPGenericRequest.new(req['method'], true, true, uri)
282
283
  net_req.body = req['body']
283
284
  net_req.content_type = req['content_type'] if req['content_type'] and not req['content_type'].empty?
@@ -302,14 +303,23 @@ module Spectre
302
303
  req_log += header_to_s(net_req)
303
304
  req_log += try_format_json(req['body'], pretty: true) if req['body'] != nil and not req['body'].empty?
304
305
 
305
- @@logger.info req_log
306
+ @@logger.info(req_log)
306
307
 
307
308
  # Request
308
309
 
309
310
  start_time = Time.now
310
- net_res = net_http.request(net_req)
311
+
312
+ begin
313
+ net_res = net_http.request(net_req)
314
+ rescue SocketError => e
315
+ raise "The request '#{req['method']} #{uri}' failed. Please check if the given URL '#{uri}' is valid and available or a corresponding HTTP config in the environment file exists. See log for more details. Original.\nOriginal error was: #{e.message}"
316
+ end
317
+
311
318
  end_time = Time.now
312
319
 
320
+ req['started_at'] = start_time
321
+ req['finished_at'] = end_time
322
+
313
323
  # Run HTTP modules
314
324
 
315
325
  @@modules.each do |mod|
@@ -320,27 +330,23 @@ module Spectre
320
330
 
321
331
  res_log = "[<] #{req_id} #{net_res.code} #{net_res.message} (#{end_time - start_time}s)\n"
322
332
  res_log += header_to_s(net_res)
323
- res_log += try_format_json(net_res.body, pretty: true) if net_res.body != nil and !net_res.body.empty?
333
+ res_log += try_format_json(net_res.body, pretty: true) unless net_res.body.nil? or net_res.body.empty?
324
334
 
325
335
  @@logger.info(res_log)
326
336
 
327
- if req['ensure_success']
328
- code = Integer(net_res.code)
329
- fail "Response code of #{req_id} did not indicate success: #{net_res.code} #{net_res.message}" if code >= 400
330
- end
337
+ fail "Response code of #{req_id} did not indicate success: #{net_res.code} #{net_res.message}" if req['ensure_success'] and net_res.code.to_i >= 400
338
+
339
+ # Set global request and response variables
331
340
 
332
341
  @@request = OpenStruct.new(req)
333
- @@response = SpectreHttpResponse.new({
334
- code: net_res.code.to_i,
335
- message: net_res.message,
336
- headers: net_res.to_hash,
337
- body: net_res.body
338
- })
342
+ @@request.freeze
343
+
344
+ @@response = SpectreHttpResponse.new(net_res)
339
345
  end
340
346
  end
341
347
 
342
348
  Spectre.register do |config|
343
- @@logger = ::Logger.new config['log_file'], progname: 'spectre/http'
349
+ @@logger = ::Logger.new(config['log_file'], progname: 'spectre/http')
344
350
  @@secure_keys = config['secure_keys'] || []
345
351
  @@debug = config['debug']
346
352
 
@@ -355,4 +361,4 @@ module Spectre
355
361
 
356
362
  Spectre.delegate :http, :https, :request, :response, to: self
357
363
  end
358
- end
364
+ end
@@ -1,142 +1,143 @@
1
- require 'ectoplasm'
2
-
3
- module Spectre
4
- module Logger
5
- class Console
6
- def initialize config
7
- raise 'No log format section in config for console logger' unless config.key? 'log_format' and config['log_format'].key? 'console'
8
-
9
- @config = config['log_format']['console']
10
- @indent = @config['indent'] || 2
11
- @width = @config['width'] || 80
12
- @fmt_end_context = @config['end_context']
13
- @fmt_sep = @config['separator']
14
- @fmt_start_group = @config['start_group']
15
- @fmt_end_group = @config['end_group']
16
-
17
- @process = nil
18
- @level = 0
19
- end
20
-
21
- def start_subject subject
22
- puts subject.desc.blue
23
- end
24
-
25
- def start_context context
26
- return unless context.__desc
27
- puts (' ' * indent) + context.__desc.magenta
28
- @level += 1
29
- end
30
-
31
- def end_context context
32
- return unless context.__desc
33
- @level -= 1
34
- puts (' ' * indent) + @fmt_end_context.gsub('<desc>', context.__desc).magenta if @fmt_end_context
35
- end
36
-
37
- def start_spec spec, data=nil
38
- text = spec.desc
39
- text += " with #{data}" if data
40
- puts (' ' * indent) + text.cyan
41
-
42
- @level += 1
43
- end
44
-
45
- def end_spec spec, data
46
- @level -= 1
47
- end
48
-
49
- def log_separator desc
50
- if desc
51
- desc = @fmt_sep.gsub('<indent>', ' ' * indent).gsub('<desc>', desc) if @fmt_sep
52
- puts desc.blue
53
- else
54
- puts
55
- end
56
- end
57
-
58
- def start_group desc
59
- desc = @fmt_start_group.gsub('<desc>', desc) if @fmt_start_group
60
- puts (' ' * indent) + desc.blue
61
- @level += 1
62
- end
63
-
64
- def end_group desc
65
- if desc and @fmt_start_group
66
- desc = @fmt_start_group.gsub('<desc>', desc) if @fmt_start_group
67
- puts (' ' * indent) + desc.blue
68
- end
69
-
70
- @level -= 1
71
- end
72
-
73
- def log_process desc
74
- print_line(desc)
75
- @process = desc
76
- @level += 1
77
- end
78
-
79
- def log_status desc, status, annotation=nil
80
- status = status.green if status == Status::OK
81
- status = status.blue if status == Status::INFO
82
- status = status.grey if status == Status::DEBUG
83
- status = status.red if status == Status::FAILED
84
- status = status.red if status == Status::ERROR
85
- status = status.grey if status == Status::SKIPPED
86
-
87
- txt = status
88
- txt += ' ' + annotation if annotation
89
-
90
- @level -= 1
91
-
92
- if @process
93
- puts txt
94
- else
95
- print_line('', status)
96
- end
97
-
98
- @process = nil
99
- end
100
-
101
- def log_info message
102
- print_line(message, Status::INFO.blue)
103
- end
104
-
105
- def log_debug message
106
- print_line(message, Status::DEBUG.grey)
107
- end
108
-
109
- def log_error spec, exception
110
- txt = (Status::ERROR + ' - ' + exception.class.name).red
111
- print_line('', txt)
112
- end
113
-
114
- def log_skipped spec
115
- print_line('', Status::SKIPPED.grey)
116
- end
117
-
118
- private
119
-
120
- def indent
121
- (@level+1) * @indent
122
- end
123
-
124
- def print_line text='', status=nil
125
- puts if @process
126
-
127
- ind = indent
128
- line = (' ' * indent) + text
129
- remaining = @width - text.length - ind
130
- line += '.' * (@width - text.length - ind) if remaining > 0
131
-
132
- print line
133
-
134
- if status
135
- puts status
136
- @process = nil
137
- end
138
- end
139
-
140
- end
141
- end
142
- end
1
+ require 'ectoplasm'
2
+
3
+ module Spectre
4
+ module Logger
5
+ class Console
6
+ def initialize config
7
+ raise 'No log format section in config for console logger' unless config.key? 'log_format' and config['log_format'].key? 'console'
8
+
9
+ @config = config['log_format']['console']
10
+ @indent = @config['indent'] || 2
11
+ @width = @config['width'] || 80
12
+ @fmt_end_context = @config['end_context']
13
+ @fmt_sep = @config['separator']
14
+ @fmt_start_group = @config['start_group']
15
+ @fmt_end_group = @config['end_group']
16
+
17
+ @process = nil
18
+ @level = 0
19
+ end
20
+
21
+ def start_subject subject
22
+ puts subject.desc.blue
23
+ end
24
+
25
+ def start_context context
26
+ return unless context.__desc
27
+
28
+ puts (' ' * indent) + context.__desc.magenta
29
+ @level += 1
30
+ end
31
+
32
+ def end_context context
33
+ return unless context.__desc
34
+
35
+ @level -= 1
36
+ puts (' ' * indent) + @fmt_end_context.gsub('<desc>', context.__desc).magenta if @fmt_end_context
37
+ end
38
+
39
+ def start_spec spec, data=nil
40
+ text = spec.desc
41
+ text += " with #{data}" if data
42
+ puts (' ' * indent) + text.cyan
43
+
44
+ @level += 1
45
+ end
46
+
47
+ def end_spec _spec, _data
48
+ @level -= 1
49
+ end
50
+
51
+ def log_separator desc
52
+ if desc
53
+ desc = @fmt_sep.gsub('<indent>', ' ' * indent).gsub('<desc>', desc) if @fmt_sep
54
+ puts desc.blue
55
+ else
56
+ puts
57
+ end
58
+ end
59
+
60
+ def start_group desc
61
+ desc = @fmt_start_group.gsub('<desc>', desc) if @fmt_start_group
62
+ puts (' ' * indent) + desc.blue
63
+ @level += 1
64
+ end
65
+
66
+ def end_group desc
67
+ if desc and @fmt_start_group
68
+ desc = @fmt_start_group.gsub('<desc>', desc) if @fmt_start_group
69
+ puts (' ' * indent) + desc.blue
70
+ end
71
+
72
+ @level -= 1
73
+ end
74
+
75
+ def log_process desc
76
+ print_line(desc)
77
+ @process = desc
78
+ @level += 1
79
+ end
80
+
81
+ def log_status _desc, status, annotation=nil
82
+ status = status.green if status == Status::OK
83
+ status = status.blue if status == Status::INFO
84
+ status = status.grey if status == Status::DEBUG
85
+ status = status.red if status == Status::FAILED
86
+ status = status.red if status == Status::ERROR
87
+ status = status.grey if status == Status::SKIPPED
88
+
89
+ txt = status
90
+ txt += ' ' + annotation if annotation
91
+
92
+ @level -= 1
93
+
94
+ if @process
95
+ puts txt
96
+ else
97
+ print_line('', status)
98
+ end
99
+
100
+ @process = nil
101
+ end
102
+
103
+ def log_info message
104
+ print_line(message, Status::INFO.blue)
105
+ end
106
+
107
+ def log_debug message
108
+ print_line(message, Status::DEBUG.grey)
109
+ end
110
+
111
+ def log_error _spec, exception
112
+ txt = (Status::ERROR + ' - ' + exception.class.name).red
113
+ print_line('', txt)
114
+ end
115
+
116
+ def log_skipped _spec
117
+ print_line('', Status::SKIPPED.grey)
118
+ end
119
+
120
+ private
121
+
122
+ def indent
123
+ (@level+1) * @indent
124
+ end
125
+
126
+ def print_line text='', status=nil
127
+ puts if @process
128
+
129
+ ind = indent
130
+ line = (' ' * indent) + text
131
+ remaining = @width - text.length - ind
132
+ line += '.' * (@width - text.length - ind) if remaining > 0
133
+
134
+ print line
135
+
136
+ if status
137
+ puts status
138
+ @process = nil
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -93,4 +93,4 @@ module Spectre
93
93
  end
94
94
  end
95
95
  end
96
- end
96
+ end
@@ -1,3 +1,4 @@
1
+ require_relative '../spectre'
1
2
  require 'date'
2
3
 
3
4
  module Spectre
@@ -98,6 +99,7 @@ module Spectre
98
99
 
99
100
  def log_debug message
100
101
  return unless @@debug
102
+
101
103
  add_log(message)
102
104
  delegate(:log_debug, message)
103
105
  end
@@ -141,4 +143,4 @@ module Spectre
141
143
 
142
144
  Spectre.delegate :log, :info, :debug, :group, :separate, to: self
143
145
  end
144
- end
146
+ end