yoti 1.6.1 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE.md +17 -0
  3. data/.gitignore +1 -0
  4. data/CONTRIBUTING.md +1 -30
  5. data/README.md +23 -224
  6. data/Rakefile +9 -8
  7. data/lib/yoti.rb +50 -1
  8. data/lib/yoti/activity_details.rb +1 -0
  9. data/lib/yoti/client.rb +13 -4
  10. data/lib/yoti/configuration.rb +7 -2
  11. data/lib/yoti/data_type/age_verification.rb +1 -1
  12. data/lib/yoti/data_type/base_profile.rb +1 -1
  13. data/lib/yoti/data_type/document_details.rb +5 -13
  14. data/lib/yoti/data_type/image.rb +4 -12
  15. data/lib/yoti/data_type/image_jpeg.rb +2 -0
  16. data/lib/yoti/data_type/image_png.rb +2 -0
  17. data/lib/yoti/data_type/media.rb +19 -0
  18. data/lib/yoti/doc_scan/client.rb +163 -0
  19. data/lib/yoti/doc_scan/constants.rb +28 -0
  20. data/lib/yoti/doc_scan/session/create/create_session_result.rb +50 -0
  21. data/lib/yoti/doc_scan/session/create/document_filter.rb +31 -0
  22. data/lib/yoti/doc_scan/session/create/document_restrictions_filter.rb +140 -0
  23. data/lib/yoti/doc_scan/session/create/notification_config.rb +142 -0
  24. data/lib/yoti/doc_scan/session/create/orthogonal_restrictions_filter.rb +150 -0
  25. data/lib/yoti/doc_scan/session/create/requested_check.rb +39 -0
  26. data/lib/yoti/doc_scan/session/create/requested_document_authenticity_check.rb +53 -0
  27. data/lib/yoti/doc_scan/session/create/requested_face_match_check.rb +95 -0
  28. data/lib/yoti/doc_scan/session/create/requested_liveness_check.rb +108 -0
  29. data/lib/yoti/doc_scan/session/create/requested_task.rb +39 -0
  30. data/lib/yoti/doc_scan/session/create/requested_text_extraction_task.rb +94 -0
  31. data/lib/yoti/doc_scan/session/create/required_document.rb +31 -0
  32. data/lib/yoti/doc_scan/session/create/required_id_document.rb +53 -0
  33. data/lib/yoti/doc_scan/session/create/sdk_config.rb +221 -0
  34. data/lib/yoti/doc_scan/session/create/session_specification.rb +203 -0
  35. data/lib/yoti/doc_scan/session/retrieve/authenticity_check_response.rb +12 -0
  36. data/lib/yoti/doc_scan/session/retrieve/breakdown_response.rb +38 -0
  37. data/lib/yoti/doc_scan/session/retrieve/check_response.rb +63 -0
  38. data/lib/yoti/doc_scan/session/retrieve/details_response.rb +28 -0
  39. data/lib/yoti/doc_scan/session/retrieve/document_fields_response.rb +21 -0
  40. data/lib/yoti/doc_scan/session/retrieve/face_map_response.rb +21 -0
  41. data/lib/yoti/doc_scan/session/retrieve/face_match_check_response.rb +12 -0
  42. data/lib/yoti/doc_scan/session/retrieve/frame_response.rb +21 -0
  43. data/lib/yoti/doc_scan/session/retrieve/generated_check_response.rb +28 -0
  44. data/lib/yoti/doc_scan/session/retrieve/generated_media.rb +28 -0
  45. data/lib/yoti/doc_scan/session/retrieve/generated_text_data_check_response.rb +12 -0
  46. data/lib/yoti/doc_scan/session/retrieve/get_session_result.rb +113 -0
  47. data/lib/yoti/doc_scan/session/retrieve/id_document_resource_response.rb +52 -0
  48. data/lib/yoti/doc_scan/session/retrieve/liveness_check_response.rb +12 -0
  49. data/lib/yoti/doc_scan/session/retrieve/liveness_resource_response.rb +24 -0
  50. data/lib/yoti/doc_scan/session/retrieve/media_response.rb +38 -0
  51. data/lib/yoti/doc_scan/session/retrieve/page_response.rb +27 -0
  52. data/lib/yoti/doc_scan/session/retrieve/recommendation_response.rb +34 -0
  53. data/lib/yoti/doc_scan/session/retrieve/report_response.rb +31 -0
  54. data/lib/yoti/doc_scan/session/retrieve/resource_container.rb +50 -0
  55. data/lib/yoti/doc_scan/session/retrieve/resource_response.rb +39 -0
  56. data/lib/yoti/doc_scan/session/retrieve/task_response.rb +87 -0
  57. data/lib/yoti/doc_scan/session/retrieve/text_data_check_response.rb +12 -0
  58. data/lib/yoti/doc_scan/session/retrieve/text_extraction_task_response.rb +18 -0
  59. data/lib/yoti/doc_scan/session/retrieve/zoom_liveness_resource_response.rb +33 -0
  60. data/lib/yoti/doc_scan/support/supported_documents.rb +60 -0
  61. data/lib/yoti/dynamic_share_service/extension/thirdparty_attribute_extension.rb +62 -11
  62. data/lib/yoti/dynamic_share_service/policy/dynamic_policy.rb +17 -17
  63. data/lib/yoti/dynamic_share_service/share_url.rb +28 -33
  64. data/lib/yoti/errors.rb +17 -2
  65. data/lib/yoti/http/aml_check_request.rb +12 -6
  66. data/lib/yoti/http/payloads/aml_address.rb +4 -0
  67. data/lib/yoti/http/payloads/aml_profile.rb +7 -1
  68. data/lib/yoti/http/profile_request.rb +11 -6
  69. data/lib/yoti/http/request.rb +221 -18
  70. data/lib/yoti/http/signed_request.rb +13 -4
  71. data/lib/yoti/protobuf/main.rb +1 -1
  72. data/lib/yoti/ssl.rb +3 -2
  73. data/lib/yoti/util/anchor_processor.rb +1 -1
  74. data/lib/yoti/util/validation.rb +41 -0
  75. data/lib/yoti/version.rb +1 -1
  76. data/rubocop.yml +9 -1
  77. data/yoti.gemspec +5 -8
  78. metadata +53 -63
  79. data/lib/yoti/sandbox.rb +0 -5
  80. data/lib/yoti/sandbox/anchor.rb +0 -49
  81. data/lib/yoti/sandbox/attribute.rb +0 -52
  82. data/lib/yoti/sandbox/profile.rb +0 -171
  83. data/lib/yoti/sandbox/sandbox.rb +0 -105
  84. data/lib/yoti/sandbox/sandbox_client.rb +0 -45
  85. data/login_flow.png +0 -0
