passfort 0.2.3 → 0.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +17 -17
  3. data/.rubocop.yml +5 -5
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +33 -28
  6. data/Gemfile +5 -5
  7. data/Gemfile.lock +91 -89
  8. data/LICENCE.txt +22 -22
  9. data/README.md +6 -6
  10. data/lib/passfort.rb +35 -35
  11. data/lib/passfort/client.rb +23 -23
  12. data/lib/passfort/endpoint.rb +10 -10
  13. data/lib/passfort/endpoint/checks.rb +19 -19
  14. data/lib/passfort/endpoint/profiles.rb +46 -46
  15. data/lib/passfort/endpoint/tasks.rb +16 -16
  16. data/lib/passfort/errors.rb +15 -15
  17. data/lib/passfort/errors/api_error.rb +16 -16
  18. data/lib/passfort/errors/chargeable_limit_reached_error.rb +12 -12
  19. data/lib/passfort/errors/invalid_api_key_error.rb +12 -12
  20. data/lib/passfort/errors/invalid_input_data_error.rb +12 -12
  21. data/lib/passfort/errors/request_error.rb +12 -12
  22. data/lib/passfort/errors/timeout_error.rb +12 -12
  23. data/lib/passfort/errors/unknown_api_error.rb +12 -12
  24. data/lib/passfort/errors/unparseable_response_error.rb +11 -11
  25. data/lib/passfort/http.rb +89 -65
  26. data/lib/passfort/resource.rb +12 -12
  27. data/lib/passfort/resource/base.rb +21 -21
  28. data/lib/passfort/resource/check.rb +10 -10
  29. data/lib/passfort/resource/company_data.rb +11 -11
  30. data/lib/passfort/resource/individual_data.rb +11 -11
  31. data/lib/passfort/resource/profile.rb +15 -15
  32. data/lib/passfort/resource/task.rb +9 -9
  33. data/lib/passfort/version.rb +5 -5
  34. data/passfort.gemspec +30 -29
  35. data/spec/fixtures/check.json +67 -67
  36. data/spec/fixtures/collected_data.json +7 -7
  37. data/spec/fixtures/company_ownership_check/check.json +78 -78
  38. data/spec/fixtures/company_ownership_check/collected_data.json +7 -7
  39. data/spec/fixtures/company_ownership_check/collected_data_update_request.json +63 -63
  40. data/spec/fixtures/company_ownership_check/profile.json +128 -128
  41. data/spec/fixtures/profile.json +128 -128
  42. data/spec/fixtures/task.json +6 -6
  43. data/spec/integration/company_ownership_check_spec.rb +74 -74
  44. data/spec/passfort/endpoint/checks_spec.rb +22 -22
  45. data/spec/passfort/endpoint/profiles_spec.rb +79 -79
  46. data/spec/passfort/endpoint/tasks_spec.rb +21 -21
  47. data/spec/passfort/http_spec.rb +130 -95
  48. data/spec/spec_helper.rb +9 -9
  49. metadata +16 -2
