minfraud 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,36 +4,34 @@ require 'faraday'
4
4
 
5
5
  module Minfraud
6
6
  module HTTPService
7
+ # Request performs HTTP requests.
7
8
  class Request
8
- # @attribute middleware
9
- # @return [Proc] A proc containing Faraday configuration
9
+ # A proc containing Faraday configuration.
10
+ #
11
+ # @return [Proc, nil]
10
12
  attr_reader :middleware
11
13
 
12
- # @attribute server
13
- # @return [String] an API endpoint
14
+ # The API endpoint.
15
+ #
16
+ # @return [String, nil]
14
17
  attr_reader :server
15
18
 
16
- # Creates Minfraud::HTTPService::Request instance
17
- # @param [Hash] params hash of parameters
18
- # @return [Minfraud::HTTPService::Request] Request instance
19
+ # @param params [Hash] Hash of parameters. Each key/value should
20
+ # correspond to one of the available attributes.
19
21
  def initialize(params = {})
20
22
  @middleware = params[:middleware]
21
23
  @server = params[:server]
22
24
  end
23
25
 
24
- # Performs an HTTP request to the specified endpoint with given body
25
- # @param [Hash] params hash of parameters.
26
- # @return [Farday::Response] Faraday::Response instance
26
+ # Perform an HTTP request to the specified endpoint with given body.
27
+ #
28
+ # @param params [Hash] Hash of parameters, including +:verb+,
29
+ # +:endpoint+, and +:body+.
30
+ #
31
+ # @return [Farday::Response]
27
32
  def perform(params)
28
- adapter.send(*params.values_at(:verb, :endpoint, :body))
29
- end
30
-
31
- private
32
-
33
- # Creates memoized Faraday::Connection instance
34
- # @return [Faraday::Connection] Faraday::Connection instance
35
- def adapter
36
- @adapter ||= Faraday.new(server, {}, &middleware)
33
+ connection = Minfraud.connection
34
+ connection.send(*params.values_at(:verb, :endpoint, :body))
37
35
  end
38
36
  end
39
37
  end
@@ -7,24 +7,26 @@ require 'minfraud/model/score'
7
7
 
8
8
  module Minfraud
9
9
  module HTTPService
10
- # Response class for HTTP requests
10
+ # Response class for HTTP requests.
11
11
  class Response
12
- # @attribute status
13
- # @return [Integer] HTTP response status
12
+ # HTTP response status.
13
+ #
14
+ # @return [Integer, nil]
14
15
  attr_reader :status
15
16
 
16
- # @attribute body
17
+ # HTTP response model.
18
+ #
17
19
  # @return [Minfraud::Model::Score, Minfraud::Model::Insights,
18
- # Minfraud::Model::Factors] HTTP response body
20
+ # Minfraud::Model::Factors, nil]
19
21
  attr_reader :body
20
22
 
21
- # @attribute headers
22
- # @return [Hash] HTTP response headers
23
+ # HTTP response headers.
24
+ #
25
+ # @return [Hash, nil]
23
26
  attr_reader :headers
24
27
 
25
- # Creates Minfraud::HTTPService::Response instance
26
- # @param [Hash] params hash of parameters
27
- # @return [Minfraud::HTTPService::Response] Response instance
28
+ # @param params [Hash] Hash of parameters. +:status+, +:endpoint+,
29
+ # +:body+, +:locales+, and +:headers+ are used.
28
30
  def initialize(params = {})
29
31
  @status = params[:status]