@@ -16,8 +16,8 @@ module Yoti
16
16
  false
17
17
  end
18
18
 
19
- def to_json(*args)
20
- as_json.to_json(*args)
19
+ def to_json(*_args)
20
+ as_json.to_json
21
21
  end
22
22
 
23
23
  def as_json(*_args)
@@ -105,19 +105,19 @@ module Yoti
105
105
  end
106
106
 
107
107
  def with_family_name(options = {})
108
- with_wanted_attribute_by_name Attribute::FAMILY_NAME, options
108
+ with_wanted_attribute_by_name Attribute::FAMILY_NAME, **options
109
109
  end
110
110
 
111
111
  def with_given_names(options = {})
112
- with_wanted_attribute_by_name Attribute::GIVEN_NAMES, options
112
+ with_wanted_attribute_by_name Attribute::GIVEN_NAMES, **options
113
113
  end
114
114
 
115
115
  def with_full_name(options = {})
116
- with_wanted_attribute_by_name Attribute::FULL_NAME, options
116
+ with_wanted_attribute_by_name Attribute::FULL_NAME, **options
117
117
  end
118
118
 
119
119
  def with_date_of_birth(options = {})
120
- with_wanted_attribute_by_name Attribute::DATE_OF_BIRTH, options
120
+ with_wanted_attribute_by_name Attribute::DATE_OF_BIRTH, **options
121
121
  end
122
122
 
123
123
  #
@@ -135,45 +135,45 @@ module Yoti
135
135
  end
136
136
 
137
137
  #
