windcave_rest 0.1.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.
@@ -0,0 +1,54 @@
1
+ module WindcaveRest
2
+ class TransactionsApi < WindcaveRest::BaseApi
3
+ # Create a Transaction
4
+ # Creates a Windcave Transaction. During the Transaction process a customer can apply for credit decisioning in real-time. This means the Transaction needs to represent a good picture of known customer details along with order information and the Transaction entity represents this as a resource. For more information on how to Transaction with Windcave see the https://px5.docs.apiary.io/#reference/0/transactions/create-Transaction guide.
5
+ # @param [Hash] opts the optional parameters
6
+ # @option opts [CreateTransactionRequest] :body
7
+ # @return [Transaction]
8
+ def transactions_create(opts = {})
9
+ data, _status_code, _headers = transactions_create_with_http_info(opts)
10
+ return data
11
+ end
12
+
13
+ # Create a Transaction
14
+ # Creates a Windcave Transaction. During the Transaction process a customer can apply for credit decisioning in real-time. This means the Transaction needs to represent a good picture of known customer details along with order information and the Transaction entity represents this as a resource. For more information on how to Transaction with Windcave see the #model:Z2QcrzRGHACY8wM6G guide.
15
+ # @param [Hash] opts the optional parameters
16
+ # @option opts [CreateTransactionRequest] :body
17
+ # @return [Array<(Transaction, Fixnum, Hash)>] Transaction data, response status code and response headers
18
+ def transactions_create_with_http_info(opts = {})
19
+ if @api_client.config.debugging
20
+ @api_client.config.logger.debug "Calling API: TransactionsApi.transactions_create ..."
21
+ end
22
+ # resource path
23
+ local_var_path = "/transactions"
24
+
25
+ # query parameters
26
+ query_params = {}
27
+
28
+ # header parameters
29
+ header_params = {}
30
+ # HTTP header 'Accept' (if needed)
31
+ header_params['Accept'] = @api_client.select_header_accept(['application/json'])
32
+ # HTTP header 'Content-Type'
33
+ header_params['Content-Type'] = @api_client.select_header_content_type(['application/json'])
34
+
35
+ # form parameters
36
+ form_params = {}
37
+
38
+ # http body (model)
39
+ post_body = @api_client.object_to_http_body(opts[:'body'])
40
+ auth_names = ['Authorization']
41
+ data, status_code, headers = @api_client.call_api(:POST, local_var_path,
42
+ :header_params => header_params,
43
+ :query_params => query_params,
44
+ :form_params => form_params,
45
+ :body => post_body,
46
+ :auth_names => auth_names,
47
+ :return_type => 'Transaction')
48
+ if @api_client.config.debugging
49
+ @api_client.config.logger.debug "API called: TransactionsApi#transactions_create\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
50
+ end
51
+ return data, status_code, headers
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,381 @@
1
+ require 'date'
2
+ require 'json'
3
+ require 'logger'
4
+ require 'tempfile'
5
+ require 'typhoeus'
6
+ require 'uri'
7
+
8
+ module WindcaveRest
9
+ class ApiClient
10
+ # The Configuration object holding settings to be used in the API client.
11
+ attr_accessor :config
12
+
13
+ # Defines the headers to be used in HTTP requests of all API calls by default.
14
+ #
15
+ # @return [Hash]
16
+ attr_accessor :default_headers
17
+
18
+ # Initializes the ApiClient
19
+ # @option config [Configuration] Configuration for initializing the object, default to Configuration.default
20
+ def initialize(config = Configuration.default)
21
+ @config = config
22
+
23
+ @user_agent = @config.user_agent
24
+ if @config.platform
25
+ @user_agent = @config.platform + "/" + @config.user_agent
26
+ end
27
+
28
+ @default_headers = {
29
+ 'Content-Type' => "application/json",
30
+ 'User-Agent' => @user_agent,
31
+ 'X-Client-Version' => @config.api_version
32
+ }
33
+ end
34
+
35
+ def self.default
36
+ @@default ||= ApiClient.new
37
+ end
38
+
39
+ # Call an API with given options.
40
+ #
41
+ # @return [Array<(Object, Fixnum, Hash)>] an array of 3 elements:
42
+ # the data deserialized from response body (could be nil), response status code and response headers.
43
+ def call_api(http_method, path, opts = {})
44
+ request = build_request(http_method, path, opts)
45
+ count = 0
46
+ @response = nil
47
+
48
+ loop do
49
+ # Retry after specified retry interval
50
+ sleep(@config.retry_interval) if count > 0 && @config.retry_interval > 0
51
+ @config.logger.debug "Running the request. Count #{count+1}" if @config.debugging
52
+ @response = request.run
53
+ count = count + 1
54
+ break if (( count >= @config.num_retries && @response.code == 0 && @response.return_message == "Couldn't connect to server") || @response.code > 0 || opts[:header_params][:'Idempotency-Key'].nil?)
55
+ end
56
+
57
+ if @config.debugging
58
+ @config.logger.debug "HTTP response body ~BEGIN~\n#{@response.body}\n~END~\n"
59
+ end
60
+
61
+ unless @response.success?
62
+ if @response.timed_out?
63
+ fail ApiError.new('Connection timed out')
64
+ elsif @response.code == 0
65
+ # Errors from libcurl will be made visible here
66
+ fail ApiError.new(:code => 0,
67
+ :message => @response.return_message)
68
+ else
69
+ fail ApiError.new(:code => @response.code,
70
+ :response_headers => @response.headers,
71
+ :response_body => @response.body),
72
+ @response.status_message
73
+ end
74
+ end
75
+
76
+ if opts[:return_type]
77
+ data = deserialize(@response, opts[:return_type])
78
+ else
79
+ data = nil
80
+ end
81
+ return data, @response.code, @response.headers
82
+ end
83
+
84
+ # Builds the HTTP request
85
+ #
86
+ # @param [String] http_method HTTP method/verb (e.g. POST)
87
+ # @param [String] path URL path (e.g. /account/new)
88
+ # @option opts [Hash] :header_params Header parameters
89
+ # @option opts [Hash] :query_params Query parameters
90
+ # @option opts [Hash] :form_params Query parameters
91
+ # @option opts [Object] :body HTTP body (JSON/XML)
92
+ # @return [Typhoeus::Request] A Typhoeus Request
93
+ def build_request(http_method, path, opts = {})
94
+ url = build_request_url(path)
95
+ http_method = http_method.to_sym.downcase
96
+
97
+ header_params = @default_headers.merge(opts[:header_params] || {})
98
+ query_params = opts[:query_params] || {}
99
+ form_params = opts[:form_params] || {}
100
+
101
+ update_params_for_auth! header_params, query_params, opts[:auth_names]
102
+
103
+ # set ssl_verifyhosts option based on @config.verify_ssl_host (true/false)
104
+ _verify_ssl_host = @config.verify_ssl_host ? 2 : 0
105
+
106
+ req_opts = {
107
+ :method => http_method,
108
+ :headers => header_params,
109
+ :params => query_params,
110
+ :params_encoding => @config.params_encoding,
111
+ :timeout => @config.timeout,
112
+ :ssl_verifypeer => @config.verify_ssl,
113
+ :ssl_verifyhost => _verify_ssl_host,
114
+ :sslcert => @config.cert_file,
115
+ :sslkey => @config.key_file,
116
+ :verbose => @config.debugging
117
+ }
118
+
119
+ # set custom cert, if provided
120
+ req_opts[:cainfo] = @config.ssl_ca_cert if @config.ssl_ca_cert
121
+
122
+ if [:post, :patch, :put, :delete].include?(http_method)
123
+ req_body = build_request_body(header_params, form_params, opts[:body])
124
+ req_opts.update :body => req_body
125
+ if @config.debugging
126
+ @config.logger.debug "HTTP request body param ~BEGIN~\n#{req_body}\n~END~\n"
127
+ end
128
+ end
129
+
130
+ Typhoeus::Request.new(url, req_opts)
131
+ end
132
+
133
+ # Check if the given MIME is a JSON MIME.
134
+ # JSON MIME examples:
135
+ # application/json
136
+ # application/json; charset=UTF8
137
+ # APPLICATION/JSON
138
+ # */*
139
+ # @param [String] mime MIME
140
+ # @return [Boolean] True if the MIME is application/json
141
+ def json_mime?(mime)
142
+ (mime == "*/*") || !(mime =~ /\Aapplication\/json(;.*)?\z/i).nil?
143
+ end
144
+
145
+ # Deserialize the response to the given return type.
146
+ #
147
+ # @param [Response] response HTTP response
148
+ # @param [String] return_type some examples: "User", "Array[User]", "Hash[String,Integer]"
149
+ def deserialize(response, return_type)
150
+ body = response.body
151
+ return nil if body.nil? || body.empty?
152
+
153
+ # return response body directly for String return type
154
+ return body if return_type == 'String'
155
+
156
+ # handle file downloading - save response body into a tmp file and return the File instance
157
+ return download_file(response) if return_type == 'File'
158
+
159
+ # ensuring a default content type
160
+ content_type = response.headers['Content-Type'] || 'application/json'
161
+
162
+ fail "Content-Type is not supported: #{content_type}" unless json_mime?(content_type)
163
+
164
+ begin
165
+ data = JSON.parse("[#{body}]", :symbolize_names => true)[0]
166
+ rescue JSON::ParserError => e
167
+ if %w(String Date DateTime).include?(return_type)
168
+ data = body
169
+ else
170
+ raise e
171
+ end
172
+ end
173
+
174
+ convert_to_type data, return_type
175
+ end
176
+
177
+ # Convert data to the given return type.
178
+ # @param [Object] data Data to be converted
179
+ # @param [String] return_type Return type
180
+ # @return [Mixed] Data in a particular type
181
+ def convert_to_type(data, return_type)
182
+ return nil if data.nil?
183
+ case return_type
184
+ when 'String'
185
+ data.to_s
186
+ when 'Integer'
187
+ data.to_i
188
+ when 'Float'
189
+ data.to_f
190
+ when 'BOOLEAN'
191
+ data == true
192
+ when 'DateTime'
193
+ # parse date time (expecting ISO 8601 format)
194
+ DateTime.parse data
195
+ when 'Date'
196
+ # parse date time (expecting ISO 8601 format)
197
+ Date.parse data
198
+ when 'Object'
199
+ # generic object (usually a Hash), return directly
200
+ data
201
+ when /\AArray<(.+)>\z/
202
+ # e.g. Array<Pet>
203
+ sub_type = $1
204
+ data.map {|item| convert_to_type(item, sub_type) }
205
+ when /\AHash\<String, (.+)\>\z/
206
+ # e.g. Hash<String, Integer>
207
+ sub_type = $1
208
+ {}.tap do |hash|
209
+ data.each {|k, v| hash[k] = convert_to_type(v, sub_type) }
210
+ end
211
+ else
212
+ # models, e.g. Pet
213
+ WindcaveRest.const_get(return_type).new.tap do |model|
214
+ model.build_from_hash data
215
+ end
216
+ end
217
+ end
218
+
219
+ # Save response body into a file in (the defined) temporary folder, using the filename
220
+ # from the "Content-Disposition" header if provided, otherwise a random filename.
221
+ #
222
+ # @see Configuration#temp_folder_path
223
+ # @return [Tempfile] the file downloaded
224
+ def download_file(response)
225
+ content_disposition = response.headers['Content-Disposition']
226
+ if content_disposition and content_disposition =~ /filename=/i
227
+ filename = content_disposition[/filename=['"]?([^'"\s]+)['"]?/, 1]
228
+ prefix = sanitize_filename(filename)
229
+ else
230
+ prefix = 'download-'
231
+ end
232
+ prefix = prefix + '-' unless prefix.end_with?('-')
233
+
234
+ tempfile = nil
235
+ encoding = response.body.encoding
236
+ Tempfile.open(prefix, @config.temp_folder_path, encoding: encoding) do |file|
237
+ file.write(response.body)
238
+ tempfile = file
239
+ end
240
+ @config.logger.info "Temp file written to #{tempfile.path}, please copy the file to a proper folder "\
241
+ "with e.g. `FileUtils.cp(tempfile.path, '/new/file/path')` otherwise the temp file "\
242
+ "will be deleted automatically with GC. It's also recommended to delete the temp file "\
243
+ "explicitly with `tempfile.delete`"
244
+ tempfile
245
+ end
246
+
247
+ # Sanitize filename by removing path.
248
+ # e.g. ../../sun.gif becomes sun.gif
249
+ #
250
+ # @param [String] filename the filename to be sanitized
251
+ # @return [String] the sanitized filename
252
+ def sanitize_filename(filename)
253
+ filename.gsub(/.*[\/\\]/, '')
254
+ end
255
+
256
+ def build_request_url(path)
257
+ # Add leading and trailing slashes to path
258
+ path = "/#{path}".gsub(/\/+/, '/')
259
+ URI.encode(@config.base_url + path)
260
+ end
261
+
262
+ # Builds the HTTP request body
263
+ #
264
+ # @param [Hash] header_params Header parameters
265
+ # @param [Hash] form_params Query parameters
266
+ # @param [Object] body HTTP body (JSON/XML)
267
+ # @return [String] HTTP body data in the form of string
268
+ def build_request_body(header_params, form_params, body)
269
+ # http form
270
+ if header_params['Content-Type'] == 'application/x-www-form-urlencoded' ||
271
+ header_params['Content-Type'] == 'multipart/form-data'
272
+ data = {}
273
+ form_params.each do |key, value|
274
+ case value
275
+ when File, Array, nil
276
+ # let typhoeus handle File, Array and nil parameters
277
+ data[key] = value
278
+ else
279
+ data[key] = value.to_s
280
+ end
281
+ end
282
+ elsif body
283
+ data = body.is_a?(String) ? body : body.to_json
284
+ else
285
+ data = nil
286
+ end
287
+ data
288
+ end
289
+
290
+ # Update hearder and query params based on authentication settings.
291
+ #
292
+ # @param [Hash] header_params Header parameters
293
+ # @param [Hash] query_params Query parameters
294
+ # @param [String] auth_names Authentication scheme name
295
+ def update_params_for_auth!(header_params, query_params, auth_names)
296
+ Array(auth_names).each do |auth_name|
297
+ auth_setting = @config.auth_settings[auth_name]
298
+ next unless auth_setting
299
+ case auth_setting[:in]
300
+ when 'header' then header_params[auth_setting[:key]] = auth_setting[:value]
301
+ when 'query' then query_params[auth_setting[:key]] = auth_setting[:value]
302
+ else fail ArgumentError, 'Authentication token must be in `query` of `header`'
303
+ end
304
+ end
305
+ end
306
+
307
+ # Sets user agent in HTTP header
308
+ #
309
+ # @param [String] user_agent User agent (e.g. merchantapi/ruby/1.0.0)
310
+ def user_agent=(user_agent)
311
+ @user_agent = user_agent
312
+ @default_headers['User-Agent'] = @user_agent
313
+ end
314
+
315
+ # Return Accept header based on an array of accepts provided.
316
+ # @param [Array] accepts array for Accept
317
+ # @return [String] the Accept header (e.g. application/json)
318
+ def select_header_accept(accepts)
319
+ return nil if accepts.nil? || accepts.empty?
320
+ # use JSON when present, otherwise use all of the provided
321
+ json_accept = accepts.find { |s| json_mime?(s) }
322
+ return json_accept || accepts.join(',')
323
+ end
324
+
325
+ # Return Content-Type header based on an array of content types provided.
326
+ # @param [Array] content_types array for Content-Type
327
+ # @return [String] the Content-Type header (e.g. application/json)
328
+ def select_header_content_type(content_types)
329
+ # use application/json by default
330
+ return 'application/json' if content_types.nil? || content_types.empty?
331
+ # use JSON when present, otherwise use the first one
332
+ json_content_type = content_types.find { |s| json_mime?(s) }
333
+ return json_content_type || content_types.first
334
+ end
335
+
336
+ # Convert object (array, hash, object, etc) to JSON string.
337
+ # @param [Object] model object to be converted into JSON string
338
+ # @return [String] JSON string representation of the object
339
+ def object_to_http_body(model)
340
+ return model if model.nil? || model.is_a?(String)
341
+ local_body = nil
342
+ if model.is_a?(Array)
343
+ local_body = model.map{|m| object_to_hash(m) }
344
+ else
345
+ local_body = object_to_hash(model)
346
+ end
347
+ local_body.to_json
348
+ end
349
+
350
+ # Convert object(non-array) to hash.
351
+ # @param [Object] obj object to be converted into JSON string
352
+ # @return [String] JSON string representation of the object
353
+ def object_to_hash(obj)
354
+ if obj.respond_to?(:to_hash)
355
+ obj.to_hash
356
+ else
357
+ obj
358
+ end
359
+ end
360
+
361
+ # Build parameter value according to the given collection format.
362
+ # @param [String] collection_format one of :csv, :ssv, :tsv, :pipes and :multi
363
+ def build_collection_param(param, collection_format)
364
+ case collection_format
365
+ when :csv
366
+ param.join(',')
367
+ when :ssv
368
+ param.join(' ')
369
+ when :tsv
370
+ param.join("\t")
371
+ when :pipes
372
+ param.join('|')
373
+ when :multi
374
+ # return the array directly as typhoeus will handle it as expected
375
+ param
376
+ else
377
+ fail "unknown collection format: #{collection_format.inspect}"
378
+ end
379
+ end
380
+ end
381
+ end
@@ -0,0 +1,26 @@
1
+ module WindcaveRest
2
+ class ApiError < StandardError
3
+ attr_reader :code, :response_headers, :response_body
4
+
5
+ # Usage examples:
6
+ # ApiError.new
7
+ # ApiError.new("message")
8
+ # ApiError.new(:code => 500, :response_headers => {}, :response_body => "")
9
+ # ApiError.new(:code => 404, :message => "Not Found")
10
+ def initialize(arg = nil)
11
+ if arg.is_a? Hash
12
+ if arg.key?(:message) || arg.key?('message')
13
+ super(arg[:message] || arg['message'])
14
+ else
15
+ super arg
16
+ end
17
+
18
+ arg.each do |k, v|
19
+ instance_variable_set "@#{k}", v
20
+ end
21
+ else
22
+ super arg
23
+ end
24
+ end
25
+ end
26
+ end