diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..e31c60ea --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,18 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.0.0 + hooks: + - id: trailing-whitespace + - id: check-yaml + - repo: local + hooks: + - id: lint + name: 'lint' + entry: bundle exec rake rubocop + language: system + description: "Lint" + - id: test + name: 'test' + entry: bundle exec rake spec + language: system + description: "Test" diff --git a/examples/doc_scan/README.md b/examples/doc_scan/README.md index 860f94e5..4b7b2f6a 100644 --- a/examples/doc_scan/README.md +++ b/examples/doc_scan/README.md @@ -3,7 +3,7 @@ 1. Create your application in the [Yoti Hub](https://hub.yoti.com) 1. Set the application domain of your app to `localhost:3002` 1. Rename the [.env.example](.env.example) file to `.env` -1. Fill in the environment variables in this file with the ones specific to your application (mentioned in the [Configuration](#configuration) section) +1. Fill in the environment variables in this file with the ones specific to your application, generated in the Yoti Hub when you create (and then publish) your application 1. Install the dependencies by running the following commands from this folder ```shell $ bundle install diff --git a/examples/doc_scan/app/controllers/yoti_controller.rb b/examples/doc_scan/app/controllers/yoti_controller.rb index cf13b4bd..95299020 100644 --- a/examples/doc_scan/app/controllers/yoti_controller.rb +++ b/examples/doc_scan/app/controllers/yoti_controller.rb @@ -1,5 +1,3 @@ -require 'base64' - class YotiController < ApplicationController # # Create Session @@ -27,6 +25,11 @@ def index .with_manual_check_never .build ) + .with_requested_check( + Yoti::DocScan::Session::Create::RequestedIdDocumentComparisonCheck + .builder + .build + ) .with_requested_task( Yoti::DocScan::Session::Create::RequestedTextExtractionTask .builder @@ -47,6 +50,28 @@ def index .with_error_url("#{request.base_url}/error") .build ) + .with_required_document( + Yoti::DocScan::Session::Create::RequiredIdDocument + .builder + .with_filter( + Yoti::DocScan::Session::Create::OrthogonalRestrictionsFilter + .builder + .with_included_document_types(['PASSPORT']) + .build + ) + .build + ) + .with_required_document( + Yoti::DocScan::Session::Create::RequiredIdDocument + .builder + .with_filter( + Yoti::DocScan::Session::Create::OrthogonalRestrictionsFilter + .builder + .with_included_document_types(['DRIVING_LICENCE']) + .build + ) + .build + ) .build create_session = Yoti::DocScan::Client.create_session(session_spec) @@ -73,15 +98,7 @@ def media media = Yoti::DocScan::Client.get_media_content(session[:DOC_SCAN_SESSION_ID], media_id) - body = media.content - content_type = media.mime_type - - if request.query_parameters[:base64Zip] == '1' && content_type == 'application/octet-stream' - body = Base64.strict_decode64(body) - content_type = 'application/zip' - end - - render body: body, content_type: content_type + render body: media.content, content_type: media.mime_type end # diff --git a/examples/doc_scan/app/views/yoti/success.html.erb b/examples/doc_scan/app/views/yoti/success.html.erb index 0c0f9dea..81b3de5c 100644 --- a/examples/doc_scan/app/views/yoti/success.html.erb +++ b/examples/doc_scan/app/views/yoti/success.html.erb @@ -34,6 +34,14 @@ User Tracking ID <%= @session_result.user_tracking_id %> + <% if @session_result.biometric_consent_timestamp %> + + Biometric Consent Timestamp + + <%= @session_result.biometric_consent_timestamp %> + + + <% end %> @@ -137,6 +145,29 @@ <% end %> + + <% if @session_result.id_document_comparison_checks.count.positive? %> +
+
+

+ +

+
+ +
+
+ <% @session_result.id_document_comparison_checks.each do |check| %> + <%= render partial: "check", locals: {check: check} %> + <% end %> +
+
+
+ <% end %> + @@ -338,39 +369,6 @@
- <% unless liveness_resource.facemap.nil? %> -
-
-

- -

-
-
-
- <% unless liveness_resource.facemap.media.nil? %> -

Media

- - - - - - - -
ID - - <%= liveness_resource.facemap.media.id %> - -
- <% end %> -
-
-
- <% end %> - <% if liveness_resource.frames.count.positive? %>
diff --git a/lib/yoti.rb b/lib/yoti.rb index c6a0f85e..6c96ecd4 100644 --- a/lib/yoti.rb +++ b/lib/yoti.rb @@ -56,6 +56,7 @@ require_relative 'yoti/doc_scan/session/create/create_session_result' require_relative 'yoti/doc_scan/session/create/requested_check' require_relative 'yoti/doc_scan/session/create/requested_document_authenticity_check' +require_relative 'yoti/doc_scan/session/create/requested_id_document_comparison_check' require_relative 'yoti/doc_scan/session/create/requested_face_match_check' require_relative 'yoti/doc_scan/session/create/requested_liveness_check' require_relative 'yoti/doc_scan/session/create/requested_task' @@ -72,6 +73,7 @@ require_relative 'yoti/doc_scan/session/retrieve/check_response' require_relative 'yoti/doc_scan/session/retrieve/resource_response' require_relative 'yoti/doc_scan/session/retrieve/authenticity_check_response' +require_relative 'yoti/doc_scan/session/retrieve/id_document_comparison_check_response' require_relative 'yoti/doc_scan/session/retrieve/breakdown_response' require_relative 'yoti/doc_scan/session/retrieve/details_response' require_relative 'yoti/doc_scan/session/retrieve/document_fields_response' diff --git a/lib/yoti/doc_scan/constants.rb b/lib/yoti/doc_scan/constants.rb index 9a9809fc..51208ec1 100644 --- a/lib/yoti/doc_scan/constants.rb +++ b/lib/yoti/doc_scan/constants.rb @@ -4,6 +4,7 @@ module Yoti module DocScan class Constants ID_DOCUMENT_AUTHENTICITY = 'ID_DOCUMENT_AUTHENTICITY' + ID_DOCUMENT_COMPARISON = 'ID_DOCUMENT_COMPARISON' ID_DOCUMENT_TEXT_DATA_CHECK = 'ID_DOCUMENT_TEXT_DATA_CHECK' ID_DOCUMENT_FACE_MATCH = 'ID_DOCUMENT_FACE_MATCH' LIVENESS = 'LIVENESS' diff --git a/lib/yoti/doc_scan/session/create/requested_document_authenticity_check.rb b/lib/yoti/doc_scan/session/create/requested_document_authenticity_check.rb index 63dca7e0..6a955d27 100644 --- a/lib/yoti/doc_scan/session/create/requested_document_authenticity_check.rb +++ b/lib/yoti/doc_scan/session/create/requested_document_authenticity_check.rb @@ -30,8 +30,18 @@ def self.builder # The configuration applied when creating a {RequestedDocumentAuthenticityCheck} # class RequestedDocumentAuthenticityCheckConfig + # + # @param [String] manual_check + # + def initialize(manual_check) + Validation.assert_is_a(String, manual_check, 'manual_check', true) + @manual_check = manual_check + end + def as_json(*_args) - {} + { + manual_check: @manual_check + }.compact end end @@ -39,11 +49,43 @@ def as_json(*_args) # Builder to assist the creation of {RequestedDocumentAuthenticityCheck} # class RequestedDocumentAuthenticityCheckBuilder + # + # Requires that a manual follow-up check is always performed + # + # @return [self] + # + def with_manual_check_always + @manual_check = Constants::ALWAYS + self + end + + # + # Requires that a manual follow-up check is performed only on failed checks, + # and those with a low level of confidence + # + # @return [self] + # + def with_manual_check_fallback + @manual_check = Constants::FALLBACK + self + end + + # + # Requires that only an automated check is performed. No manual follow-up + # Check will ever be initiated + # + # @return [self] + # + def with_manual_check_never + @manual_check = Constants::NEVER + self + end + # # @return [RequestedDocumentAuthenticityCheck] # def build - config = RequestedDocumentAuthenticityCheckConfig.new + config = RequestedDocumentAuthenticityCheckConfig.new(@manual_check) RequestedDocumentAuthenticityCheck.new(config) end end diff --git a/lib/yoti/doc_scan/session/create/requested_face_match_check.rb b/lib/yoti/doc_scan/session/create/requested_face_match_check.rb index d45cd2d0..97284011 100644 --- a/lib/yoti/doc_scan/session/create/requested_face_match_check.rb +++ b/lib/yoti/doc_scan/session/create/requested_face_match_check.rb @@ -63,7 +63,7 @@ def with_manual_check_always end # - # Requires that a manual follow-up check is performed only on failed Checks, + # Requires that a manual follow-up check is performed only on failed checks, # and those with a low level of confidence # # @return [self] @@ -74,7 +74,7 @@ def with_manual_check_fallback end # - # Requires that only an automated Check is performed. No manual follow-up + # Requires that only an automated check is performed. No manual follow-up # Check will ever be initiated # # @return [self] diff --git a/lib/yoti/doc_scan/session/create/requested_id_document_comparison_check.rb b/lib/yoti/doc_scan/session/create/requested_id_document_comparison_check.rb new file mode 100644 index 00000000..9cab93f7 --- /dev/null +++ b/lib/yoti/doc_scan/session/create/requested_id_document_comparison_check.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module Yoti + module DocScan + module Session + module Create + # + # Requests creation of an ID Document Comparison Check + # + class RequestedIdDocumentComparisonCheck < RequestedCheck + def initialize(config) + Validation.assert_is_a( + RequestedIdDocumentComparisonCheckConfig, + config, + 'config' + ) + + super(Constants::ID_DOCUMENT_COMPARISON, config) + end + + # + # @return [RequestedIdDocumentComparisonCheckBuilder] + # + def self.builder + RequestedIdDocumentComparisonCheckBuilder.new + end + end + + # + # The configuration applied when creating a {RequestedIdDocumentComparisonCheck} + # + class RequestedIdDocumentComparisonCheckConfig + def as_json(*_args) + {} + end + end + + # + # Builder to assist the creation of {RequestedIdDocumentComparisonCheck} + # + class RequestedIdDocumentComparisonCheckBuilder + # + # @return [RequestedIdDocumentComparisonCheck] + # + def build + config = RequestedIdDocumentComparisonCheckConfig.new + RequestedIdDocumentComparisonCheck.new(config) + end + end + end + end + end +end diff --git a/lib/yoti/doc_scan/session/create/session_specification.rb b/lib/yoti/doc_scan/session/create/session_specification.rb index 4376f2e5..94adb9be 100644 --- a/lib/yoti/doc_scan/session/create/session_specification.rb +++ b/lib/yoti/doc_scan/session/create/session_specification.rb @@ -14,6 +14,7 @@ class SessionSpecification # @param [Array] requested_tasks # @param [SdkConfig] sdk_config # @param [Array] required_documents + # @param [Boolean] block_biometric_consent # def initialize( client_session_token_ttl, @@ -23,7 +24,8 @@ def initialize( requested_checks, requested_tasks, sdk_config, - required_documents + required_documents, + block_biometric_consent = nil ) Validation.assert_is_a(Integer, client_session_token_ttl, 'client_session_token_ttl', true) @client_session_token_ttl = client_session_token_ttl @@ -48,6 +50,8 @@ def initialize( Validation.assert_is_a(Array, required_documents, 'required_documents', true) @required_documents = required_documents + + @block_biometric_consent = block_biometric_consent end def to_json(*_args) @@ -63,7 +67,8 @@ def as_json(*_args) requested_checks: @requested_checks.map(&:as_json), requested_tasks: @requested_tasks.map(&:as_json), sdk_config: @sdk_config, - required_documents: @required_documents.map(&:as_json) + required_documents: @required_documents.map(&:as_json), + block_biometric_consent: @block_biometric_consent }.compact end @@ -181,6 +186,18 @@ def with_required_document(required_document) self end + # + # Whether or not to block the collection of biometric consent + # + # @param [Boolean] block_biometric_consent + # + # @return [self] + # + def with_block_biometric_consent(block_biometric_consent) + @block_biometric_consent = block_biometric_consent + self + end + # # @return [SessionSpecification] # @@ -193,7 +210,8 @@ def build @requested_checks, @requested_tasks, @sdk_config, - @required_documents + @required_documents, + @block_biometric_consent ) end end diff --git a/lib/yoti/doc_scan/session/retrieve/get_session_result.rb b/lib/yoti/doc_scan/session/retrieve/get_session_result.rb index c647fe06..5f594e23 100644 --- a/lib/yoti/doc_scan/session/retrieve/get_session_result.rb +++ b/lib/yoti/doc_scan/session/retrieve/get_session_result.rb @@ -26,6 +26,9 @@ class GetSessionResult # @return [ResourceContainer] attr_reader :resources + # @return [DateTime] + attr_reader :biometric_consent_timestamp + # # @param [Hash] response # @@ -53,6 +56,8 @@ def initialize(response) end @resources = ResourceContainer.new(response['resources']) unless response['resources'].nil? + + @biometric_consent_timestamp = DateTime.parse(response['biometric_consent']) unless response['biometric_consent'].nil? end # @@ -83,6 +88,13 @@ def liveness_checks @checks.select { |check| check.is_a?(LivenessCheckResponse) } end + # + # @return [Array] + # + def id_document_comparison_checks + @checks.select { |check| check.is_a?(IdDocumentComparisonCheckResponse) } + end + private # @@ -95,6 +107,8 @@ def map_checks(checks) case check['type'] when Constants::ID_DOCUMENT_AUTHENTICITY AuthenticityCheckResponse.new(check) + when Constants::ID_DOCUMENT_COMPARISON + IdDocumentComparisonCheckResponse.new(check) when Constants::ID_DOCUMENT_FACE_MATCH FaceMatchCheckResponse.new(check) when Constants::ID_DOCUMENT_TEXT_DATA_CHECK diff --git a/lib/yoti/doc_scan/session/retrieve/id_document_comparison_check_response.rb b/lib/yoti/doc_scan/session/retrieve/id_document_comparison_check_response.rb new file mode 100644 index 00000000..0a39d90e --- /dev/null +++ b/lib/yoti/doc_scan/session/retrieve/id_document_comparison_check_response.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Yoti + module DocScan + module Session + module Retrieve + class IdDocumentComparisonCheckResponse < CheckResponse + end + end + end + end +end diff --git a/lib/yoti/version.rb b/lib/yoti/version.rb index 490cfd1b..725e512d 100644 --- a/lib/yoti/version.rb +++ b/lib/yoti/version.rb @@ -1,4 +1,4 @@ module Yoti # @return [String] the gem's current version - VERSION = '1.8.0'.freeze + VERSION = '1.9.0'.freeze end diff --git a/sonar-project.properties b/sonar-project.properties index 257239c6..f803cf33 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -3,7 +3,7 @@ sonar.organization = getyoti sonar.projectKey = getyoti:ruby sonar.projectName = Ruby SDK -sonar.projectVersion = 1.8.0 +sonar.projectVersion = 1.9.0 sonar.language = ruby sonar.exclusions = **/protobuf/**.rb, coverage/**, spec/sample-data/** diff --git a/spec/yoti/doc_scan/session/create/requested_document_authenticity_check_spec.rb b/spec/yoti/doc_scan/session/create/requested_document_authenticity_check_spec.rb index 28d34ca1..50338acc 100644 --- a/spec/yoti/doc_scan/session/create/requested_document_authenticity_check_spec.rb +++ b/spec/yoti/doc_scan/session/create/requested_document_authenticity_check_spec.rb @@ -3,16 +3,72 @@ require 'spec_helper' describe 'Yoti::DocScan::Session::Create::RequestedDocumentAuthenticityCheck' do - it 'serializes correctly' do - check = Yoti::DocScan::Session::Create::RequestedDocumentAuthenticityCheck - .builder - .build + describe 'without manual check config' do + it 'serializes correctly' do + check = Yoti::DocScan::Session::Create::RequestedDocumentAuthenticityCheck + .builder + .build - expected = { - type: 'ID_DOCUMENT_AUTHENTICITY', - config: {} - } + expected = { + type: 'ID_DOCUMENT_AUTHENTICITY', + config: {} + } - expect(check.to_json).to eql expected.to_json + expect(check.to_json).to eql expected.to_json + end + end + + describe 'with manual check always' do + it 'serializes correctly' do + check = Yoti::DocScan::Session::Create::RequestedDocumentAuthenticityCheck + .builder + .with_manual_check_always + .build + + expected = { + type: 'ID_DOCUMENT_AUTHENTICITY', + config: { + manual_check: 'ALWAYS' + } + } + + expect(check.to_json).to eql expected.to_json + end + end + + describe 'with manual check never' do + it 'serializes correctly' do + check = Yoti::DocScan::Session::Create::RequestedDocumentAuthenticityCheck + .builder + .with_manual_check_never + .build + + expected = { + type: 'ID_DOCUMENT_AUTHENTICITY', + config: { + manual_check: 'NEVER' + } + } + + expect(check.to_json).to eql expected.to_json + end + end + + describe 'with manual check fallback' do + it 'serializes correctly' do + check = Yoti::DocScan::Session::Create::RequestedDocumentAuthenticityCheck + .builder + .with_manual_check_fallback + .build + + expected = { + type: 'ID_DOCUMENT_AUTHENTICITY', + config: { + manual_check: 'FALLBACK' + } + } + + expect(check.to_json).to eql expected.to_json + end end end diff --git a/spec/yoti/doc_scan/session/create/session_specification_spec.rb b/spec/yoti/doc_scan/session/create/session_specification_spec.rb index 7f1e1333..df946e80 100644 --- a/spec/yoti/doc_scan/session/create/session_specification_spec.rb +++ b/spec/yoti/doc_scan/session/create/session_specification_spec.rb @@ -29,6 +29,10 @@ .builder .build + some_comparison_check = Yoti::DocScan::Session::Create::RequestedIdDocumentComparisonCheck + .builder + .build + some_document = Yoti::DocScan::Session::Create::RequiredIdDocument .builder .with_filter( @@ -48,6 +52,7 @@ .with_requested_check(some_face_match_check) .with_requested_check(some_liveness_check) .with_requested_check(some_authenticity_check) + .with_requested_check(some_comparison_check) .with_requested_task(some_text_extraction_task) .with_notifications(some_notification_config) .with_required_document(some_document) @@ -61,7 +66,8 @@ requested_checks: [ some_face_match_check, some_liveness_check, - some_authenticity_check + some_authenticity_check, + some_comparison_check ], requested_tasks: [ some_text_extraction_task @@ -74,4 +80,40 @@ expect(spec.to_json).to eql expected.to_json end + + context 'with block biometric consent true' do + it 'serializes correctly' do + spec = Yoti::DocScan::Session::Create::SessionSpecification + .builder + .with_block_biometric_consent(true) + .build + + expected = { + requested_checks: [], + requested_tasks: [], + required_documents: [], + block_biometric_consent: true + } + + expect(spec.to_json).to eql expected.to_json + end + end + + context 'with block biometric consent false' do + it 'serializes correctly' do + spec = Yoti::DocScan::Session::Create::SessionSpecification + .builder + .with_block_biometric_consent(false) + .build + + expected = { + requested_checks: [], + requested_tasks: [], + required_documents: [], + block_biometric_consent: false + } + + expect(spec.to_json).to eql expected.to_json + end + end end diff --git a/spec/yoti/doc_scan/session/retrieve/get_session_result_spec.rb b/spec/yoti/doc_scan/session/retrieve/get_session_result_spec.rb index a77fbb7d..49deffb1 100644 --- a/spec/yoti/doc_scan/session/retrieve/get_session_result_spec.rb +++ b/spec/yoti/doc_scan/session/retrieve/get_session_result_spec.rb @@ -8,6 +8,7 @@ 'user_tracking_id' => 'some-user-id', 'state' => 'some-state', 'client_session_token' => 'some-token', + 'biometric_consent' => '2006-02-02T22:04:05.123Z', 'resources' => { 'id_documents' => [], 'liveness_capture' => [] @@ -25,6 +26,9 @@ { 'type' => 'ID_DOCUMENT_TEXT_DATA_CHECK' }, + { + 'type' => 'ID_DOCUMENT_COMPARISON' + }, {} ] ) @@ -64,11 +68,12 @@ describe 'when checks are available' do it 'should return array of checks' do checks = session.checks - expect(checks.length).to eql(5) + expect(checks.length).to eql(6) expect(checks[0]).to be_a(Yoti::DocScan::Session::Retrieve::AuthenticityCheckResponse) expect(checks[1]).to be_a(Yoti::DocScan::Session::Retrieve::LivenessCheckResponse) expect(checks[2]).to be_a(Yoti::DocScan::Session::Retrieve::FaceMatchCheckResponse) expect(checks[3]).to be_a(Yoti::DocScan::Session::Retrieve::TextDataCheckResponse) + expect(checks[4]).to be_a(Yoti::DocScan::Session::Retrieve::IdDocumentComparisonCheckResponse) expect(checks).to all(be_a(Yoti::DocScan::Session::Retrieve::CheckResponse)) end end @@ -115,9 +120,32 @@ end end + describe '.id_document_comparison_checks' do + it 'should return array of IdDocumentComparisonResponse)' do + checks = session.id_document_comparison_checks + expect(checks.length).to eql(1) + expect(checks[0]).to be_a(Yoti::DocScan::Session::Retrieve::IdDocumentComparisonCheckResponse) + end + end + describe '.resources' do it 'should return resource container' do expect(session.resources).to be_a(Yoti::DocScan::Session::Retrieve::ResourceContainer) end end + + describe '.biometric_consent_timestamp' do + context 'when consent is provided' do + it 'should return timestamp as DateTime' do + biometric_consent_timestamp = session.biometric_consent_timestamp + expect(biometric_consent_timestamp).to eql(DateTime.new(2006, 2, 2, 22, 4, 5.123)) + end + end + context 'when consent is not provided' do + it 'should return nil' do + session = Yoti::DocScan::Session::Retrieve::GetSessionResult.new({}) + expect(session.biometric_consent_timestamp).to be_nil + end + end + end end diff --git a/yoti.gemspec b/yoti.gemspec index 0af2d032..f9c4a20d 100644 --- a/yoti.gemspec +++ b/yoti.gemspec @@ -21,6 +21,7 @@ Gem::Specification.new do |spec| exclude_patterns = [ '^(test|spec|features|examples|docs|.github)/', '^.gitignore$', + '^.pre-commit-config.yaml$', '^sonar-project.properties$', '^.dependabot/config.yml$', '^.travis.yml$',