138
- # @param [Integer] derivation
138
+ # @param [Integer] age
139
139
  #
140
140
  def with_age_over(age, options = {})
141
- with_age_derived_attribute(Attribute::AGE_OVER + age.to_s, options)
141
+ with_age_derived_attribute(Attribute::AGE_OVER + age.to_s, **options)
142
142
  end
143
143
 
144
144
  #
145
- # @param [Integer] derivation
145
+ # @param [Integer] age
146
146
  #
147
147
  def with_age_under(age, options = {})
148
- with_age_derived_attribute(Attribute::AGE_UNDER + age.to_s, options)
148
+ with_age_derived_attribute(Attribute::AGE_UNDER + age.to_s, **options)
149
149
  end
150
150
 
151
151
  def with_gender(options = {})
152
- with_wanted_attribute_by_name Attribute::GENDER, options
152
+ with_wanted_attribute_by_name Attribute::GENDER, **options
153
153
  end
154
154
 
155
155
  def with_postal_address(options = {})
156
- with_wanted_attribute_by_name(Attribute::POSTAL_ADDRESS, options)
156
+ with_wanted_attribute_by_name(Attribute::POSTAL_ADDRESS, **options)
157
157
  end
158
158
 
159
159
  def with_structured_postal_address(options = {})
160
- with_wanted_attribute_by_name(Attribute::STRUCTURED_POSTAL_ADDRESS, options)
160
+ with_wanted_attribute_by_name(Attribute::STRUCTURED_POSTAL_ADDRESS, **options)
161
161
  end
162
162
 
163
163
  def with_nationality(options = {})
164
- with_wanted_attribute_by_name(Attribute::NATIONALITY, options)
164
+ with_wanted_attribute_by_name(Attribute::NATIONALITY, **options)
165
165
  end
166
166
 
167
167
  def with_phone_number(options = {})
168
- with_wanted_attribute_by_name(Attribute::PHONE_NUMBER, options)
168
+ with_wanted_attribute_by_name(Attribute::PHONE_NUMBER, **options)
169
169
  end
170
170
 
171
171
  def with_selfie(options = {})
172
- with_wanted_attribute_by_name(Attribute::SELFIE, options)
172
+ with_wanted_attribute_by_name(Attribute::SELFIE, **options)
173
173
  end
174
174
 
175
175
  def with_email(options = {})
176
- with_wanted_attribute_by_name(Attribute::EMAIL_ADDRESS, options)
176
+ with_wanted_attribute_by_name(Attribute::EMAIL_ADDRESS, **options)
177
177
  end
178
178
 
179
179
  def with_document_details
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'securerandom'
4
+
3
5
  module Yoti
4
6
  module DynamicSharingService
5
7
  class Share
@@ -11,50 +13,33 @@ module Yoti
11
13
  end
12
14
  end
13
15
 
14
- def self.create_share_url_endpoint
15
- "/qrcodes/apps/#{Yoti.configuration.client_sdk_id}"
16
- end
17
-
18
- def self.create_share_url_query
19
- "?nonce=#{SecureRandom.uuid}&timestamp=#{Time.now.to_i}"
20
- end
21
-
22
16
  def self.create_share_url(scenario)
23
- endpoint = "#{create_share_url_endpoint}#{create_share_url_query}"
24
- uri = URI("#{Yoti.configuration.api_endpoint}#{endpoint}")
25
-
26
- unsigned = Net::HTTP::Post.new uri
27
- unsigned.body = scenario.to_json
17
+ yoti_request = Yoti::Request
18
+ .builder
19
+ .with_http_method('POST')
20
+ .with_base_url(Yoti.configuration.api_endpoint)
21
+ .with_endpoint("qrcodes/apps/#{Yoti.configuration.client_sdk_id}")
22
+ .with_query_param('appId', Yoti.configuration.client_sdk_id)
23
+ .with_payload(scenario)
24
+ .build
28
25
 
29
- signed_request = Yoti::SignedRequest.new(
30
- unsigned,
31
- endpoint,
32
- scenario
33
- ).sign
26
+ begin
27
+ create_share_url_parse_response yoti_request.execute
28
+ rescue Yoti::RequestError => e
29
+ raise if e.response.nil?
34
30
 
