onfido 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +161 -0
  3. data/.rspec +3 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +14 -0
  6. data/LICENSE +22 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +154 -0
  9. data/Rakefile +2 -0
  10. data/lib/onfido.rb +23 -0
  11. data/lib/onfido/address.rb +10 -0
  12. data/lib/onfido/api.rb +18 -0
  13. data/lib/onfido/applicant.rb +18 -0
  14. data/lib/onfido/check.rb +21 -0
  15. data/lib/onfido/configuration.rb +35 -0
  16. data/lib/onfido/document.rb +13 -0
  17. data/lib/onfido/null_logger.rb +6 -0
  18. data/lib/onfido/report.rb +11 -0
  19. data/lib/onfido/request_error.rb +23 -0
  20. data/lib/onfido/requestable.rb +12 -0
  21. data/lib/onfido/resource.rb +48 -0
  22. data/lib/onfido/response_handler.rb +38 -0
  23. data/lib/onfido/version.rb +3 -0
  24. data/onfido.gemspec +25 -0
  25. data/spec/integrations/4xx_response_spec.rb +35 -0
  26. data/spec/integrations/address_spec.rb +10 -0
  27. data/spec/integrations/applicant_spec.rb +73 -0
  28. data/spec/integrations/check_spec.rb +36 -0
  29. data/spec/integrations/document_spec.rb +27 -0
  30. data/spec/integrations/report_spec.rb +20 -0
  31. data/spec/onfido/request_error_spec.rb +39 -0
  32. data/spec/onfido/resource_spec.rb +63 -0
  33. data/spec/onfido/response_handler_spec.rb +79 -0
  34. data/spec/onfido_spec.rb +55 -0
  35. data/spec/spec_helper.rb +45 -0
  36. data/spec/support/fake_onfido_api.rb +55 -0
  37. data/spec/support/fixtures/4xx_response.json +14 -0
  38. data/spec/support/fixtures/addresses.json +24 -0
  39. data/spec/support/fixtures/applicant.json +44 -0
  40. data/spec/support/fixtures/applicants.json +12 -0
  41. data/spec/support/fixtures/check.json +30 -0
  42. data/spec/support/fixtures/checks.json +34 -0
  43. data/spec/support/fixtures/document.json +10 -0
  44. data/spec/support/fixtures/report.json +10 -0
  45. data/spec/support/fixtures/reports.json +24 -0
  46. metadata +151 -0
