onfido 0.15.0 → 2.0.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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -49
  3. data/.travis.yml +3 -10
  4. data/CHANGELOG.md +24 -0
  5. data/Gemfile +2 -0
  6. data/LICENSE +2 -1
  7. data/README.md +46 -172
  8. data/lib/onfido.rb +4 -3
  9. data/lib/onfido/api.rb +21 -15
  10. data/lib/onfido/errors/connection_error.rb +2 -0
  11. data/lib/onfido/errors/onfido_error.rb +2 -0
  12. data/lib/onfido/errors/request_error.rb +2 -0
  13. data/lib/onfido/errors/server_error.rb +2 -0
  14. data/lib/onfido/options.rb +38 -0
  15. data/lib/onfido/resource.rb +44 -61
  16. data/lib/onfido/resources/address.rb +3 -4
  17. data/lib/onfido/resources/applicant.rb +8 -6
  18. data/lib/onfido/resources/check.rb +15 -19
  19. data/lib/onfido/resources/document.rb +13 -11
  20. data/lib/onfido/resources/extraction.rb +11 -0
  21. data/lib/onfido/resources/live_photo.rb +11 -14
  22. data/lib/onfido/resources/live_video.rb +7 -8
  23. data/lib/onfido/resources/report.rb +10 -9
  24. data/lib/onfido/resources/sdk_token.rb +5 -5
  25. data/lib/onfido/resources/webhook.rb +15 -11
  26. data/lib/onfido/version.rb +3 -1
  27. data/onfido.gemspec +10 -12
  28. data/spec/integrations/address_spec.rb +5 -2
  29. data/spec/integrations/applicant_spec.rb +29 -42
  30. data/spec/integrations/check_spec.rb +28 -69
  31. data/spec/integrations/document_spec.rb +22 -19
  32. data/spec/integrations/extraction_spec.rb +23 -0
  33. data/spec/integrations/live_photo_spec.rb +18 -15
  34. data/spec/integrations/live_video_spec.rb +13 -11
  35. data/spec/integrations/report_spec.rb +16 -13
  36. data/spec/integrations/resource_spec.rb +93 -0
  37. data/spec/integrations/sdk_token_spec.rb +10 -6
  38. data/spec/integrations/webhook_spec.rb +56 -37
  39. data/spec/onfido/api_spec.rb +14 -25
  40. data/spec/onfido/connection_error_spec.rb +4 -2
  41. data/spec/onfido/options_spec.rb +39 -0
  42. data/spec/onfido/request_error_spec.rb +4 -2
  43. data/spec/spec_helper.rb +3 -5
  44. data/spec/support/fake_onfido_api.rb +77 -88
  45. data/spec/support/fixtures/applicant.json +21 -42
  46. data/spec/support/fixtures/check.json +4 -4
  47. data/spec/support/fixtures/checks.json +4 -4
  48. data/spec/support/fixtures/document.json +2 -2
  49. data/spec/support/fixtures/documents.json +8 -8
  50. data/spec/support/fixtures/extraction.json +23 -0
  51. data/spec/support/fixtures/live_photo.json +3 -3
  52. data/spec/support/fixtures/live_photos.json +6 -6
  53. data/spec/support/fixtures/live_video.json +3 -3
  54. data/spec/support/fixtures/live_videos.json +4 -4
  55. data/spec/support/fixtures/report.json +4 -4
  56. data/spec/support/fixtures/reports.json +8 -8
  57. data/spec/support/fixtures/webhook.json +6 -5
  58. data/spec/support/fixtures/webhooks.json +17 -12
  59. metadata +25 -65
  60. data/Rakefile +0 -1
  61. data/lib/onfido/configuration.rb +0 -47
  62. data/lib/onfido/null_logger.rb +0 -5
  63. data/lib/onfido/resources/report_type_group.rb +0 -11
  64. data/spec/integrations/exceptions_spec.rb +0 -74
  65. data/spec/integrations/report_type_group_spec.rb +0 -19
  66. data/spec/onfido/resource_spec.rb +0 -137
  67. data/spec/onfido_spec.rb +0 -84
  68. data/spec/support/fixtures/check_with_expanded_reports.json +0 -30
  69. data/spec/support/fixtures/checks_with_expanded_reports.json +0 -34
  70. data/spec/support/fixtures/report_type_group.json +0 -25
  71. data/spec/support/fixtures/report_type_groups.json +0 -30
