spectre-core 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,19 @@
1
+ require 'ostruct'
2
+
3
+ module Spectre
4
+ module Bag
5
+ class << self
6
+ @@bag
7
+
8
+ def bag
9
+ @@bag
10
+ end
11
+ end
12
+
13
+ Spectre.register do |config|
14
+ @@bag = OpenStruct.new
15
+ end
16
+
17
+ Spectre.delegate :bag, to: Bag
18
+ end
19
+ end
@@ -0,0 +1,368 @@
1
+ require 'open3'
2
+ require 'ostruct'
3
+
4
+
5
+ module Spectre::Curl
6
+ class SpectreHttpRequest < Spectre::DslClass
7
+ def initialize request
8
+ @__req = request
9
+ end
10
+
11
+ def method method_name
12
+ @__req['method'] = method_name.upcase
13
+ end
14
+
15
+ def url base_url
16
+ @__req['base_url'] = base_url
17
+ end
18
+
19
+ def path url_path
20
+ @__req['path'] = url_path
21
+ end
22
+
23
+ def header name, value
24
+ @__req['headers'] = [] if not @__req['headers']
25
+ @__req['headers'].append [name, value.to_s.strip]
26
+ end
27
+
28
+ def param name, value
29
+ @__req['query'] = [] if not @__req['query']
30
+ @__req['query'].append [name, value.to_s.strip]
31
+ end
32
+
33
+ def content_type media_type
34
+ @__req['headers'] = [] if not @__req['headers']
35
+ @__req['headers'].append ['Content-Type', media_type]
36
+ end
37
+
38
+ def json data
39
+ body JSON.pretty_generate(data)
40
+ content_type 'application/json'
41
+ end
42
+
43
+ def body body_content
44
+ @__req['body'] = body_content
45
+ end
46
+
47
+ def ensure_success!
48
+ @__req['ensure_success'] = true
49
+ end
50
+
51
+ def ensure_success?
52
+ @__req['ensure_success']
53
+ end
54
+
55
+ def authenticate method
56
+ @__req['auth'] = method
57
+ end
58
+
59
+ def certificate path
60
+ @__req['cert'] = path
61
+ use_ssl!
62
+ end
63
+
64
+ def use_ssl!
65
+ @__req['use_ssl'] = true
66
+ end
67
+
68
+ alias_method :auth, :authenticate
69
+ alias_method :cert, :certificate
70
+ alias_method :media_type, :content_type
71
+ end
72
+
73
+ class SpectreHttpHeader
74
+ def initialize headers
75
+ @headers = headers || {}
76
+ end
77
+
78
+ def [] key
79
+ @headers[key.downcase]
80
+ end
81
+ end
82
+
83
+ class SpectreHttpResponse
84
+ def initialize res
85
+ @res = res
86
+ @data = nil
87
+ end
88
+
89
+ def code
90
+ @res[:code]
91
+ end
92
+
93
+ def message
94
+ @res[:message]
95
+ end
96
+
97
+ def protocol
98
+ @res[:protocol]
99
+ end
100
+
101
+ def version
102
+ @res[:version]
103
+ end
104
+
105
+ def headers
106
+ SpectreHttpHeader.new @res[:headers]
107
+ end
108
+
109
+ def body
110
+ @res[:body]
111
+ end
112
+
113
+ def json
114
+ return nil if not @res[:body]
115
+
116
+ if @data == nil
117
+ begin
118
+ @data = JSON.parse(@res[:body], object_class: OpenStruct)
119
+ rescue
120
+ raise 'invalid json'
121
+ end
122
+ end
123
+
124
+ @data
125
+ end
126
+
127
+ def success?
128
+ @res[:code] < 400
129
+ end
130
+
131
+ def pretty
132
+ @res.pretty
133
+ end
134
+ end
135
+
136
+ # DEFAULT_HTTP_REQUEST = {
137
+ # 'method' => 'GET', # -X, --request <cmd>
138
+ # 'base_url' => nil,
139
+ # 'path' => nil,
140
+ # 'headers' => nil, # -H, --header <header/@file>
141
+ # 'query' => nil,
142
+ # 'body' => nil, # -d, --data <data>
143
+ # 'cert' => nil, # --cacert
144
+ # 'follow' => false, # -L, --location
145
+ # 'username' => nil, # -u, --user <user:password>
146
+ # 'password' => nil,
147
+ # 'use_ssl' => false, # -k
148
+ # }
149
+
150
+
151
+ class << self
152
+ @@http_cfg = {}
153
+ @@response = nil
154
+ @@request = nil
155
+ @@modules = []
156
+
157
+ def curl name, secure: false, &block
158
+ req = {
159
+ 'use_ssl' => secure,
160
+ }
161
+
162
+ if @@http_cfg.has_key? name
163
+ req.merge! @@http_cfg[name]
164
+ raise "No `base_url' set for HTTP client '#{name}'. Check your HTTP config in your environment." if !req['base_url']
165
+ else
166
+ req['base_url'] = name
167
+ end
168
+
169
+ SpectreHttpRequest.new(req).instance_eval(&block) if block_given?
170
+
171
+ invoke(req)
172
+ end
173
+
174
+ def curl_request
175
+ raise 'No request has been invoked yet' unless @@request
176
+ @@request
177
+ end
178
+
179
+ def curl_response
180
+ raise 'There is no response. No request has been invoked yet.' unless @@response
181
+ @@response
182
+ end
183
+
184
+ def register mod
185
+ raise 'Module must not be nil' unless mod
186
+ @@modules << mod
187
+ end
188
+
189
+ private
190
+
191
+ def try_format_json str, pretty: false
192
+ return str unless str or str.empty?
193
+
194
+ begin
195
+ json = JSON.parse str
196
+
197
+ if pretty
198
+ str = JSON.pretty_generate(json)
199
+ else
200
+ str = JSON.dump(json)
201
+ end
202
+ rescue
203
+ # do nothing
204
+ end
205
+
206
+ str
207
+ end
208
+
209
+ def invoke req
210
+ cmd = [@@curl_path]
211
+
212
+ if req['cert'] or req['use_ssl']
213
+ scheme = 'https'
214
+ else
215
+ scheme = 'http'
216
+ end
217
+
218
+ uri = req['base_url']
219
+
220
+ if not uri.match /http(?:s)?:\/\//
221
+ uri = scheme + '://' + uri
222
+ end
223
+
224
+ if req['path']
225
+ uri += '/' if !uri.end_with? '/'
226
+ uri += req['path']
227
+ end
228
+
229
+ if req['query']
230
+ uri += '?'
231
+ uri += req['query']
232
+ .map { |x| x.join '='}
233
+ .join '&'
234
+ end
235
+
236
+ cmd.append '"' + uri + '"'
237
+ cmd.append '-X', req['method'] unless req['method'] == 'GET' or (req['body'] and req['method'] == 'POST')
238
+
239
+ # Call all registered modules
240
+ @@modules.each do |mod|
241
+ mod.on_req(req, cmd) if mod.respond_to? :on_req
242
+ end
243
+
244
+ # Add headers to curl command
245
+ req['headers'].each do |header|
246
+ cmd.append '-H', '"' + header.join(':') + '"'
247
+ end if req['headers']
248
+
249
+ # Add request body
250
+ if req['body'] != nil and not req['body'].empty?
251
+ req_body = try_format_json(req['body']).gsub(/"/, '\\"')
252
+ cmd.append '-d', '"' + req_body + '"'
253
+ elsif ['POST', 'PUT', 'PATCH'].include? req['method'].upcase
254
+ cmd.append '-d', '"\n"'
255
+ end
256
+
257
+ # Add certificate path if one if given
258
+ if req['cert']
259
+ raise "Certificate '#{req['cert']}' does not exist" unless File.exists? req['cert']
260
+ cmd.append '--cacert', req['cert']
261
+ elsif req['use_ssl'] or uri.start_with? 'https'
262
+ cmd.append '-k'
263
+ end
264
+
265
+ cmd.append '-i'
266
+ cmd.append '-v'
267
+
268
+ @@request = OpenStruct.new req
269
+
270
+ sys_cmd = cmd.join ' '
271
+
272
+ @@logger.debug sys_cmd
273
+
274
+ req_id = SecureRandom.uuid()[0..5]
275
+
276
+ req_log = "[>] #{req_id} #{req['method']} #{uri}\n"
277
+ req['headers'].each do |header|
278
+ req_log += "#{header[0].to_s.ljust(30, '.')}: #{header[1].to_s}\n"
279
+ end if req['headers']
280
+ req_log += req['body'] if req['body'] != nil and not req['body'].empty?
281
+
282
+ @@logger.info req_log
283
+
284
+ start_time = Time.now
285
+
286
+ stdin, stdout, stderr, wait_thr = Open3.popen3(sys_cmd)
287
+
288
+ end_time = Time.now
289
+
290
+ output = stdout.gets(nil)
291
+ stdout.close
292
+
293
+ debug_log = stderr.gets(nil)
294
+ stderr.close
295
+
296
+ # debug_log.lines.each { |x| @@logger.debug x unless x.empty? }
297
+
298
+ raise "Unable to request #{uri}. Please check if this service is reachable." unless output
299
+
300
+ @@logger.debug "[<] #{req_id} stdout:\n#{output}"
301
+
302
+ header, body = output.split /\r?\n\r?\n/
303
+
304
+ result = header.lines.first
305
+
306
+ exit_code = wait_thr.value.exitstatus
307
+
308
+ raise Exception.new "An error occured while executing curl:\n#{debug_log.lines.map { |x| not x.empty? }}" unless exit_code == 0
309
+
310
+ # Parse protocol, version, status code and status message from response
311
+ match = /^(?<protocol>[A-Za-z0-9]+)\/(?<version>\d+\.?\d*) (?<code>\d+) (?<message>.*)/.match result
312
+
313
+ raise "Unexpected result from curl request:\n#{result}" unless match
314
+
315
+ res_headers = header.lines[1..-1]
316
+ .map { |x| /^(?<key>[A-Za-z0-9-]+):\s*(?<value>.*)$/.match x }
317
+ .select { |x| x != nil }
318
+ .map { |x| [x[:key].downcase, x[:value]] }
319
+
320
+ res = {
321
+ protocol: match[:protocol],
322
+ version: match[:version],
323
+ code: match[:code].to_i,
324
+ message: match[:message],
325
+ headers: Hash[res_headers],
326
+ body: body
327
+ }
328
+
329
+ # Call all registered modules
330
+ @@modules.each do |mod|
331
+ mod.on_res(res, output) if mod.respond_to? :on_res
332
+ end
333
+
334
+ res_log = "[<] #{req_id} #{res[:code]} #{res[:message]} (#{end_time - start_time}s)\n"
335
+ res_headers.each do |header|
336
+ res_log += "#{header[0].to_s.ljust(30, '.')}: #{header[1].to_s}\n"
337
+ end
338
+
339
+ if res[:body] != nil and not res[:body].empty?
340
+ res_log += try_format_json(res[:body], pretty: true)
341
+ end
342
+
343
+ @@logger.info res_log
344
+
345
+ @@response = SpectreHttpResponse.new res
346
+
347
+ raise "Response did not indicate success: #{@@response.code} #{@@response.message}" if req['ensure_success'] and not @@response.success?
348
+
349
+ @@response
350
+ end
351
+ end
352
+
353
+ Spectre.register do |config|
354
+ @@logger = ::Logger.new config['log_file'], progname: 'spectre/curl'
355
+
356
+ @@curl_path = config['curl_path'] || 'curl'
357
+
358
+ if config.has_key? 'http'
359
+ @@http_cfg = {}
360
+
361
+ config['http'].each do |name, cfg|
362
+ @@http_cfg[name] = cfg
363
+ end
364
+ end
365
+ end
366
+
367
+ Spectre.delegate :curl, :curl_response, :curl_request, to: self
368
+ end
@@ -0,0 +1,78 @@
1
+ require 'pg'
2
+
3
+
4
+ class PG::Result
5
+ alias :count :ntuples
6
+ end
7
+
8
+
9
+ module Spectre
10
+ module Database
11
+ module Postgres
12
+ @@modules = []
13
+
14
+ class SQLStatement
15
+ attr_accessor :query, :params
16
+
17
+ def initialize
18
+ @query = nil
19
+ @params = []
20
+ end
21
+
22
+ def statement query
23
+ @query = query
24
+ end
25
+
26
+ def param val
27
+ @params << val
28
+ end
29
+ end
30
+
31
+
32
+ class << self
33
+ def postgres name, &block
34
+ raise "postgres '#{name}' not configured" unless @@db_cfg.has_key? name
35
+
36
+ statement = SQLStatement.new
37
+ statement.instance_eval &block
38
+
39
+ cfg = @@db_cfg[name]
40
+
41
+ begin
42
+ con = PG.connect({
43
+ host: cfg['host'],
44
+ port: cfg['port'],
45
+ dbname: cfg['database'],
46
+ user: cfg['username'],
47
+ password: cfg['username'],
48
+ })
49
+
50
+ if statement.params
51
+ @@result = con.exec_params(statement.query, statement.params)
52
+ else
53
+ @@result = con.exec(statement.query)
54
+ end
55
+
56
+ ensure
57
+ con.close if con
58
+ end
59
+ end
60
+
61
+
62
+ def result
63
+ @@result
64
+ end
65
+ end
66
+
67
+ Spectre.register do |config|
68
+ @@db_cfg = {}
69
+
70
+ config['postgres'].each do |name, cfg|
71
+ @@db_cfg[name] = cfg
72
+ end
73
+ end
74
+
75
+ Spectre.delegate :postgres, :result, to: self
76
+ end
77
+ end
78
+ end