@@ -0,0 +1,18 @@
1
+ module Onfido
2
+ class Applicant < Resource
3
+ def create(payload)
4
+ post(
5
+ url: url_for('applicants'),
6
+ payload: payload
7
+ )
8
+ end
9
+
10
+ def find(applicant_id)
11
+ get(url: url_for("applicants/#{applicant_id}"), payload: {})
12
+ end
13
+
14
+ def all
15
+ get(url: url_for("applicants"), payload: {})
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ module Onfido
2
+ 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
9
+
10
+ def find(applicant_id, check_id)
11
+ get(
12
+ url: url_for("applicants/#{applicant_id}/checks/#{check_id}"),
13
+ payload: {}
14
+ )
15
+ end
16
+
17
+ def all(applicant_id)
18
+ get(url: url_for("applicants/#{applicant_id}/checks"), payload: {})
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ module Onfido
2
+ module Configuration
3
+ attr_accessor :api_key, :throws_exceptions
4
+
5
+ def self.extended(base)
6
+ base.reset
7
+ end
8
+
9
+ def configure
10
+ yield self
11
+ end
12
+
13
+ def reset
14
+ self.api_key = nil
15
+ self.throws_exceptions = true
16
+ RestClient.log = nil
17
+ end
18
+
19
+ def logger=(log)
20
+ if log.respond_to?(:<<)
21
+ RestClient.log = log
22
+ else
23
+ raise "#{log.class} doesn't seem to behave like a logger!"
24
+ end
25
+ end
26
+
27
+ def logger
28
+ RestClient.log ||= NullLogger.new
29
+ end
30
+
31
+ def endpoint
32
+ 'https://api.onfido.com/v1/'
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,13 @@
1
+ module Onfido
2
+ class Document < Resource
3
+ # with open-uri the file can be a link or an actual file
4
+
5
+ def create(applicant_id, payload)
6
+ payload[:file] = open(payload.fetch(:file), 'r')
7
+ post(
8
+ url: url_for("applicants/#{applicant_id}/documents"),
9
+ payload: payload
10
+ )
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ module Onfido
2
+ class NullLogger
3
+ def <<
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ module Onfido
2
+ class Report < Resource
3
+ def find(check_id, report_id)
4
+ get(url: url_for("checks/#{check_id}/reports/#{report_id}"), payload: {})
5
+ end
6
+
7
+ def all(check_id)
8
+ get(url: url_for("checks/#{check_id}/reports"), payload: {})
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ =begin
2
+ The class will handle response failures coming from Onfido
3
+ It's main purpose is to produce meaningful error messages to the user e.g.
4
+
5
+ RequestError: Authorization error: please re-check your credentials
6
+
7
+ Users can also rescue the error and insect its type and affected fields as
8
+ specified in the Onfido documentation e.g.
9
+
10
+ begin
11
+ # error being raised
12
+ rescue Onfido::RequestError => e
13
+ e.type
14
+ e.fields
15
+ end
16
+
17
+ =end
18
+
19
+ module Onfido
20
+ class RequestError < StandardError
21
+ attr_accessor :type, :fields, :response_code
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ module Onfido
2
+ module Requestable
3
+ def make_request(options)
4
+ url = options.fetch(:url)
5
+ payload = options.fetch(:payload)
6
+ method = options.fetch(:method)
7
+ RestClient::Request.execute(url: url, payload: payload, method: method, headers: headers) do |response, *|
8
+ ResponseHandler.new(response).parse!
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,48 @@
1
+ module Onfido
2
+ class Resource
3
+ VALID_HTTP_METHODS = %i(get post)
4
+
5
+ include Requestable
6
+
7
+ def url_for(path)
8
+ Onfido.endpoint + path
9
+ end
10
+
11
+ def method_missing(method, *args)
12
+ if VALID_HTTP_METHODS.include?(method.to_sym)
13
+ make_request(
14
+ url: args.first.fetch(:url),
15
+ payload: build_query(args.first.fetch(:payload)),
16
+ method: method.to_sym
17
+ )
18
+ else
19
+ super
20
+ end
21
+ end
22
+
23
+ def respond_to_missing?(method, include_private = false)
24
+ VALID_HTTP_METHODS.include?(method.to_sym) || super
25
+ end
26
+
27
+ def headers
28
+ {
29
+ 'Authorization' => "Token token=#{Onfido.api_key}",
30
+ 'Accept' => "application/json"
31
+ }
32
+ end
33
+
34
+ private
35
+
36
+ # There seems to be a serialization issue with the HTTP client
37
+ # which does not serialize the payload properly.
38
+ # Have a look here https://gist.github.com/PericlesTheo/cb35139c57107ab3c84a
39
+
40
+ def build_query(payload)
41
+ if payload[:file]
42
+ payload
43
+ else
44
+ Rack::Utils.build_nested_query(payload)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,38 @@
1
+ module Onfido
2
+ class ResponseHandler
3
+ attr_reader :response
4
+
5
+ def initialize(response)
6
+ @response = response
7
+ end
8
+
9
+ def parse!
10
+ if Onfido.throws_exceptions && parsed_response["error"]
11
+ raise request_error
12
+ else
13
+ parsed_response
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def parsed_response
20
+ @parsed_response ||=
21
+ begin
22
+ JSON.parse(response)
23
+ rescue JSON::ParserError
24
+ {
25
+ 'error' => {"message" => "Unparseable response: #{response}"}
26
+ }
27
+ end
28
+ end
29
+
30
+ def request_error
31
+ RequestError.new(parsed_response['error']['message']).tap do |error|
32
+ error.type = parsed_response['error']["type"]
33
+ error.fields = parsed_response['error']["fields"]
34
+ error.response_code = response.code if response.respond_to?(:code)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module Onfido
2
+ VERSION = '0.0.1'
3
+ end
data/onfido.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'onfido/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'onfido'
8
+ spec.version = Onfido::VERSION
9
+ spec.authors = ['Pericles Theodorou']
10
+ spec.email = ['periclestheo@gmail.com']
11
+ spec.summary = %q{A wrapper for Onfido API 1.0}
12
+ spec.description = %q{A wrapper for Onfido API 1.0}
13
+ spec.homepage = 'http://github.com/hvssle/onfido'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.7'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+
24
+ spec.add_dependency 'rest-client', '~> 1.8.0'
25
+ end
@@ -0,0 +1,35 @@
1
+ describe Onfido::Resource do
2
+ let(:resource) { described_class.new }
3
+ let(:url) { Onfido.endpoint + path }
4
+ let(:api_key) { 'some_key' }
5
+ let(:payload) { {postcode: 'SE1 4NG'} }
6
+
7
+
8
+ before do
9
+ allow(Onfido).to receive(:api_key).and_return(api_key)
10
+ end
11
+
12
+ context 'when a response is a 4xx' do
13
+ let(:path) { '4xx_response' }
14
+
15
+ context "when 'throws_exceptions' is true" do
16
+ it 'raises a custom error' do
17
+ expect {
18
+ resource.get({url: url, payload: payload})
19
+ }.to raise_error(
20
+ Onfido::RequestError, 'Something went wrong')
21
+ end
22
+ end
23
+
24
+ context "when 'throws_exceptions' is false" do
25
+ before do
26
+ allow(Onfido).to receive(:throws_exceptions).and_return(false)
27
+ end
28
+
29
+ it 'returns the body as a hash' do
30
+ response = resource.get({url: url, payload: payload})
31
+ expect(response['error']).not_to be_nil
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,10 @@
1
+ describe Onfido::Address do
2
+ subject(:api) { Onfido::API.new }
3
+
4
+ describe '#all' do
5
+ it 'returns the addresses matching the postcode' do
6
+ response = api.address.all('SW1 4NG')
7
+ expect(response['addresses'].count).to eq(2)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,73 @@
1
+ describe Onfido::Applicant do
2
+ subject(:applicant) { described_class.new }
3
+ let(:params) do
4
+ {
5
+ 'title' => 'Mr',
6
+ 'first_name' => 'Chandler',
7
+ 'last_name' => 'Bing',
8
+ 'gender' => 'male',
9
+ 'middle_name' => 'Muriel',
10
+ 'dob' => '1968-04-08',
11
+ 'telephone' => '555555555',
12
+ 'mobile' => '77777777',
13
+ 'email' => 'chandler_bing_6@friends.com',
14
+ 'addresses' => [
15
+ {
16
+ 'flat_number' => '4',
17
+ 'building_number' => '100',
18
+ 'building_name' => 'Awesome Building',
19
+ 'street' => 'Main Street',
20
+ 'sub_street' => 'A sub street',
21
+ 'town' => 'London',
22
+ 'postcode' => 'SW4 6EH',
23
+ 'country' => 'GBR'
24
+ },
25
+ {
26
+ 'flat_number' => '1',
27
+ 'building_number' => '10',
28
+ 'building_name' => 'Great Building',
29
+ 'street' => 'Old Street',
30
+ 'sub_street' => 'Sub Street',
31
+ 'town' => 'London',
32
+ 'postcode' => 'SW1 4NG',
33
+ 'country' => 'GBR'
34
+ }
35
+ ]
36
+ }
37
+ end
38
+
39
+ describe '#create' do
40
+ # Need to find a better way of testing that the request is not malformed.
41
+ # Currently this runs for every feature spec. The fact that it's under here
42
+ # is only for semantic reasons
43
+
44
+ it 'serializes the payload correctly' do
45
+ WebMock.after_request do |request_signature, response|
46
+ if request_signature.uri.path == 'v1/applicants'
47
+ expect(Rack::Utils.parse_nested_query(request_signature.body)).to eq(params)
48
+ end
49
+ end
50
+ end
51
+
52
+ it 'creates an applicant' do
53
+ response = applicant.create(params)
54
+ expect(response['id']).not_to be_nil
55
+ end
56
+ end
57
+
58
+ describe '#find' do
59
+ let(:applicant_id) { '61f659cb-c90b-4067-808a-6136b5c01351' }
60
+
61
+ it 'returns the applicant' do
62
+ response = applicant.find(applicant_id)
63
+ expect(response['id']).to eq(applicant_id)
64
+ end
65
+ end
66
+
67
+ describe '#all' do
68
+ it 'returns all the applicants' do
69
+ response = applicant.all
70
+ expect(response['applicants'].size).to eq(2)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,36 @@
1
+ describe Onfido::Check do
2
+ subject(:check) { described_class.new }
3
+ let(:applicant_id) { '61f659cb-c90b-4067-808a-6136b5c01351' }
4
+
5
+ describe '#create' do
6
+ let(:params) do
7
+ {
8
+ type: 'express',
9
+ reports: [{name: 'identity'}]
10
+ }
11
+ end
12
+
13
+ it 'creates a new check for an applicant' do
14
+ response = check.create(applicant_id, params)
15
+ expect(response['id']).not_to be_nil
16
+ end
17
+ end
18
+
19
+ describe '#find' do
20
+ let(:check_id) { '8546921-123123-123123' }
21
+
22
+ it 'returns an existing check for the applicant' do
23
+ response = check.find(applicant_id, check_id)
24
+ expect(response['id']).to eq(check_id)
25
+ end
26
+ end
27
+
28
+ describe '#all' do
29
+ let(:check_id) { '8546921-123123-123123' }
30
+
31
+ it 'returns all existing checks for the applicant' do
32
+ response = check.all(applicant_id)
33
+ expect(response['checks'].size).to eq(1)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,27 @@
1
+ require 'tempfile'
2
+
3
+ describe Onfido::Document do
4
+ subject(:document) { described_class.new }
5
+
6
+ describe '#create' do
7
+ after do
8
+ file.close
9
+ file.unlink
10
+ end
11
+
12
+ let(:file) { Tempfile.new(['passport', '.jpg']) }
13
+ let(:params) do
14
+ {
15
+ type: 'passport',
16
+ side: 'back',
17
+ file: file
18
+ }
19
+ let(:applicant_id) { '1030303-123123-123123' }
20
+
21
+ it 'creates a new document' do
22
+ response = document.create('foobar', params)
23
+ expect(response['id']).not_to be_nil
24
+ end
25
+ end
26
+ end
27
+ end