data/lib/onfido/api.rb CHANGED
@@ -1,47 +1,53 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Onfido
2
4
  class API
3
- def initialize(options = {})
4
- @api_key = options[:api_key]
5
+ def initialize(api_key:, region:, **extra_options)
6
+ @options = Onfido::Options.new(api_key: api_key, region: region, **extra_options)
5
7
  end
6
8
 
7
9
  def applicant
8
- Onfido::Applicant.new(@api_key)
10
+ @applicant ||= Onfido::Applicant.new(options)
9
11
  end
10
12
 
11
13
  def check
12
- Onfido::Check.new(@api_key)
14
+ @check ||= Onfido::Check.new(options)
13
15
  end
14
16
 
15
17
  def document
16
- Onfido::Document.new(@api_key)
18
+ @document ||= Onfido::Document.new(options)
17
19
  end
18
20
 
19
21
  def live_photo
20
- Onfido::LivePhoto.new(@api_key)
22
+ @live_photo ||= Onfido::LivePhoto.new(options)
21
23
  end
22
24
 
23
25
  def live_video
24
- Onfido::LiveVideo.new(@api_key)
26
+ @live_video ||= Onfido::LiveVideo.new(options)
25
27
  end
26
28
 
27
29
  def report
28
- Onfido::Report.new(@api_key)
29
- end
30
-
31
- def report_type_group
32
- Onfido::ReportTypeGroup.new(@api_key)
30
+ @report ||= Onfido::Report.new(options)
33
31
  end
34
32
 
35
33
  def sdk_token
36
- Onfido::SdkToken.new(@api_key)
34
+ @sdk_token ||= Onfido::SdkToken.new(options)
37
35
  end
38
36
 
39
37
  def webhook
40
- Onfido::Webhook.new(@api_key)
38
+ @webhook ||= Onfido::Webhook.new(options)
41
39
  end
42
40
 
43
41
  def address
44
- Onfido::Address.new(@api_key)
42
+ @address ||= Onfido::Address.new(options)
45
43
  end
44
+
45
+ def extraction
46
+ @extraction ||= Onfido::Extraction.new(options)
47
+ end
48
+
49
+ private
50
+
51
+ attr_reader :options
46
52
  end
47
53
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Onfido
2
4
  class ConnectionError < OnfidoError
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Onfido
2
4
  class OnfidoError < StandardError
3
5
  attr_accessor :response_code, :response_body
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Onfido
2
4
  class RequestError < OnfidoError
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Onfido
2
4
  class ServerError < OnfidoError
3
5
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Onfido
4
+ class Options
5
+ REGIONS = %w[eu us ca].freeze
6
+
7
+ def initialize(api_key:, region:, open_timeout: 10, read_timeout: 30, unknown_api_url: nil)
8
+ @api_key = api_key
9
+ @region = region.to_s.downcase
10
+ @open_timeout = open_timeout
11
+ @read_timeout = read_timeout
12
+ @unknown_api_url = unknown_api_url
13
+
14
+ raise "Unknown region #{@region}" unless REGIONS.include?(@region)
15
+ end
16
+
17
+ def rest_client
18
+ @rest_client ||= RestClient::Resource.new(
19
+ base_url,
20
+ read_timeout: read_timeout,
21
+ open_timeout: open_timeout,
22
+ headers: {
23
+ 'Authorization' => "Token token=#{api_key}",
24
+ 'Accept' => 'application/json',
25
+ 'User-Agent' => "onfido-ruby/#{Onfido::VERSION}"
26
+ }
27
+ )
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :api_key, :open_timeout, :read_timeout
33
+
34
+ def base_url
35
+ @unknown_api_url || "https://api.#{@region}.onfido.com/v3.1/"
36
+ end
37
+ end
38
+ end
@@ -1,59 +1,52 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Onfido
2
- class Resource
3
- VALID_HTTP_METHODS = %i(get post put delete).freeze
4
+ class Resource # rubocop:todo Metrics/ClassLength
5
+ VALID_HTTP_METHODS = %i[get post put delete].freeze
4
6
  REQUEST_TIMEOUT_HTTP_CODE = 408
