spectre-core 1.11.0 → 1.12.0

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