minfraud 1.2.0 → 1.3.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.
@@ -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: []