5
7
 
6
- def initialize(api_key = nil)
7
- @api_key = api_key || Onfido.api_key
8
+ def initialize(options)
9
+ @rest_client = options.rest_client
8
10
  end
9
11
 
10
- def url_for(path)
11
- Onfido.endpoint + path
12
- end
12
+ private
13
13
 
14
- VALID_HTTP_METHODS.each do |method|
15
- define_method method do |*args|
16
- make_request(
17
- method: method.to_sym,
18
- url: args.first.fetch(:url),
19
- payload: build_query(args.first.fetch(:payload, {}))
20
- )
21
- end
14
+ attr_reader :rest_client
15
+
16
+ def get(path:)
17
+ handle_request { rest_client[path].get }
22
18
  end
23
19
 
24
- private
20
+ def post(path:, payload: nil)
21
+ handle_request { rest_client[path].post(payload) }
22
+ end
25
23
 
26
- def make_request(options)
27
- url = options.fetch(:url)
28
- payload = options.fetch(:payload)
29
- method = options.fetch(:method)
24
+ def put(path:, payload: nil)
25
+ handle_request { rest_client[path].put(payload) }
26
+ end
30
27
 
31
- request_options = {
32
- url: url,
33
- payload: payload,
34
- method: method,
35
- headers: headers,
36
- open_timeout: Onfido.open_timeout,
37
- timeout: Onfido.read_timeout
38
- }
28
+ def delete(path:)
29
+ handle_request { rest_client[path].delete }
30
+ end
39
31
 
40
- response = RestClient::Request.execute(request_options)
32
+ def handle_request
33
+ response = yield
41
34
 
42
35
  # response should be parsed only when there is a response expected
43
36
  parse(response) unless response.code == 204 # no_content
44
- rescue RestClient::ExceptionWithResponse => error
45
- if error.response && !timeout_response?(error.response)
46
- handle_api_error(error.response)
37
+ rescue RestClient::ExceptionWithResponse => e
38
+ if e.response && !timeout_response?(e.response)
39
+ handle_api_error(e.response)
47
40
  else
48
- handle_restclient_error(error, url)
41
+ handle_restclient_error(e)
49
42
  end
50
- rescue RestClient::Exception, Errno::ECONNREFUSED => error
51
- handle_restclient_error(error, url)
43
+ rescue RestClient::Exception, Errno::ECONNREFUSED => e
44
+ handle_restclient_error(e)
52
45
  end
53
46
 
54
47
  def parse(response)
55
48
  content_type = response.headers[:content_type]
56
- if content_type && content_type.include?("application/json")
49
+ if content_type&.include?('application/json')
57
50
  JSON.parse(response.body.to_s)
58
51
  else
59
52
  response.body
@@ -66,13 +59,6 @@ module Onfido
66
59
  response.code.to_i == REQUEST_TIMEOUT_HTTP_CODE
67
60
  end
68
61
 
69
- def headers
70
- {
71
- 'Authorization' => "Token token=#{@api_key}",
72
- 'Accept' => "application/json"
73
- }
74
- end
75
-
76
62
  # There seems to be a serialization issue with the HTTP client
77
63
  # which does not serialize the payload properly.
78
64
  # Have a look here https://gist.github.com/PericlesTheo/cb35139c57107ab3c84a
@@ -88,12 +74,12 @@ module Onfido
88
74
  def handle_api_error(response)
89
75
  parsed_response = parse(response)
90
76
 
91
- general_api_error(response.code, response.body) unless parsed_response["error"]
77
+ general_api_error(response.code, response.body) unless parsed_response['error']
92
78
 
