onfido 1.0.0 → 2.0.2

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