@@ -1,11 +1,11 @@
1
- # frozen_string_literal: true
2
-
3
- module Passfort
4
- module Errors
5
- class UnparseableResponseError < APIError
6
- def initialize(*args)
7
- super("Unparseable response body", *args)
8
- end
9
- end
10
- end
11
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Passfort
4
+ module Errors
5
+ class UnparseableResponseError < APIError
6
+ def initialize(*args)
7
+ super("Unparseable response body", *args)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,65 +1,89 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
- require "excon"
5
-
6
- module Passfort
7
- class Http
8
- DOMAIN = "https://api.passfort.com"
9
- ROOT_PATH = "/4.0"
10
-
11
- def initialize(api_key, connection: Excon.new(DOMAIN))
12
- @api_key = api_key
13
- @connection = connection
14
- end
15
-
16
- def get(path)
17
- response = @connection.get(path: ROOT_PATH + path, headers: { apikey: @api_key })
18
- body = JSON.parse(response.body)
19
- handle_error(response, body)
20
- body
21
- rescue JSON::ParserError
22
- raise Passfort::Errors::UnparseableResponseError.new([], response)
23
- rescue Excon::Errors::Timeout
24
- raise Passfort::Errors::TimeoutError
25
- end
26
-
27
- def post(path, body:)
28
- response = @connection.post(
29
- path: ROOT_PATH + path,
30
- body: body.to_json,
31
- headers: { apikey: @api_key, "Content-Type" => "application/json" },
32
- )
33
- body = JSON.parse(response.body)
34
- handle_error(response, body)
35
- body
36
- rescue JSON::ParserError
37
- raise Passfort::Errors::UnparseableResponseError.new([], response)
38
- rescue Excon::Errors::Timeout
39
- raise Passfort::Errors::TimeoutError
40
- end
41
-
42
- private
43
-
44
- # error codes: https://passfort.com/developer/v4/data-reference/ErrorCodes
45
- def handle_error(response, body)
46
- unless [200, 201].include?(response.status)
47
- raise Passfort::Errors::RequestError.new([], response)
48
- end
49
-
50
- errors = body["errors"].is_a?(Array) ? body["errors"] : [body["errors"]]
51
-
52
- handle_response_error(errors, response) if errors.any?
53
- end
54
-
55
- def handle_response_error(errors, response)
56
- error_class = case errors[0]["code"]
57
- when 201 then Passfort::Errors::InvalidInputDataError
58
- when 204 then Passfort::Errors::InvalidAPIKeyError
59
- when 104 then Passfort::Errors::ChargeableLimitReachedError
60
- else Passfort::Errors::UnknownApiError
61
- end
62
- raise error_class.new(errors, response)
63
- end
64
- end
65
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/notifications"
4
+ require "excon"
5
+ require "json"
6
+
7
+ module Passfort
8
+ class Http
9
+ DOMAIN = "https://api.passfort.com"
10
+ ROOT_PATH = "/4.0"
11
+
12
+ def initialize(api_key, connection: Excon.new(DOMAIN))
13
+ @api_key = api_key
14
+ @connection = connection
15
+ end
16
+
17
+ def get(path)
18
+ execute(
19
+ -> {
20
+ @connection.get(
21
+ path: ROOT_PATH + path,
22
+ headers: { apikey: @api_key },
23
+ )
24
+ },
25
+ :get,
26
+ path: path,
27
+ )
28
+ end
29
+
30
+ def post(path, body:)
31
+ execute(
32
+ -> {
33
+ @connection.post(
34
+ path: ROOT_PATH + path,
35
+ body: body.to_json,
36
+ headers: { apikey: @api_key, "Content-Type" => "application/json" },
37
+ )
38
+ },
39
+ :post,
40
+ path: path,
41
+ body: body,
42
+ )
43
+ end
44
+
45
+ private
46
+
47
+ def execute(get_response, method, payload)
48
+ started = Time.now
49
+ response = get_response.call
50
+ payload[:response] = response.body
51
+ body = JSON.parse(response.body)
52
+ handle_error(response, body)
53
+ body
54
+ rescue JSON::ParserError => raw_error
55
+ payload[:error] = raw_error
56
+ raise Passfort::Errors::UnparseableResponseError.new([], response)
57
+ rescue Excon::Errors::Timeout => raw_error
58
+ payload[:error] = raw_error
59
+ raise Passfort::Errors::TimeoutError
60
+ ensure
61
+ publish("passfort.#{method}", started, Time.now, SecureRandom.hex(10), payload)
62
+ end
63
+
64
+ # error codes: https://passfort.com/developer/v4/data-reference/ErrorCodes
65
+ def handle_error(response, body)
66
+ unless [200, 201].include?(response.status)
67
+ raise Passfort::Errors::RequestError.new([], response)
68
+ end
69
+
70
+ errors = body["errors"].is_a?(Array) ? body["errors"] : [body["errors"]]
71
+
72
+ handle_response_error(errors, response) if errors.any?
73
+ end
74
+
75
+ def handle_response_error(errors, response)
76
+ error_class = case errors[0]["code"]
77
+ when 201 then Passfort::Errors::InvalidInputDataError
78
+ when 204 then Passfort::Errors::InvalidAPIKeyError
79
+ when 104 then Passfort::Errors::ChargeableLimitReachedError
80
+ else Passfort::Errors::UnknownApiError
81
+ end
82
+ raise error_class.new(errors, response)
83
+ end
84
+
85
+ def publish(*args)
86
+ ActiveSupport::Notifications.publish(*args)
87
+ end
88
+ end
89
+ end
@@ -1,12 +1,12 @@
1
- # frozen_string_literal: true
2
-
3
- require "passfort/resource/base"
4
- require "passfort/resource/profile"
5
- require "passfort/resource/company_data"
6
- require "passfort/resource/task"
7
- require "passfort/resource/check"
8
-
9
- module Passfort
10
- module Resource
11
- end
12
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "passfort/resource/base"
4
+ require "passfort/resource/profile"
5
+ require "passfort/resource/company_data"
6
+ require "passfort/resource/task"
7
+ require "passfort/resource/check"
8
+
9
+ module Passfort
10
+ module Resource
11
+ end
12
+ end
@@ -1,21 +1,21 @@
1
- # frozen_string_literal: true
2
-
3
- require "active_support/core_ext/hash"
4
-
5
- module Passfort
6
- module Resource
7
- class Base
8
- def self.attributes(*keys)
9
- keys.each { |key| define_method(key) { @attributes[key] } }
10
- end
11
-
12
- def initialize(attributes)
13
- @attributes = attributes.with_indifferent_access
14
- end
15
-
16
- def to_h
17
- @attributes
18
- end
19
- end
20
- end
21
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash"
4
+
5
+ module Passfort
6
+ module Resource
7
+ class Base
8
+ def self.attributes(*keys)
9
+ keys.each { |key| define_method(key) { @attributes[key] } }
10
+ end
11
+
12
+ def initialize(attributes)
13
+ @attributes = attributes.with_indifferent_access
14
+ end
15
+
16
+ def to_h
17
+ @attributes
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,10 +1,10 @@
1
- # frozen_string_literal: true
2
-
3
- module Passfort
4
- module Resource
5
- class Check < Base
6
- attributes :id, :check_type, :state, :input_data, :output_data, :result,
7
- :performed_on, :errors, :task_ids, :instructed_externally
8
- end
9
- end
10
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Passfort
4
+ module Resource
5
+ class Check < Base
6
+ attributes :id, :check_type, :state, :input_data, :output_data, :result,
7
+ :performed_on, :errors, :task_ids, :instructed_externally
8
+ end
9
+ end
10
+ end
@@ -1,11 +1,11 @@
1
- # frozen_string_literal: true
2
-
3
- module Passfort
4
- module Resource
5
- class CompanyData < Base
6
- attributes :entity_type, :metadata, :authorized_persons,
7
- :unauthorized_persons, :ownership_structure, :officers,
8
- :filings, :customer_ref, :external_refs
9
- end
10
- end
11
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Passfort
4
+ module Resource
5
+ class CompanyData < Base
6
+ attributes :entity_type, :metadata, :authorized_persons,
7
+ :unauthorized_persons, :ownership_structure, :officers,
8
+ :filings, :customer_ref, :external_refs
9
+ end
10
+ end
11
+ end
@@ -1,11 +1,11 @@
1
- # frozen_string_literal: true
2
-
3
- module Passfort
4
- module Resource
5
- class IndividualData < Base
6
- attributes :entity_type, :personal_details, :address_history,
7
- :contact_details, :phone_number, :email, :ip_location,
8
- :documents
9
- end
10
- end
11
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Passfort
4
+ module Resource
5
+ class IndividualData < Base
6
+ attributes :entity_type, :personal_details, :address_history,
7
+ :contact_details, :phone_number, :email, :ip_location,
8
+ :documents
9
+ end
10
+ end
11
+ end
@@ -1,15 +1,15 @@
1
- # frozen_string_literal: true
2
-
3
- require "passfort/resource/task"
4
-
5
- module Passfort
6
- module Resource
7
- class Profile < Base
8
- attributes :id, :role, :collected_data, :verified_data, :checks,
9
- :collection_steps, :display_name, :applications, :task_types,
10
- :tasks, :events, :category, :status, :document_images, :tags,
11
- :risk, :unresolved_event_types, :task_progress,
12
- :has_associates, :has_collection_steps
13
- end
14
- end
15
- end
1
+ # frozen_string_literal: true
2
+
3
+ require "passfort/resource/task"
4
+
5
+ module Passfort
6
+ module Resource
7
+ class Profile < Base
8
+ attributes :id, :role, :collected_data, :verified_data, :checks,
9
+ :collection_steps, :display_name, :applications, :task_types,
10
+ :tasks, :events, :category, :status, :document_images, :tags,
11
+ :risk, :unresolved_event_types, :task_progress,
12
+ :has_associates, :has_collection_steps
13
+ end
14
+ end
15
+ end
@@ -1,9 +1,9 @@
1
- # frozen_string_literal: true
2
-
3
- module Passfort
4
- module Resource
5
- class Task < Base
6
- attributes :id, :type, :is_complete, :is_expired, :check_ids
7
- end
8
- end
9
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Passfort
4
+ module Resource
5
+ class Task < Base
6
+ attributes :id, :type, :is_complete, :is_expired, :check_ids
7
+ end
8
+ end
9
+ end
@@ -1,5 +1,5 @@
1
- # frozen_string_literal: true
2
-
3
- module Passfort
4
- VERSION = "0.2.3"
5
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Passfort
4
+ VERSION = "0.3.0"
5
+ end
@@ -1,29 +1,30 @@
1
- # frozen_string_literal: true
2
-
3
- lib = File.expand_path("lib", __dir__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
-
6
- require "passfort/version"
7
-
8
- Gem::Specification.new do |spec|
9
- spec.name = "passfort"
10
- spec.version = Passfort::VERSION
11
- spec.summary = "Client for the PassFort API"
12
- spec.authors = %w[GoCardless]
13
- spec.homepage = "https://github.com/gocardless/passfort"
14
- spec.email = %w[developers@gocardless.com]
15
- spec.license = "MIT"
16
-
17
- spec.files = `git ls-files -z`.split("\x0")
18
- spec.test_files = spec.files.grep(%r{^spec/})
19
- spec.require_paths = ["lib"]
20
-
21
- spec.add_dependency "activesupport", "~> 5.0"
22
- spec.add_dependency "excon", "~> 0.60"
23
-
24
- spec.add_development_dependency "gc_ruboconfig", "~> 2.3"
25
- spec.add_development_dependency "pry", "~> 0.10"
26
- spec.add_development_dependency "rspec", "~> 3.2"
27
- spec.add_development_dependency "rspec_junit_formatter", "~> 0.3"
28
- spec.add_development_dependency "webmock", "~> 3.3"
29
- end
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require "passfort/version"
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = "passfort"
10
+ spec.version = Passfort::VERSION
11
+ spec.summary = "Client for the PassFort API"
12
+ spec.authors = %w[GoCardless]
13
+ spec.homepage = "https://github.com/gocardless/passfort"
14
+ spec.email = %w[developers@gocardless.com]
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.test_files = spec.files.grep(%r{^spec/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "activesupport", "~> 5.0"
22
+ spec.add_dependency "excon", "~> 0.60"
23
+
24
+ spec.add_development_dependency "gc_ruboconfig", "~> 2.3"
25
+ spec.add_development_dependency "pry", "~> 0.10"
26
+ spec.add_development_dependency "rspec", "~> 3.2"
27
+ spec.add_development_dependency "rspec_junit_formatter", "~> 0.3"
28
+ spec.add_development_dependency "timecop", "~> 0.8"
29
+ spec.add_development_dependency "webmock", "~> 3.3"
30
+ end
@@ -1,67 +1,67 @@
1
- {
2
- "check_type": "IDENTITY_CHECK",
3
- "errors": [],
4
- "id": "6c1d594a-496e-11e7-911e-acbc32b67d7b",
5
- "input_data": {
6
- "address_history": [
7
- {
8
- "address": {
9
- "country": "Southend-on-Sea",
10
- "locality": "Southend-on-Sea",
11
- "postal_code": "1",
12
- "postal_town": "Leigh-on-Sea",
13
- "route": "The Fairway",
14
- "state_province": "England",
15
- "street_number": "106",
16
- "type": "STRUCTURED"
17
- },
18
- "end_date": "2017-05",
19
- "start_date": "2017-01"
20
- }
21
- ],
22
- "entity_type": "INDIVIDUAL",
23
- "personal_details": {
24
- "dob": "1987-01-01",
25
- "name": {
26
- "family_name": "Smith",
27
- "given_names": [
28
- "Thomas"
29
- ]
30
- }
31
- }
32
- },
33
- "output_data": {
34
- "credit_ref": {
35
- "exact": {
36
- "count": 8,
37
- "match": true
38
- },
39
- "exact_dob": {
40
- "count": 8,
41
- "match": true
42
- },
43
- "partial": {
44
- "count": 8,
45
- "match": true
46
- }
47
- },
48
- "db_matches": {
49
- "electoral_exact": {
50
- "count": 1,
51
- "match": true
52
- },
53
- "electoral_exact_dob": {
54
- "count": 0,
55
- "match": false
56
- },
57
- "electoral_partial": {
58
- "count": 1,
59
- "match": true
60
- }
61
- },
62
- "entity_type": "INDIVIDUAL"
63
- },
64
- "result": "2+2",
65
- "state": "COMPLETE",
66
- "task_ids": []
67
- }
1
+ {
2
+ "check_type": "IDENTITY_CHECK",
3
+ "errors": [],
4
+ "id": "6c1d594a-496e-11e7-911e-acbc32b67d7b",
5
+ "input_data": {
6
+ "address_history": [
7
+ {
8
+ "address": {
9
+ "country": "Southend-on-Sea",
10
+ "locality": "Southend-on-Sea",
11
+ "postal_code": "1",
12
+ "postal_town": "Leigh-on-Sea",
13
+ "route": "The Fairway",
14
+ "state_province": "England",
15
+ "street_number": "106",
16
+ "type": "STRUCTURED"
17
+ },
18
+ "end_date": "2017-05",
19
+ "start_date": "2017-01"
20
+ }
21
+ ],
22
+ "entity_type": "INDIVIDUAL",
23
+ "personal_details": {
24
+ "dob": "1987-01-01",
25
+ "name": {
26
+ "family_name": "Smith",
27
+ "given_names": [
28
+ "Thomas"
29
+ ]
30
+ }
31
+ }
32
+ },
33
+ "output_data": {
34
+ "credit_ref": {
35
+ "exact": {
36
+ "count": 8,
37
+ "match": true
38
+ },
39
+ "exact_dob": {
40
+ "count": 8,
41
+ "match": true
42
+ },
43
+ "partial": {
44
+ "count": 8,
45
+ "match": true
46
+ }
47
+ },
48
+ "db_matches": {
49
+ "electoral_exact": {
50
+ "count": 1,
51
+ "match": true
52
+ },
53
+ "electoral_exact_dob": {
54
+ "count": 0,
55
+ "match": false
56
+ },
57
+ "electoral_partial": {
58
+ "count": 1,
59
+ "match": true
60
+ }
61
+ },
62
+ "entity_type": "INDIVIDUAL"
63
+ },
64
+ "result": "2+2",
65
+ "state": "COMPLETE",
66
+ "task_ids": []
67
+ }