35
- response = Net::HTTP.start(
36
- uri.hostname,
37
- uri.port,
38
- use_ssl: true
39
- ) do |http|
40
- http.request signed_request
41
- end
42
-
43
- create_share_url_parse_response response
44
- end
45
-
46
- def self.create_share_url_parse_response(response)
47
- if response.code.to_i < 200 || response.code.to_i >= 300
48
- case response.code
31
+ case e.response.code
49
32
  when '400'
50
33
  raise InvalidDataError
51
34
  when '404'
52
35
  raise ApplicationNotFoundError
53
36
  else
54
- raise UnknownHTTPError, response.code
37
+ raise UnknownHTTPError, e.response.code
55
38
  end
56
39
  end
40
+ end
57
41
 
42
+ def self.create_share_url_parse_response(response)
58
43
  Share.new JSON.parse response.body
59
44
  end
60
45
 
@@ -76,5 +61,15 @@ module Yoti
76
61
  super msg
77
62
  end
78
63
  end
64
+
65
+ # @deprecated no longer used - will be removed in 2.0.0
66
+ def self.create_share_url_query
67
+ "?nonce=#{SecureRandom.uuid}&timestamp=#{Time.now.to_i}"
68
+ end
69
+
70
+ # @deprecated no longer used - will be removed in 2.0.0
71
+ def self.create_share_url_endpoint
72
+ "/qrcodes/apps/#{Yoti.configuration.client_sdk_id}"
73
+ end
79
74
  end
80
75
  end
@@ -3,12 +3,27 @@ module Yoti
3
3
  class ProtobufError < StandardError; end
4
4
 
5
5
  # Raises exceptions related to API requests
6
- class RequestError < StandardError; end
6
+ class RequestError < StandardError
7
+ attr_reader :response
8
+
9
+ def initialize(message, response = nil)
10
+ super(append_response_message(message, response))
11
+ @response = response
12
+ end
13
+
14
+ private
15
+
16
+ def append_response_message(message, response)
17
+ return message if response.nil? || response.body.empty?
18
+
19
+ "#{message}: #{response.body}"
20
+ end
21
+ end
7
22
 
8
23
  # Raises exceptions related to OpenSSL actions
9
24
  class SslError < StandardError; end
10
25
 
11
- # Raises exceptions realted to an incorrect gem configuration value
26
+ # Raises exceptions related to an incorrect gem configuration value
12
27
  class ConfigurationError < StandardError; end
13
28
 
14
29
  # Raises exceptions related to AML actions
@@ -1,13 +1,16 @@
1
1
  module Yoti
2
2
  # Manage the API's AML check requests
3
3
  class AmlCheckRequest
4
+ #
5
+ # @param [AmlProfile] aml_profile
6
+ #
4
7
  def initialize(aml_profile)
5
8
  @aml_profile = aml_profile
6
9
  @payload = aml_profile.payload
7
10
  @request = request
8
11
  end
9
12
 
10
- # @return [String] a JSON representation of the AML check response
13
+ # @return [Hash] a JSON representation of the AML check response
11
14
  def response
12
15
  JSON.parse(@request.body)
13
16
  end
@@ -15,11 +18,14 @@ module Yoti
15
18
  private
16
19
 
17
20
  def request
18
- yoti_request = Yoti::Request.new
19
- yoti_request.http_method = 'POST'
20
- yoti_request.endpoint = 'aml-check'
21
- yoti_request.payload = @payload
22
- yoti_request
21
+ Yoti::Request
22
+ .builder
23
+ .with_http_method('POST')
24
+ .with_base_url(Yoti.configuration.api_endpoint)
25
+ .with_endpoint('aml-check')
26
+ .with_query_param('appId', Yoti.configuration.client_sdk_id)
27
+ .with_payload(@payload)
28
+ .build
23
29
  end
24
30
  end
25
31
  end
@@ -7,6 +7,10 @@ module Yoti
7
7
  # @return [String] the postcode required for USA, optional otherwise
8
8
  attr_accessor :post_code
9
9
 
10
+ #
11
+ # @param [String] country
12
+ # @param [String] post_code
13
+ #
10
14
  def initialize(country, post_code = nil)