93
79
  error_class = response.code.to_i >= 500 ? ServerError : RequestError
94
80
 
95
81
  raise error_class.new(
96
- parsed_response["error"]['message'],
82
+ parsed_response['error']['message'],
97
83
  response_code: response.code,
98
84
  response_body: response.body
99
85
  )
@@ -110,46 +96,43 @@ module Onfido
110
96
  )
111
97
  end
112
98
 
113
- def handle_restclient_error(error, url)
99
+ def handle_restclient_error(error) # rubocop:todo Metrics/MethodLength
114
100
  connection_message =
115
- "Please check your internet connection and try again. " \
116
- "If this problem persists, you should let us know at info@onfido.com."
101
+ 'Please check your internet connection and try again. ' \
102
+ 'If this problem persists, you should let us know at info@onfido.com.'
117
103
 
118
104
  message =
119
105
  case error
120
106
  when RestClient::RequestTimeout
121
- "Could not connect to Onfido (#{url}). #{connection_message}"
107
+ "Could not connect to Onfido . #{connection_message}"
122
108
 
123
109
  when RestClient::ServerBrokeConnection
124
- "The connection to the server (#{url}) broke before the " \
125
- "request completed. #{connection_message}"
110
+ "The connection to the server broke before the request completed. #{connection_message}"
126
111
 
127
112
  when RestClient::SSLCertificateNotVerified
128
113
  "Could not verify Onfido's SSL certificate. Please make sure " \
129
- "that your network is not intercepting certificates. " \
130
- "(Try going to #{Onfido.endpoint} in your browser.) " \
131
- "If this problem persists, let us know at info@onfido.com."
114
+ 'that your network is not intercepting certificates. '
132
115
 
133
116
  when SocketError
134
- "Unexpected error when trying to connect to Onfido. " \
135
- "You may be seeing this message because your DNS is not working. " \
117
+ 'Unexpected error when trying to connect to Onfido. ' \
118
+ 'You may be seeing this message because your DNS is not working. ' \
136
119
  "To check, try running 'host onfido.com' from the command line."
137
120
 
138
121
  else
139
- "Unexpected error communicating with Onfido. " \
140
- "If this problem persists, let us know at info@onfido.com."
122
+ 'Unexpected error communicating with Onfido. ' \
123
+ 'If this problem persists, let us know at info@onfido.com.'
141
124
  end
142
125
 
143
126
  full_message = message + "\n\n(Network error: #{error.message})"
144
127
 
145
- raise ConnectionError.new(full_message)
128
+ raise ConnectionError, full_message
146
129
  end
147
130
 
148
131
  def validate_file!(file)
149
132
  return if file.respond_to?(:read) && file.respond_to?(:path)
150
133
 
151
- raise ArgumentError, "File must be a `File`-like object which responds to " \
152
- "`#read` and `#path`"
134
+ raise ArgumentError, 'File must be a `File`-like object which responds to ' \
135
+ '`#read` and `#path`'
153
136
  end
154
137
  end
155
138
  end
@@ -1,10 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Onfido
2
4
  class Address < Resource
3
5
  def all(postcode)
4
- get(
5
- url: url_for('addresses/pick'),
6
- payload: { postcode: postcode.delete(' ') }
7
- )
6
+ get(path: "addresses/pick?postcode=#{postcode.delete(' ')}")
8
7
  end
9
8
  end
10
9
  end
@@ -1,27 +1,29 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Onfido
2
4
  class Applicant < Resource
3
5
  def create(payload)
4
- post(url: url_for('applicants'), payload: payload)
6
+ post(path: 'applicants', payload: payload)
5
7
  end
6
8
 
7
9
  def update(applicant_id, payload)
8
- put(url: url_for("applicants/#{applicant_id}"), payload: payload)
10
+ put(path: "applicants/#{applicant_id}", payload: payload)
9
11
  end
10
12
 
11
13
  def destroy(applicant_id)
12
- delete(url: url_for("applicants/#{applicant_id}"))
14
+ delete(path: "applicants/#{applicant_id}")
13
15
  end