30
32
  @body = make_body(
@@ -35,8 +37,9 @@ module Minfraud
35
37
  @headers = params[:headers]
36
38
  end
37
39
 
38
- # Returns minFraud-specific response code
39
- # @return [Symbol, nil] minFraud specific request code
40
+ # Return the minFraud-specific response code.
41
+ #
42
+ # @return [Symbol, nil]
40
43
  def code
41
44
  return nil if body.nil?
42
45
 
@@ -61,6 +61,7 @@ module Minfraud
61
61
 
62
62
  # The risk associated with the device. If present, this is a value in the
63
63
  # range of 0.01 to 99.
64
+ #
64
65
  # @return [Float, nil]
65
66
  attr_reader :device
66
67
 
@@ -79,6 +80,7 @@ module Minfraud
79
80
  # The risk associated with the email address local part (the part of
80
81
  # the email address before the @ symbol). If present, this is a value
81
82
  # in the range 0.01 to 99.
83
+ #
82
84
  # @return [Float, nil]
83
85
  attr_reader :email_local_part
84
86
 
@@ -124,6 +126,7 @@ module Minfraud
124
126
 
125
127
  # The risk associated with the shipping address. If present, this is a
126
128
  # value in the range 0.01 to 99.
129
+ #
127
130
  # @return [Float, nil]
128
131
  attr_reader :shipping_address
129
132
 
@@ -1,38 +1,56 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Minfraud
4
+ # Report is used to perform minFraud Report Transaction API requests.
5
+ #
6
+ # @see https://dev.maxmind.com/minfraud/report-transaction/
4
7
  class Report
5
8
  include ::Minfraud::HTTPService
6
9
 
7
- # @!attribute transaction
8
- # @return [Minfraud::Components::Report::Transaction] Report::Transaction component
10
+ # The Report::Transaction component.
11
+ #
12
+ # @return [Minfraud::Components::Report::Transaction, nil]
9
13
  attr_accessor :transaction
10
14
 
11
- # @param [Hash] params hash of parameters
12
- # @return [Minfraud::ReportTransaction] ReportTransaction instance
15
+ # @param params [Hash] Hash of parameters. The only supported key is
16
+ # +:transaction+, which should have a
17
+ # +Minfraud::Components::Report::Transaction+ as its value.
13
18
  def initialize(params = {})
14
19
  @transaction = params[:transaction]
15
20
  end
16
21
 
17
- # @method report_transaction
18
- # Makes a request to the minFraud report transactions API.
19
- # Raises an error in case of invalid response.
22
+ # Perform a request to the minFraud Report Transaction API.
23
+ #
20
24
  # @return [nil]
25
+ #
26
+ # @raise [Minfraud::AuthorizationError] If there was an authentication
27
+ # problem.
28
+ #
29
+ # @raise [Minfraud::ClientError] If there was a critical problem with one
30
+ # of your inputs.
31
+ #
32
+ # @raise [Minfraud::ServerError] If the server reported an error of some
33
+ # kind.
21
34
  def report_transaction
22
- raw = request.perform(verb: :post, endpoint: 'transactions/report', body: @transaction.to_json)
35
+ raw = request.perform(
36
+ verb: :post,
37
+ endpoint: 'transactions/report',
38
+ body: @transaction.to_json,
39
+ )
23
40
 
24
41
  response = ::Minfraud::HTTPService::Response.new(
25
42
  status: raw.status.to_i,
26
43
  body: raw.body,
27
44
  headers: raw.headers
28
45
  )
46
+
29
47
  ::Minfraud::ErrorHandler.examine(response)
30
48
 
31
49
  nil
32
50
  end
33
51
 
34
- # Creates memoized Minfraud::HTTPService::Request instance
35
- # @return [Minfraud::HTTPService::Request] Request instance based on configuration params
52
+ private
53
+
36
54
  def request
37
55
  @request ||= Request.new(::Minfraud::HTTPService.configuration)
38
56
  end
@@ -1,12 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Minfraud
4
+ # Resolver provides functionality for setting component attributes.
4
5
  module Resolver
5
6
  class << self
6
- # @param [Object] context an object for variable assignment
7
- # @param [Hash] params a hash of parameters
8
- # @return [Array] a list of supplied params
9
- # @note Raises RequestFormatError once unpermitted key is met
7
+ # Set keys on the context based on the provided parameters.
8
+ #
9
+ # @param context [Object] An object for variable assignment.
10
+ #
11
+ # @param params [Hash] A hash of parameters.
12
+ #
13
+ # @return [Array]
14
+ #
15
+ # @raise [Minfraud::RequestFormatError] If an unexpected key is found.
10
16
  def assign(context, params)
11
17
  Array(params).each do |key, value|
12
18
  raise RequestFormatError, "#{key} does not belong to request document format" unless MAPPING[key]
@@ -17,7 +23,7 @@ module Minfraud
17
23
  end
18
24
  end
19
25
 
20
- # Mapping between components & minFraud request keys
26
+ # @!visibility private
21
27
  MAPPING = {
22
28
  account: ::Minfraud::Components::Account,
23
29
  billing: ::Minfraud::Components::Billing,
@@ -0,0 +1,187 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+ require 'ipaddr'
5
+ require 'uri'
6
+
7
+ # rubocop:disable Metrics/ModuleLength
8
+ module Minfraud
9
+ # @!visibility private
10
+ module Validates
11
+ def validate_string(field, length, value)
12
+ return if !value
13
+
14
+ if value.to_s.length > length
15
+ raise InvalidInputError, "The #{field} value is not valid. The maximum length is #{length}."
16
+ end
17
+ end
18
+
19
+ def validate_md5(field, value)
20
+ return if !value
21
+
22
+ if !value.to_s.match(/\A[a-fA-F0-9]{32}\z/)
23
+ raise InvalidInputError, "The #{field} value is not valid. It must be an MD5 hash as a string."
24
+ end
25
+ end
26
+
27
+ def validate_subdivision_code(field, value)
28
+ return if !value
29
+
30
+ if !value.to_s.match(/\A[0-9A-Z]{1,4}\z/)
31
+ raise InvalidInputError, "The #{field} value is not valid. It must be an ISO 3166-2 subdivision code."
32
+ end
33
+ end
34
+
35
+ def validate_country_code(field, value)
36
+ return if !value
37
+
38
+ if !value.to_s.match(/\A[A-Z]{2}\z/)
39
+ raise InvalidInputError, "The #{field} value is not valid. It must be an ISO 3166-1 alpha-2 country code."
40
+ end
41
+ end
42
+
43
+ def validate_telephone_country_code(field, value)
44
+ return if !value
45
+
46
+ if !value.to_s.match(/\A[0-9]{1,4}\z/)
47
+ raise InvalidInputError, "The #{field} value is not valid. It must be at most 4 digits."
48
+ end
49
+ end
50
+
51
+ def validate_regex(field, regex, value)
52
+ return if !value
53
+
54
+ if !regex.match(value.to_s)
55
+ raise InvalidInputError, "The #{field} value is not valid. It must match the pattern #{regex}."
56
+ end
57
+ end
58
+
59
+ def validate_credit_card_token(field, value)
60
+ return if !value
61
+
62
+ s = value.to_s
63
+
64
+ if !/\A[\x21-\x7E]{1,255}\z/.match(s)
65
+ raise InvalidInputError, "The #{field} value is not valid. It must contain only non-space printable ASCII characters."
66
+ end
67
+
68
+ if /\A[0-9]{1,19}\z/.match(s)
69
+ raise InvalidInputError, "The #{field} value is not valid. If it is all digits, it must be longer than 19 characters."
70
+ end
71
+ end
72
+
73
+ def validate_custom_input_value(field, value)
74
+ return if !value
75
+
76
+ if [true, false].include?(value)
77
+ return
78
+ end
79
+
80
+ if value.is_a? Numeric
81
+ if value < -1e13 + 1 || value > 1e13 - 1
82
+ raise InvalidInputError, "The #{field} value is not valid. Numeric values must be between -1e13 and 1e13."
83
+ end
84
+
85
+ return
86
+ end
87
+
88
+ validate_string(field, 255, value)
89
+ end
90
+
91
+ def validate_ip(field, value)
92
+ return if !value
93
+
94
+ s = value.to_s
95
+ if s.include? '/'
96
+ raise InvalidInputError, "The #{field} value is not valid. It must be an individual IP address."
97
+ end
98
+
99
+ # rubocop:disable Style/RescueStandardError
100
+ begin
101
+ IPAddr.new(s)
102
+ rescue
103
+ raise InvalidInputError, "The #{field} value is not valid. It must be an IPv4 or IPv6 address."
104
+ end
105
+ # rubocop:enable Style/RescueStandardError
106
+
107
+ nil
108
+ end
109
+
110
+ def validate_nonnegative_number(field, value)
111
+ return if !value
112
+
113
+ if !value.is_a? Numeric
114
+ raise InvalidInputError, "The #{field} value is not valid. It must be numeric."
115
+ end
116
+
117
+ if value < 0 || value > 1e13 - 1
118
+ raise InvalidInputError, "The #{field} value is not valid. It must be at least 0 and at most 1e13 - 1."
119
+ end
120
+ end
121
+
122
+ def validate_nonnegative_integer(field, value)
123
+ return if !value
124
+
125
+ if !value.is_a? Integer
126
+ raise InvalidInputError, "The #{field} is not valid. It must be an integer."
127
+ end
128
+
129
+ if value < 0 || value > 1e13 - 1
130
+ raise InvalidInputError, "The #{field} is not valid. It must be at least 0 and at most 1e13 - 1."
131
+ end
132
+ end
133
+
134
+ def validate_email(field, value)
135
+ return if !value
136
+
137
+ if /.@./.match(value)
138
+ validate_string(field, 255, value)
139
+ return
140
+ end
141
+
142
+ validate_md5(field, value)
143
+ end
144
+
145
+ def validate_rfc3339(field, value)
146
+ return if !value
147
+
148
+ # rubocop:disable Style/RescueStandardError
149
+ begin
150
+ DateTime.rfc3339(value)
151
+ rescue
152
+ raise InvalidInputError, "The #{field} value is not valid. It must be in the RFC 3339 date-time format."
153
+ end
154
+ # rubocop:enable Style/RescueStandardError
155
+
156
+ nil
157
+ end
158
+
159
+ def validate_boolean(field, value)
160
+ return if !value
161
+
162
+ if ![false, true].include? value
163
+ raise InvalidInputError, "The #{field} value is not valid. It must be boolean."
164
+ end
165
+ end
166
+
167
+ def validate_uri(field, value)
168
+ return if !value
169
+
170
+ if value.to_s.length > 1_024
171
+ raise InvalidInputError, "The #{field} value is not valid. It must not exceed 1024 characters."
172
+ end
173
+
174
+ # rubocop:disable Style/RescueStandardError
175
+ begin
176
+ u = URI(value)
177
+ if !u.scheme
178
+ raise InvalidInputError
179
+ end
180
+ rescue
181
+ raise InvalidInputError, "The #{field} value is not valid. It must be an absolute URI."
182
+ end
183
+ # rubocop:enable Style/RescueStandardError
184
+ end
185
+ end
186
+ end
187
+ # rubocop:enable Metrics/ModuleLength
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Minfraud
4
- VERSION = '1.2.0'
4
+ # The Gem version.
5
+ VERSION = '1.3.0'
5
6
  end
@@ -11,11 +11,11 @@ Gem::Specification.new do |spec|
11
11
  spec.authors = ['kushnir.yb']
12
12
  spec.email = ['support@maxmind.com']
13
13
 
14
- spec.summary = 'Ruby interface to the MaxMind minFraud v2.0 API services'
14
+ spec.summary = 'Ruby API for the minFraud Score, Insights, Factors, and Report Transactions services'
15
15
  spec.homepage = 'https://github.com/maxmind/minfraud-api-ruby'
16
16
  spec.license = 'MIT'
17
17
 
18
- spec.required_ruby_version = '>= 1.9'
18
+ spec.required_ruby_version = '>= 2.0'
19
19
 
20
20
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
21
  spec.bindir = 'exe'
@@ -24,9 +24,11 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.add_runtime_dependency 'faraday', '>= 0.9.1', '< 2.0'
26
26
  spec.add_runtime_dependency 'faraday_middleware', '>= 0.9.1', '< 2.0'
27
+ spec.add_runtime_dependency 'net-http-persistent', '>= 2.0.0', '< 5.0'
27
28
 
28
29
  spec.add_development_dependency 'bundler', '>= 1.16'
29
30
  spec.add_development_dependency 'rake'
30
31
  spec.add_development_dependency 'rspec', '~> 3.0'
31
32
  spec.add_development_dependency 'rubocop'
33
+ spec.add_development_dependency 'webmock'
32
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minfraud
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - kushnir.yb
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-07-15 00:00:00.000000000 Z
11
+ date: 2020-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -50,6 +50,26 @@ dependencies:
50
50
  - - "<"
51
51
  - !ruby/object:Gem::Version
52
52
  version: '2.0'
53
+ - !ruby/object:Gem::Dependency
54
+ name: net-http-persistent
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: 2.0.0
60
+ - - "<"
61
+ - !ruby/object:Gem::Version
62
+ version: '5.0'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 2.0.0
70
+ - - "<"
71
+ - !ruby/object:Gem::Version
72
+ version: '5.0'
53
73
  - !ruby/object:Gem::Dependency
54
74
  name: bundler
55
75
  requirement: !ruby/object:Gem::Requirement
@@ -106,7 +126,21 @@ dependencies:
106
126
  - - ">="
107
127
  - !ruby/object:Gem::Version
108
128
  version: '0'
109
- description:
129
+ - !ruby/object:Gem::Dependency
130
+ name: webmock
131
+ requirement: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ type: :development
137
+ prerelease: false
138
+ version_requirements: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ description:
110
144
  email:
111
145
  - support@maxmind.com
112
146
  executables: []
@@ -185,13 +219,14 @@ files:
185
219
  - lib/minfraud/model/warning.rb
186
220
  - lib/minfraud/report.rb
187
221
  - lib/minfraud/resolver.rb
222
+ - lib/minfraud/validates.rb
188
223
  - lib/minfraud/version.rb
189
224
  - minfraud.gemspec
190
225
  homepage: https://github.com/maxmind/minfraud-api-ruby
191
226
  licenses:
192
227
  - MIT
193
228
  metadata: {}
194
- post_install_message:
229
+ post_install_message:
195
230
  rdoc_options: []
196
231
  require_paths:
197
232
  - lib
@@ -199,16 +234,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
199
234
  requirements:
200
235
  - - ">="
201
236
  - !ruby/object:Gem::Version
202
- version: '1.9'
237
+ version: '2.0'
203
238
  required_rubygems_version: !ruby/object:Gem::Requirement
204
239
  requirements:
205
240
  - - ">="
206
241
  - !ruby/object:Gem::Version
207
242
  version: '0'
208
243
  requirements: []
209
- rubyforge_project:
244
+ rubyforge_project:
210
245
  rubygems_version: 2.7.6.2
211
- signing_key:
246
+ signing_key:
212
247
  specification_version: 4
213
- summary: Ruby interface to the MaxMind minFraud v2.0 API services
248
+ summary: Ruby API for the minFraud Score, Insights, Factors, and Report Transactions
249
+ services
214
250
  test_files: []