11
15
  raise AmlError, 'AmlAddress requires a country.' if country.to_s.empty?
12
16
 
@@ -1,6 +1,12 @@
1
1
  module Yoti
2
2
  # Manages the AML check Profile object
3
3
  class AmlProfile
4
+ #
5
+ # @param [String] given_names
6
+ # @param [String] family_name
7
+ # @param [AmlAddress] aml_address
8
+ # @param [String] ssn
9
+ #
4
10
  def initialize(given_names, family_name, aml_address, ssn = nil)
5
11
  @given_names = given_names
6
12
  @family_name = family_name
@@ -11,7 +17,7 @@ module Yoti
11
17
  raise AmlError, 'Request for USA require a valid SSN and postcode.' if usa_invalid
12
18
  end
13
19
 
14
- # @return [Object] the AML check request body
20
+ # @return [Hash] the AML check request body
15
21
  def payload
16
22
  {
17
23
  given_names: @given_names,
@@ -1,6 +1,9 @@
1
1
  module Yoti
2
2
  # Manage the API's profile requests
3
3
  class ProfileRequest
4
+ #
5
+ # @param [String] encrypted_connect_token
6
+ #
4
7
  def initialize(encrypted_connect_token)
5
8
  @encrypted_connect_token = encrypted_connect_token
6
9
  @request = request
@@ -14,12 +17,14 @@ module Yoti
14
17
  private
15
18
 
16
19
  def request
17
- yoti_request = Yoti::Request.new
18
- yoti_request.add_header('X-Yoti-Auth-Key', Yoti::SSL.auth_key_from_pem)
19
- yoti_request.encrypted_connect_token = @encrypted_connect_token
20
- yoti_request.http_method = 'GET'
21
- yoti_request.endpoint = 'profile'
22
- yoti_request
20
+ Yoti::Request
21
+ .builder
22
+ .with_http_method('GET')
23
+ .with_base_url(Yoti.configuration.api_endpoint)
24
+ .with_endpoint("profile/#{Yoti::SSL.decrypt_token(@encrypted_connect_token)}")
25
+ .with_query_param('appId', Yoti.configuration.client_sdk_id)
26
+ .with_header('X-Yoti-Auth-Key', Yoti::SSL.auth_key_from_pem)
27
+ .build
23
28
  end
24
29
  end
25
30
  end
@@ -1,9 +1,19 @@
1
+ require 'securerandom'
2
+ require 'cgi'
3
+
1
4
  module Yoti
2
5
  # Manage the API's HTTPS requests
3
6
  class Request
7
+ # @deprecated will be removed in 2.0.0 - token is now provided with the endpoint
4
8
  # @return [String] the URL token received from Yoti Connect
5
9
  attr_accessor :encrypted_connect_token
6
10
 
11
+ # @return [String] the base URL
12
+ attr_writer :base_url
13
+
14
+ # @return [Hash] query params to add to the request
15
+ attr_accessor :query_params
16
+
7
17
  # @return [String] the HTTP method used for the request
8
18
  # The allowed methods are: GET, DELETE, POST, PUT, PATCH
9
19
  attr_accessor :http_method
@@ -11,36 +21,93 @@ module Yoti
11
21
  # @return [String] the API endpoint for the request
12
22
  attr_accessor :endpoint
13
23
 
14
- # @return [Hash] the body sent with the request
24
+ # @return [#to_json,String] the body sent with the request
15
25
  attr_accessor :payload
16
26
 
17
27
  def initialize
18
28
  @headers = {}
19
29
  end
20
30
 
31
+ #
32
+ # @return [RequestBuilder]
33
+ #
34
+ def self.builder
35
+ RequestBuilder.new
36
+ end
37
+
38
+ #
21
39
  # Adds a HTTP header to the request
40
+ #
41
+ # @param [String] header
42
+ # @param [String] value
43
+ #
22
44
  def add_header(header, value)
23
45
  @headers[header] = value
24
46
  end
25
47
 
48
+ #
26
49
  # Makes a HTTP request after signing the headers
27
- # @return [Hash] the body from the HTTP request
28
- def body
50
+ #
51
+ # @return [HTTPResponse]
52
+ #
53
+ def execute
29
54
  raise RequestError, 'The request requires a HTTP method.' unless @http_method
30
- raise RequestError, 'The payload needs to be a hash.' unless @payload.to_s.empty? || @payload.is_a?(Hash)
31
55
 
32
- res = Net::HTTP.start(uri.hostname, Yoti.configuration.api_port, use_ssl: https_uri?) do |http|
56
+ http_res = Net::HTTP.start(uri.hostname, Yoti.configuration.api_port, use_ssl: https_uri?) do |http|
33
57
  signed_request = SignedRequest.new(unsigned_request, path, @payload).sign
34
58
  http.request(signed_request)
35
59
  end
36
60
 
37
- raise RequestError, "Unsuccessful Yoti API call: #{res.message}" unless res.code == '200'
61
+ raise RequestError.new("Unsuccessful Yoti API call: #{http_res.message}", http_res) unless response_is_success(http_res)
38
62
 
39
- res.body
63
+ http_res
64
+ end
65
+
66
+ #
67
+ # Makes a HTTP request and returns the body after signing the headers
68
+ #
69
+ # @return [String]
70
+ #
71
+ def body
72
+ execute.body
73
+ end
74
+
75
+ #
76
+ # @return [String] the base URL
77
+ #
78
+ def base_url
79
+ @base_url ||= Yoti.configuration.api_endpoint
40
80
  end
41
81
 
42
82
  private
43
83
 
84
+ #
85
+ # @param [Net::HTTPResponse] http_res
86
+ #
87
+ # @return [Boolean]
88
+ #
89
+ def response_is_success(http_res)
90
+ http_res.code.to_i >= 200 && http_res.code.to_i < 300
91
+ end
92
+
93
+ #
94
+ # Adds payload to provided HTTP request
95
+ #
96
+ # @param [Net::HTTPRequest] http_req
97
+ #
98
+ def add_payload(http_req)
99
+ return if @payload.to_s.empty?
100
+
101
+ if @payload.is_a?(String)
102
+ http_req.body = @payload
103
+ elsif @payload.respond_to?(:to_json)
104
+ http_req.body = @payload.to_json
105
+ end
106
+ end
107
+
108
+ #
109
+ # @return [Net::HTTPRequest] the unsigned HTTP request
110
+ #
44
111
  def unsigned_request
45
112
  case @http_method
46
113
  when 'GET'
@@ -49,13 +116,13 @@ module Yoti
49
116
  http_req = Net::HTTP::Delete.new(uri)
50
117
  when 'POST'
51
118
  http_req = Net::HTTP::Post.new(uri)
52
- http_req.body = @payload.to_json unless @payload.to_s.empty?
119
+ add_payload(http_req)
53
120
  when 'PUT'
54
121
  http_req = Net::HTTP::Put.new(uri)
55
- http_req.body = @payload.to_json unless @payload.to_s.empty?
122
+ add_payload(http_req)
56
123
  when 'PATCH'
57
124
  http_req = Net::HTTP::Patch.new(uri)
58
- http_req.body = @payload.to_json unless @payload.to_s.empty?
125
+ add_payload(http_req)
59
126
  else
60
127
  raise RequestError, "Request method not allowed: #{@http_method}"
61
128
  end
@@ -67,22 +134,27 @@ module Yoti
67
134
  http_req
68
135
  end
69
136
 
137
+ #
138
+ # @return [URI] the full request URI
139
+ #
70
140
  def uri
71
- @uri ||= URI(Yoti.configuration.api_endpoint + path)
141
+ @uri ||= URI(base_url + path)
72
142
  end
73
143
 
144
+ #
145
+ # @return [String] the path with query string
146
+ #
74
147
  def path
75
148
  @path ||= begin
76
- nonce = SecureRandom.uuid
77
- timestamp = Time.now.to_i
78
-
79
- "/#{@endpoint}/#{token}"\
80
- "?nonce=#{nonce}"\
81
- "&timestamp=#{timestamp}"\
82
- "&appId=#{Yoti.configuration.client_sdk_id}"
149
+ "/#{@endpoint}/#{token}".chomp('/') + "?#{query_string}"
83
150
  end
84
151
  end
85
152
 
153
+ #
154
+ # @deprecated will be removed in 2.0.0 - token is now provided with the endpoint
155
+ #
156
+ # @return [String] the decrypted connect token
157
+ #
86
158
  def token
87
159
  return '' unless @encrypted_connect_token
88
160
 
@@ -92,5 +164,136 @@ module Yoti
92
164
  def https_uri?
93
165
  uri.scheme == 'https'
94
166
  end
167
+
168
+ #
169
+ # Builds query string including nonce and timestamp
170
+ #
171
+ # @return [String]
172
+ #
173
+ def query_string
174
+ params = {
175
+ nonce: SecureRandom.uuid,
176
+ timestamp: Time.now.to_i
177
+ }
178
+
179
+ if @query_params.nil?
180
+ # @deprecated this default will be removed in 2.0.0
181
+ # Append appId when no custom query params are provided.
182
+ params.merge!(appId: Yoti.configuration.client_sdk_id)
183
+ else
184
+ Validation.assert_is_a(Hash, @query_params, 'query_params')
185
+ params.merge!(@query_params)
186
+ end
187
+
188
+ params.map do |k, v|
189
+ CGI.escape(k.to_s) + '=' + CGI.escape(v.to_s)
190
+ end.join('&')
191
+ end
192
+ end
193
+
194
+ #
195
+ # Builder for {Request}
196
+ #
197
+ class RequestBuilder
198
+ def initialize
199
+ @headers = {}
200
+ @query_params = {}
201
+ end
202
+
203
+ #
204
+ # Sets the base URL
205
+ #
206
+ # @param [String] base_url
207
+ #
208
+ # @return [self]
209
+ #
210
+ def with_base_url(base_url)
211
+ Validation.assert_is_a(String, base_url, 'base_url')
212
+ @base_url = base_url
213
+ self
214
+ end
215
+
216
+ #
217
+ # Adds a HTTP header to the request
218
+ #
219
+ # @param [String] header
220
+ # @param [String] value
221
+ #
222
+ # @return [self]
223
+ #
224
+ def with_header(header, value)
225
+ Validation.assert_is_a(String, header, 'header')
226
+ Validation.assert_is_a(String, value, 'value')
227
+ @headers[header] = value
228
+ self
229
+ end
230
+
231
+ #
232
+ # Adds a query parameter to the request
233
+ #
234
+ # @param [String] key
235
+ # @param [String] value
236
+ #
237
+ # @return [self]
238
+ #
239
+ def with_query_param(key, value)
240
+ Validation.assert_is_a(String, key, 'key')
241
+ Validation.assert_is_a(String, value, 'value')
242
+ @query_params[key] = value
243
+ self
244
+ end
245
+
246
+ #
247
+ # Sets the HTTP method
248
+ #
249
+ # @param [String] http_method
250
+ #
251
+ # @return [self]
252
+ #
253
+ def with_http_method(http_method)
254
+ Validation.assert_is_a(String, http_method, 'http_method')
255
+ @http_method = http_method
256
+ self
257
+ end
258
+
259
+ #
260
+ # Sets the API endpoint for the request
261
+ #
262
+ # @param [String] endpoint
263
+ #
264
+ # @return [self]
265
+ #
266
+ def with_endpoint(endpoint)
267
+ Validation.assert_is_a(String, endpoint, 'endpoint')
268
+ @endpoint = endpoint
269
+ self
270
+ end
271
+
272
+ #
273
+ # Sets the body sent with the request
274
+ #
275
+ # @param [#to_json,String] payload
276
+ #
277
+ # @return [self]
278
+ #
279
+ def with_payload(payload)
280
+ Validation.assert_respond_to(:to_json, payload, 'payload') unless payload.is_a?(String)
281
+ @payload = payload
282
+ self
283
+ end
284
+
285
+ #
286
+ # @return [Request]
287
+ #
288
+ def build
289
+ request = Request.new
290
+ request.base_url = @base_url
291
+ request.endpoint = @endpoint
292
+ request.query_params = @query_params
293
+ request.http_method = @http_method
294
+ request.payload = @payload
295
+ @headers.map { |k, v| request.add_header(k, v) }
296
+ request
297
+ end
95
298
  end
96
299
  end