14
16
 
15
17
  def find(applicant_id)
16
- get(url: url_for("applicants/#{applicant_id}"))
18
+ get(path: "applicants/#{applicant_id}")
17
19
  end
18
20
 
19
21
  def all(page: 1, per_page: 20)
20
- get(url: url_for("applicants?page=#{page}&per_page=#{per_page}"))
22
+ get(path: "applicants?page=#{page}&per_page=#{per_page}")
21
23
  end
22
24
 
23
25
  def restore(applicant_id)
24
- post(url: url_for("applicants/#{applicant_id}/restore"))
26
+ post(path: "applicants/#{applicant_id}/restore")
25
27
  end
26
28
  end
27
29
  end
@@ -1,32 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Onfido
2
4
  class Check < Resource
3
- def create(applicant_id, payload)
4
- post(
5
- url: url_for("applicants/#{applicant_id}/checks"),
6
- payload: payload
7
- )
8
- end
5
+ def create(applicant_id:, report_names:, **payload)
6
+ payload[:applicant_id] = applicant_id
7
+ payload[:report_names] = report_names
9
8
 
10
- def find(applicant_id, check_id, expand: nil)
11
- querystring = "&expand=#{expand}" if expand
12
- get(url: url_for("applicants/#{applicant_id}/checks/#{check_id}?" \
13
- "#{querystring}"))
9
+ post(path: 'checks', payload: payload)
14
10
  end
15
11
 
16
- def find_by_url(url, expand: nil)
17
- url_path = url.sub(%r/^https\:\/\/api\.onfido\.com\/v2\//, '')
18
- querystring = "&expand=#{expand}" if expand
19
- get(url: url_for("#{url_path}?#{querystring}"))
12
+ def find(check_id)
13
+ get(path: "checks/#{check_id}")
20
14
  end
21
15
 
22
- def all(applicant_id, page: 1, per_page: 20, expand: nil)
23
- querystring = "page=#{page}&per_page=#{per_page}"
24
- querystring += "&expand=#{expand}" if expand
25
- get(url: url_for("applicants/#{applicant_id}/checks?#{querystring}"))
16
+ def all(applicant_id)
17
+ get(path: "checks?applicant_id=#{applicant_id}")
26
18
  end
27
19
 
28
20
  def resume(check_id)
29
- post(url: url_for("checks/#{check_id}/resume"))
21
+ post(path: "checks/#{check_id}/resume")
22
+ end
23
+
24
+ def download(check_id)
25
+ get(path: "checks/#{check_id}/download")
30
26
  end
31
27
  end
32
28
  end
@@ -1,26 +1,28 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Onfido
2
4
  class Document < Resource
3
5
  # with open-uri the file can be a link or an actual file
4
6
 
5
- def create(applicant_id, payload)
6
- validate_file!(payload.fetch(:file))
7
+ def create(applicant_id:, file:, type:, **payload)
8
+ validate_file!(file)
9
+ payload[:applicant_id] = applicant_id
10
+ payload[:file] = file
11
+ payload[:type] = type
7
12
 
8
- post(
9
- url: url_for("applicants/#{applicant_id}/documents"),
10
- payload: payload
11
- )
13
+ post(path: 'documents', payload: payload)
12
14
  end
13
15
 
14
- def find(applicant_id, document_id)
15
- get(url: url_for("applicants/#{applicant_id}/documents/#{document_id}"))
16
+ def find(document_id)
17
+ get(path: "documents/#{document_id}")
16
18
  end
17
19
 
18
- def download(applicant_id, document_id)
19
- get(url: url_for("applicants/#{applicant_id}/documents/#{document_id}/download"))
20
+ def download(document_id)
21
+ get(path: "documents/#{document_id}/download")
20
22
  end
21
23
 
22
24
  def all(applicant_id)
23
- get(url: url_for("applicants/#{applicant_id}/documents"))
25
+ get(path: "documents?applicant_id=#{applicant_id}")
24
26
  end
25
27
  end
26
28
  end