onfido 1.0.0 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
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)