voltaria_sdk 2.34.5

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 (209) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +69 -0
  3. data/README.md +202 -0
  4. data/Rakefile +20 -0
  5. data/custom.gemspec.rb +16 -0
  6. data/lib/voltaria/accounts/client.rb +168 -0
  7. data/lib/voltaria/accounts/types/get_client_account_request.rb +12 -0
  8. data/lib/voltaria/accounts/types/list_client_account_fields_request.rb +11 -0
  9. data/lib/voltaria/accounts/types/list_client_accounts_request.rb +14 -0
  10. data/lib/voltaria/accounts/types/partner_client_account_create_request.rb +23 -0
  11. data/lib/voltaria/accounts/types/partner_client_account_create_request_status.rb +14 -0
  12. data/lib/voltaria/client.rb +74 -0
  13. data/lib/voltaria/clients/client.rb +557 -0
  14. data/lib/voltaria/clients/types/approve_onboarding_request.rb +11 -0
  15. data/lib/voltaria/clients/types/client_create_payload.rb +16 -0
  16. data/lib/voltaria/clients/types/client_data_create_payload.rb +12 -0
  17. data/lib/voltaria/clients/types/client_user_invite_request.rb +16 -0
  18. data/lib/voltaria/clients/types/delete_client_request.rb +11 -0
  19. data/lib/voltaria/clients/types/get_client_by_id_request.rb +11 -0
  20. data/lib/voltaria/clients/types/get_limit_request_request.rb +11 -0
  21. data/lib/voltaria/clients/types/limit_request_create_payload.rb +14 -0
  22. data/lib/voltaria/clients/types/limit_request_create_payload_requested_limit.rb +15 -0
  23. data/lib/voltaria/clients/types/list_client_checklist_summaries_request.rb +13 -0
  24. data/lib/voltaria/clients/types/list_client_waivers_request.rb +15 -0
  25. data/lib/voltaria/clients/types/list_clients_request.rb +14 -0
  26. data/lib/voltaria/clients/types/list_limit_requests_request.rb +15 -0
  27. data/lib/voltaria/clients/types/list_onboarding_clients_request.rb +12 -0
  28. data/lib/voltaria/clients/types/reject_onboarding_request.rb +11 -0
  29. data/lib/voltaria/documents/client.rb +221 -0
  30. data/lib/voltaria/documents/types/delete_document_request.rb +11 -0
  31. data/lib/voltaria/documents/types/document_create_payload.rb +14 -0
  32. data/lib/voltaria/documents/types/get_document_by_id_request.rb +11 -0
  33. data/lib/voltaria/documents/types/list_documents_request.rb +18 -0
  34. data/lib/voltaria/drawdowns/client.rb +141 -0
  35. data/lib/voltaria/drawdowns/types/drawdown_create_payload.rb +12 -0
  36. data/lib/voltaria/drawdowns/types/drawdown_create_payload_amount.rb +15 -0
  37. data/lib/voltaria/drawdowns/types/list_drawdown_checklists_request.rb +15 -0
  38. data/lib/voltaria/drawdowns/types/list_drawdowns_request.rb +14 -0
  39. data/lib/voltaria/environment.rb +8 -0
  40. data/lib/voltaria/errors/api_error.rb +8 -0
  41. data/lib/voltaria/errors/client_error.rb +17 -0
  42. data/lib/voltaria/errors/redirect_error.rb +8 -0
  43. data/lib/voltaria/errors/response_error.rb +42 -0
  44. data/lib/voltaria/errors/server_error.rb +11 -0
  45. data/lib/voltaria/errors/timeout_error.rb +8 -0
  46. data/lib/voltaria/installments/client.rb +292 -0
  47. data/lib/voltaria/installments/types/delete_installment_request.rb +11 -0
  48. data/lib/voltaria/installments/types/get_installment_by_id_request.rb +11 -0
  49. data/lib/voltaria/installments/types/installment_create_payload.rb +12 -0
  50. data/lib/voltaria/installments/types/installment_edit_payload.rb +13 -0
  51. data/lib/voltaria/installments/types/installment_edit_payload_amount.rb +15 -0
  52. data/lib/voltaria/installments/types/list_installments_request.rb +16 -0
  53. data/lib/voltaria/installments/types/list_payment_promises_request.rb +16 -0
  54. data/lib/voltaria/installments/types/payment_promise_create_payload.rb +14 -0
  55. data/lib/voltaria/installments/types/payment_promise_create_payload_amount.rb +15 -0
  56. data/lib/voltaria/internal/errors/constraint_error.rb +10 -0
  57. data/lib/voltaria/internal/errors/type_error.rb +10 -0
  58. data/lib/voltaria/internal/http/base_request.rb +51 -0
  59. data/lib/voltaria/internal/http/raw_client.rb +214 -0
  60. data/lib/voltaria/internal/iterators/cursor_item_iterator.rb +28 -0
  61. data/lib/voltaria/internal/iterators/cursor_page_iterator.rb +51 -0
  62. data/lib/voltaria/internal/iterators/item_iterator.rb +59 -0
  63. data/lib/voltaria/internal/iterators/offset_item_iterator.rb +30 -0
  64. data/lib/voltaria/internal/iterators/offset_page_iterator.rb +83 -0
  65. data/lib/voltaria/internal/json/request.rb +41 -0
  66. data/lib/voltaria/internal/json/serializable.rb +25 -0
  67. data/lib/voltaria/internal/multipart/multipart_encoder.rb +141 -0
  68. data/lib/voltaria/internal/multipart/multipart_form_data.rb +78 -0
  69. data/lib/voltaria/internal/multipart/multipart_form_data_part.rb +51 -0
  70. data/lib/voltaria/internal/multipart/multipart_request.rb +40 -0
  71. data/lib/voltaria/internal/types/array.rb +47 -0
  72. data/lib/voltaria/internal/types/boolean.rb +34 -0
  73. data/lib/voltaria/internal/types/enum.rb +56 -0
  74. data/lib/voltaria/internal/types/hash.rb +36 -0
  75. data/lib/voltaria/internal/types/model/field.rb +38 -0
  76. data/lib/voltaria/internal/types/model.rb +208 -0
  77. data/lib/voltaria/internal/types/type.rb +35 -0
  78. data/lib/voltaria/internal/types/union.rb +161 -0
  79. data/lib/voltaria/internal/types/unknown.rb +15 -0
  80. data/lib/voltaria/internal/types/utils.rb +116 -0
  81. data/lib/voltaria/investors/client.rb +312 -0
  82. data/lib/voltaria/investors/types/investor_get_client_request.rb +11 -0
  83. data/lib/voltaria/investors/types/investor_get_installment_request.rb +11 -0
  84. data/lib/voltaria/investors/types/investor_get_loan_request.rb +11 -0
  85. data/lib/voltaria/investors/types/investor_list_clients_request.rb +14 -0
  86. data/lib/voltaria/investors/types/investor_list_installments_request.rb +16 -0
  87. data/lib/voltaria/investors/types/investor_list_loans_request.rb +15 -0
  88. data/lib/voltaria/investors/types/investor_list_repayments_request.rb +17 -0
  89. data/lib/voltaria/loans/client.rb +271 -0
  90. data/lib/voltaria/loans/types/bulk_loan_create_payload.rb +11 -0
  91. data/lib/voltaria/loans/types/delete_loan_request.rb +11 -0
  92. data/lib/voltaria/loans/types/get_bulk_loan_status_request.rb +11 -0
  93. data/lib/voltaria/loans/types/get_loan_by_id_request.rb +11 -0
  94. data/lib/voltaria/loans/types/list_loans_request.rb +15 -0
  95. data/lib/voltaria/loans/types/loan_create_payload.rb +17 -0
  96. data/lib/voltaria/loans/types/loan_create_payload_amount.rb +15 -0
  97. data/lib/voltaria/loans/types/loan_default_payload.rb +13 -0
  98. data/lib/voltaria/loans/types/loan_default_payload_sold_amount.rb +15 -0
  99. data/lib/voltaria/partners/client.rb +129 -0
  100. data/lib/voltaria/partners/types/list_partner_waterfalls_request.rb +14 -0
  101. data/lib/voltaria/partners/types/partner_data_create_payload.rb +11 -0
  102. data/lib/voltaria/prefix_routing.rb +80 -0
  103. data/lib/voltaria/repayments/client.rb +183 -0
  104. data/lib/voltaria/repayments/types/bulk_repayment_create_payload.rb +11 -0
  105. data/lib/voltaria/repayments/types/get_bulk_repayment_status_request.rb +11 -0
  106. data/lib/voltaria/repayments/types/list_repayment_logs_request.rb +17 -0
  107. data/lib/voltaria/repayments/types/repayment_create_payload.rb +17 -0
  108. data/lib/voltaria/repayments/types/repayment_create_payload_amount.rb +15 -0
  109. data/lib/voltaria/sandbox/client.rb +124 -0
  110. data/lib/voltaria/sandbox/types/client_update_sandbox.rb +13 -0
  111. data/lib/voltaria/sandbox/types/client_update_sandbox_limit.rb +15 -0
  112. data/lib/voltaria/sandbox/types/loan_update_sandbox.rb +12 -0
  113. data/lib/voltaria/sandbox/types/webhook_test_sandbox.rb +12 -0
  114. data/lib/voltaria/types/account_address.rb +14 -0
  115. data/lib/voltaria/types/account_holder_type_enum.rb +12 -0
  116. data/lib/voltaria/types/account_status_enum.rb +13 -0
  117. data/lib/voltaria/types/available_document_categories_response.rb +9 -0
  118. data/lib/voltaria/types/available_funding.rb +15 -0
  119. data/lib/voltaria/types/bulk_loan_item_payload.rb +15 -0
  120. data/lib/voltaria/types/bulk_loan_item_payload_amount.rb +13 -0
  121. data/lib/voltaria/types/bulk_loan_item_result.rb +15 -0
  122. data/lib/voltaria/types/bulk_loan_result.rb +13 -0
  123. data/lib/voltaria/types/bulk_loan_task_response.rb +11 -0
  124. data/lib/voltaria/types/bulk_loan_task_status.rb +14 -0
  125. data/lib/voltaria/types/bulk_repayment_item_payload.rb +15 -0
  126. data/lib/voltaria/types/bulk_repayment_item_payload_amount.rb +13 -0
  127. data/lib/voltaria/types/bulk_repayment_item_result.rb +15 -0
  128. data/lib/voltaria/types/bulk_repayment_result.rb +13 -0
  129. data/lib/voltaria/types/bulk_repayment_task_response.rb +11 -0
  130. data/lib/voltaria/types/bulk_repayment_task_status.rb +14 -0
  131. data/lib/voltaria/types/checklist_summary_partner_response.rb +14 -0
  132. data/lib/voltaria/types/checklist_type_enum.rb +12 -0
  133. data/lib/voltaria/types/client_account_response.rb +21 -0
  134. data/lib/voltaria/types/client_base_info.rb +13 -0
  135. data/lib/voltaria/types/client_data_response.rb +12 -0
  136. data/lib/voltaria/types/client_investor_response.rb +17 -0
  137. data/lib/voltaria/types/client_limit_response.rb +16 -0
  138. data/lib/voltaria/types/client_response.rb +19 -0
  139. data/lib/voltaria/types/client_status_enum.rb +18 -0
  140. data/lib/voltaria/types/client_type_enum.rb +12 -0
  141. data/lib/voltaria/types/client_user_response.rb +24 -0
  142. data/lib/voltaria/types/client_user_status_enum.rb +13 -0
  143. data/lib/voltaria/types/currency_enum.rb +16 -0
  144. data/lib/voltaria/types/currency_field_spec.rb +10 -0
  145. data/lib/voltaria/types/document_response.rb +20 -0
  146. data/lib/voltaria/types/drawdown_checklist_response.rb +16 -0
  147. data/lib/voltaria/types/drawdown_response.rb +15 -0
  148. data/lib/voltaria/types/drawdown_status_enum.rb +13 -0
  149. data/lib/voltaria/types/http_validation_error.rb +9 -0
  150. data/lib/voltaria/types/installment_response.rb +24 -0
  151. data/lib/voltaria/types/installment_response_with_client_info.rb +25 -0
  152. data/lib/voltaria/types/installment_status_enum.rb +19 -0
  153. data/lib/voltaria/types/jurisdiction_enum.rb +13 -0
  154. data/lib/voltaria/types/kyc_status_enum.rb +14 -0
  155. data/lib/voltaria/types/limit_request_response.rb +16 -0
  156. data/lib/voltaria/types/limit_request_status_enum.rb +13 -0
  157. data/lib/voltaria/types/loan_installment_create_payload.rb +10 -0
  158. data/lib/voltaria/types/loan_installment_create_payload_amount.rb +13 -0
  159. data/lib/voltaria/types/loan_installment_response.rb +15 -0
  160. data/lib/voltaria/types/loan_investor_response.rb +27 -0
  161. data/lib/voltaria/types/loan_payment_status_enum.rb +12 -0
  162. data/lib/voltaria/types/loan_response_with_client_info.rb +26 -0
  163. data/lib/voltaria/types/loan_response_with_installments.rb +26 -0
  164. data/lib/voltaria/types/loan_status_enum.rb +21 -0
  165. data/lib/voltaria/types/paginated_response_checklist_summary_partner_response.rb +16 -0
  166. data/lib/voltaria/types/paginated_response_client_account_response.rb +16 -0
  167. data/lib/voltaria/types/paginated_response_client_investor_response.rb +16 -0
  168. data/lib/voltaria/types/paginated_response_client_response.rb +16 -0
  169. data/lib/voltaria/types/paginated_response_document_response.rb +16 -0
  170. data/lib/voltaria/types/paginated_response_drawdown_checklist_response.rb +16 -0
  171. data/lib/voltaria/types/paginated_response_drawdown_response.rb +16 -0
  172. data/lib/voltaria/types/paginated_response_installment_response_with_client_info.rb +16 -0
  173. data/lib/voltaria/types/paginated_response_limit_request_response.rb +16 -0
  174. data/lib/voltaria/types/paginated_response_loan_investor_response.rb +16 -0
  175. data/lib/voltaria/types/paginated_response_loan_response_with_client_info.rb +16 -0
  176. data/lib/voltaria/types/paginated_response_payment_promise_response.rb +16 -0
  177. data/lib/voltaria/types/paginated_response_repayment_response_with_client_info.rb +16 -0
  178. data/lib/voltaria/types/paginated_response_waiver_response.rb +16 -0
  179. data/lib/voltaria/types/paginated_response_waterfall_response.rb +16 -0
  180. data/lib/voltaria/types/paginated_response_webhook_log_response.rb +16 -0
  181. data/lib/voltaria/types/paginated_response_webhook_subscription_response.rb +16 -0
  182. data/lib/voltaria/types/partner_data_response.rb +11 -0
  183. data/lib/voltaria/types/payment_promise_response.rb +19 -0
  184. data/lib/voltaria/types/payment_promise_status_enum.rb +14 -0
  185. data/lib/voltaria/types/repayment_response.rb +21 -0
  186. data/lib/voltaria/types/repayment_response_with_client_info.rb +22 -0
  187. data/lib/voltaria/types/role_response.rb +11 -0
  188. data/lib/voltaria/types/validation_error.rb +13 -0
  189. data/lib/voltaria/types/validation_error_loc_item.rb +12 -0
  190. data/lib/voltaria/types/waiver_response.rb +18 -0
  191. data/lib/voltaria/types/waiver_status_enum.rb +14 -0
  192. data/lib/voltaria/types/waterfall_response.rb +19 -0
  193. data/lib/voltaria/types/waterfall_status_enum.rb +12 -0
  194. data/lib/voltaria/types/webhook_event_type_enum.rb +15 -0
  195. data/lib/voltaria/types/webhook_log_response.rb +19 -0
  196. data/lib/voltaria/types/webhook_log_response_request_body.rb +13 -0
  197. data/lib/voltaria/types/webhook_status_enum.rb +13 -0
  198. data/lib/voltaria/types/webhook_subscription_response.rb +17 -0
  199. data/lib/voltaria/version.rb +5 -0
  200. data/lib/voltaria/webhooks/client.rb +241 -0
  201. data/lib/voltaria/webhooks/types/delete_webhook_subscription_request.rb +11 -0
  202. data/lib/voltaria/webhooks/types/get_webhook_subscription_request.rb +11 -0
  203. data/lib/voltaria/webhooks/types/list_webhook_logs_request.rb +13 -0
  204. data/lib/voltaria/webhooks/types/list_webhook_subscriptions_request.rb +13 -0
  205. data/lib/voltaria/webhooks/types/webhook_create_payload.rb +16 -0
  206. data/lib/voltaria/webhooks/types/webhook_update_payload.rb +17 -0
  207. data/lib/voltaria.rb +205 -0
  208. data/reference.md +5135 -0
  209. metadata +250 -0
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Voltaria
4
+ module Internal
5
+ module Multipart
6
+ # @api private
7
+ class FormData
8
+ # @return [Array<FormDataPart>] The parts in this multipart form data.
9
+ attr_reader :parts
10
+
11
+ # @return [Encoder] The encoder for this multipart form data.
12
+ private attr_reader :encoder
13
+
14
+ def initialize
15
+ @encoder = Encoder.new
16
+ @parts = []
17
+ end
18
+
19
+ # Adds a new part to the multipart form data.
20
+ #
21
+ # @param name [String] The name of the form field
22
+ # @param value [String, Integer, Float, Boolean, #read] The value of the field
23
+ # @param content_type [String, nil] Optional content type
24
+ # @return [self] Returns self for chaining
25
+ def add(name:, value:, content_type: nil)
26
+ headers = content_type ? { "Content-Type" => content_type } : nil
27
+ add_part(FormDataPart.new(name:, value:, headers:))
28
+ end
29
+
30
+ # Adds a file to the multipart form data.
31
+ #
32
+ # @param name [String] The name of the form field
33
+ # @param file [#read] The file or readable object
34
+ # @param filename [String, nil] Optional filename (defaults to basename of path for File objects)
35
+ # @param content_type [String, nil] Optional content type (e.g. "image/png")
36
+ # @return [self] Returns self for chaining
37
+ def add_file(name:, file:, filename: nil, content_type: nil)
38
+ headers = content_type ? { "Content-Type" => content_type } : nil
39
+ filename ||= filename_for(file)
40
+ add_part(FormDataPart.new(name:, value: file, filename:, headers:))
41
+ end
42
+
43
+ # Adds a pre-created part to the multipart form data.
44
+ #
45
+ # @param part [FormDataPart] The part to add
46
+ # @return [self] Returns self for chaining
47
+ def add_part(part)
48
+ @parts << part
49
+ self
50
+ end
51
+
52
+ # Gets the content type string including the boundary.
53
+ #
54
+ # @return [String] The content type with boundary.
55
+ def content_type
56
+ @encoder.content_type
57
+ end
58
+
59
+ # Encode the multipart form data into a multipart/form-data payload.
60
+ #
61
+ # @return [String] The encoded body.
62
+ def encode
63
+ @encoder.encode(self)
64
+ end
65
+
66
+ private
67
+
68
+ def filename_for(file)
69
+ if file.is_a?(::File) || file.respond_to?(:path)
70
+ ::File.basename(file.path)
71
+ elsif file.respond_to?(:name)
72
+ file.name
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
5
+ module Voltaria
6
+ module Internal
7
+ module Multipart
8
+ # @api private
9
+ class FormDataPart
10
+ attr_reader :name, :contents, :filename, :headers
11
+
12
+ # @param name [String] The name of the form field
13
+ # @param value [String, Integer, Float, Boolean, File, #read] The value of the field
14
+ # @param filename [String, nil] Optional filename for file uploads
15
+ # @param headers [Hash<String, String>, nil] Optional additional headers
16
+ def initialize(name:, value:, filename: nil, headers: nil)
17
+ @name = name
18
+ @contents = convert_to_content(value)
19
+ @filename = filename
20
+ @headers = headers
21
+ end
22
+
23
+ # Converts the part to a hash suitable for serialization.
24
+ #
25
+ # @return [Hash] A hash representation of the part
26
+ def to_hash
27
+ result = {
28
+ name: @name,
29
+ contents: @contents
30
+ }
31
+ result[:filename] = @filename if @filename
32
+ result[:headers] = @headers if @headers
33
+ result
34
+ end
35
+
36
+ private
37
+
38
+ # Converts various types of values to a content representation
39
+ # @param value [String, Integer, Float, Boolean, #read] The value to convert
40
+ # @return [String] The string representation of the value
41
+ def convert_to_content(value)
42
+ if value.respond_to?(:read)
43
+ value.read
44
+ else
45
+ value.to_s
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Voltaria
4
+ module Internal
5
+ module Multipart
6
+ # @api private
7
+ class Request < Voltaria::Internal::Http::BaseRequest
8
+ attr_reader :body
9
+
10
+ # @param base_url [String] The base URL for the request
11
+ # @param path [String] The path for the request
12
+ # @param method [Symbol] The HTTP method for the request (:get, :post, etc.)
13
+ # @param headers [Hash] Additional headers for the request (optional)
14
+ # @param query [Hash] Query parameters for the request (optional)
15
+ # @param body [MultipartFormData, nil] The multipart form data for the request (optional)
16
+ # @param request_options [Voltaria::RequestOptions, Hash{Symbol=>Object}, nil]
17
+ def initialize(base_url:, path:, method:, headers: {}, query: {}, body: nil, request_options: {})
18
+ super(base_url:, path:, method:, headers:, query:, request_options:)
19
+
20
+ @body = body
21
+ end
22
+
23
+ # @return [Hash] The encoded HTTP request headers.
24
+ # @param protected_keys [Array<String>] Header keys set by the SDK client (e.g. auth, metadata)
25
+ # that must not be overridden by additional_headers from request_options.
26
+ def encode_headers(protected_keys: [])
27
+ sdk_headers = {
28
+ "Content-Type" => @body.content_type
29
+ }.merge(@headers)
30
+ merge_additional_headers(sdk_headers, protected_keys:)
31
+ end
32
+
33
+ # @return [String, nil] The encoded HTTP request body.
34
+ def encode_body
35
+ @body&.encode
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Voltaria
4
+ module Internal
5
+ module Types
6
+ # An array of a specific type
7
+ class Array
8
+ include Voltaria::Internal::Types::Type
9
+
10
+ attr_reader :type
11
+
12
+ class << self
13
+ # Instantiates a new `Array` of a given type
14
+ #
15
+ # @param type [Object] The member type of this array
16
+ #
17
+ # @return [Voltaria::Internal::Types::Array]
18
+ def [](type)
19
+ new(type)
20
+ end
21
+ end
22
+
23
+ # @api private
24
+ def initialize(type)
25
+ @type = type
26
+ end
27
+
28
+ # Coerces a value into this array
29
+ #
30
+ # @param value [Object]
31
+ # @option strict [Boolean]
32
+ # @return [::Array]
33
+ def coerce(value, strict: strict?)
34
+ unless value.is_a?(::Array)
35
+ raise Errors::TypeError, "cannot coerce `#{value.class}` to Array<#{type}>" if strict
36
+
37
+ return value
38
+ end
39
+
40
+ value.map do |element|
41
+ Utils.coerce(type, element, strict: strict)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Voltaria
4
+ module Internal
5
+ module Types
6
+ module Boolean
7
+ extend Voltaria::Internal::Types::Union
8
+
9
+ member TrueClass
10
+ member FalseClass
11
+
12
+ # Overrides the base coercion method for enums to allow integer and string values to become booleans
13
+ #
14
+ # @param value [Object]
15
+ # @option strict [Boolean]
16
+ # @return [Object]
17
+ def self.coerce(value, strict: strict?)
18
+ case value
19
+ when TrueClass, FalseClass
20
+ return value
21
+ when Integer
22
+ return value == 1
23
+ when String
24
+ return %w[1 true].include?(value)
25
+ end
26
+
27
+ raise Errors::TypeError, "cannot coerce `#{value.class}` to Boolean" if strict
28
+
29
+ value
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Voltaria
4
+ module Internal
5
+ module Types
6
+ # Module for defining enums
7
+ module Enum
8
+ include Type
9
+
10
+ # @api private
11
+ #
12
+ # @return [Array<Object>]
13
+ def values
14
+ @values ||= constants.map { |c| const_get(c) }
15
+ end
16
+
17
+ # @api private
18
+ def finalize!
19
+ values
20
+ end
21
+
22
+ # @api private
23
+ def strict?
24
+ @strict ||= false
25
+ end
26
+
27
+ # @api private
28
+ def strict!
29
+ @strict = true
30
+ end
31
+
32
+ def coerce(value, strict: strict?)
33
+ coerced_value = Utils.coerce(Symbol, value)
34
+
35
+ return coerced_value if values.include?(coerced_value)
36
+
37
+ raise Errors::TypeError, "`#{value}` not in enum #{self}" if strict
38
+
39
+ value
40
+ end
41
+
42
+ # Parse JSON string and coerce to the enum value
43
+ #
44
+ # @param str [String] JSON string to parse
45
+ # @return [String] The enum value
46
+ def load(str)
47
+ coerce(::JSON.parse(str))
48
+ end
49
+
50
+ def inspect
51
+ "#{name}[#{values.join(", ")}]"
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Voltaria
4
+ module Internal
5
+ module Types
6
+ class Hash
7
+ include Type
8
+
9
+ attr_reader :key_type, :value_type
10
+
11
+ class << self
12
+ def [](key_type, value_type)
13
+ new(key_type, value_type)
14
+ end
15
+ end
16
+
17
+ def initialize(key_type, value_type)
18
+ @key_type = key_type
19
+ @value_type = value_type
20
+ end
21
+
22
+ def coerce(value, strict: strict?)
23
+ unless value.is_a?(::Hash)
24
+ raise Errors::TypeError, "not hash" if strict
25
+
26
+ return value
27
+ end
28
+
29
+ value.to_h do |k, v|
30
+ [Utils.coerce(key_type, k, strict: strict), Utils.coerce(value_type, v, strict: strict)]
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Voltaria
4
+ module Internal
5
+ module Types
6
+ class Model
7
+ # Definition of a field on a model
8
+ class Field
9
+ SENSITIVE_FIELD_NAMES = %i[
10
+ password secret token api_key apikey access_token refresh_token
11
+ client_secret client_id credential bearer authorization
12
+ ].freeze
13
+
14
+ attr_reader :name, :type, :optional, :nullable, :api_name, :value, :default
15
+
16
+ def initialize(name:, type:, optional: false, nullable: false, api_name: nil, value: nil, default: nil)
17
+ @name = name.to_sym
18
+ @type = type
19
+ @optional = optional
20
+ @nullable = nullable
21
+ @api_name = api_name || name.to_s
22
+ @value = value
23
+ @default = default
24
+ end
25
+
26
+ def literal?
27
+ !value.nil?
28
+ end
29
+
30
+ def sensitive?
31
+ SENSITIVE_FIELD_NAMES.include?(@name) ||
32
+ SENSITIVE_FIELD_NAMES.any? { |sensitive| @name.to_s.include?(sensitive.to_s) }
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Voltaria
4
+ module Internal
5
+ module Types
6
+ # @abstract
7
+ #
8
+ # An abstract model that all data objects will inherit from
9
+ class Model
10
+ include Type
11
+
12
+ class << self
13
+ # The defined fields for this model
14
+ #
15
+ # @api private
16
+ #
17
+ # @return [Hash<Symbol, Field>]
18
+ def fields
19
+ @fields ||= if self < Voltaria::Internal::Types::Model
20
+ superclass.fields.dup
21
+ else
22
+ {}
23
+ end
24
+ end
25
+
26
+ # Any extra fields that have been created from instantiation
27
+ #
28
+ # @api private
29
+ #
30
+ # @return [Hash<Symbol, Field>]
31
+ def extra_fields
32
+ @extra_fields ||= {}
33
+ end
34
+
35
+ # Define a new field on this model
36
+ #
37
+ # @param name [Symbol] The name of the field
38
+ # @param type [Class] Type of the field
39
+ # @option optional [Boolean] If it is an optional field
40
+ # @option nullable [Boolean] If it is a nullable field
41
+ # @option api_name [Symbol, String] Name in the API of this field. When serializing/deserializing, will use
42
+ # this field name
43
+ # @return [void]
44
+ def field(name, type, optional: false, nullable: false, api_name: nil, default: nil)
45
+ add_field_definition(name: name, type: type, optional: optional, nullable: nullable, api_name: api_name,
46
+ default: default)
47
+
48
+ define_accessor(name)
49
+ define_setter(name)
50
+ end
51
+
52
+ # Define a new literal for this model
53
+ #
54
+ # @param name [Symbol]
55
+ # @param value [Object]
56
+ # @option api_name [Symbol, String]
57
+ # @return [void]
58
+ def literal(name, value, api_name: nil)
59
+ add_field_definition(name: name, type: value.class, optional: false, nullable: false, api_name: api_name,
60
+ value: value)
61
+
62
+ define_accessor(name)
63
+ end
64
+
65
+ # Adds a new field definition into the class's fields registry
66
+ #
67
+ # @api private
68
+ #
69
+ # @param name [Symbol]
70
+ # @param type [Class]
71
+ # @option optional [Boolean]
72
+ # @return [void]
73
+ private def add_field_definition(name:, type:, optional:, nullable:, api_name:, default: nil, value: nil)
74
+ fields[name.to_sym] =
75
+ Field.new(name: name, type: type, optional: optional, nullable: nullable, api_name: api_name,
76
+ value: value, default: default)
77
+ end
78
+
79
+ # Adds a new field definition into the class's extra fields registry
80
+ #
81
+ # @api private
82
+ #
83
+ # @param name [Symbol]
84
+ # @param type [Class]
85
+ # @option required [Boolean]
86
+ # @option optional [Boolean]
87
+ # @return [void]
88
+ def add_extra_field_definition(name:, type:)
89
+ return if extra_fields.key?(name.to_sym)
90
+
91
+ extra_fields[name.to_sym] = Field.new(name: name, type: type, optional: true, nullable: false)
92
+
93
+ define_accessor(name)
94
+ define_setter(name)
95
+ end
96
+
97
+ # @api private
98
+ private def define_accessor(name)
99
+ method_name = name.to_sym
100
+
101
+ define_method(method_name) do
102
+ @data[name]
103
+ end
104
+ end
105
+
106
+ # @api private
107
+ private def define_setter(name)
108
+ method_name = :"#{name}="
109
+
110
+ define_method(method_name) do |val|
111
+ @data[name] = val
112
+ end
113
+ end
114
+
115
+ def coerce(value, strict: (respond_to?(:strict?) ? strict? : false)) # rubocop:disable Lint/UnusedMethodArgument
116
+ return value if value.is_a?(self)
117
+
118
+ return value unless value.is_a?(::Hash)
119
+
120
+ new(value)
121
+ end
122
+
123
+ def load(str)
124
+ coerce(::JSON.parse(str, symbolize_names: true))
125
+ end
126
+
127
+ def ===(instance)
128
+ instance.class.ancestors.include?(self)
129
+ end
130
+ end
131
+
132
+ # Creates a new instance of this model
133
+ # TODO: Should all this logic be in `#coerce` instead?
134
+ #
135
+ # @param values [Hash]
136
+ # @option strict [Boolean]
137
+ # @return [self]
138
+ def initialize(values = {})
139
+ @data = {}
140
+
141
+ values = Utils.symbolize_keys(values.dup)
142
+
143
+ self.class.fields.each do |field_name, field|
144
+ value = values.delete(field.api_name.to_sym) || values.delete(field.api_name) || values.delete(field_name)
145
+
146
+ field_value = value || (if field.literal?
147
+ field.value
148
+ elsif field.default
149
+ field.default
150
+ end)
151
+
152
+ @data[field_name] = Utils.coerce(field.type, field_value)
153
+ end
154
+
155
+ # Any remaining values in the input become extra fields
156
+ values.each do |name, value|
157
+ self.class.add_extra_field_definition(name: name, type: value.class)
158
+
159
+ @data[name.to_sym] = value
160
+ end
161
+ end
162
+
163
+ def to_h
164
+ result = self.class.fields.merge(self.class.extra_fields).each_with_object({}) do |(name, field), acc|
165
+ # If there is a value present in the data, use that value
166
+ # If there is a `nil` value present in the data, and it is optional but NOT nullable, exclude key altogether
167
+ # If there is a `nil` value present in the data, and it is optional and nullable, use the nil value
168
+
169
+ value = @data[name]
170
+
171
+ next if value.nil? && field.optional && !field.nullable
172
+
173
+ if value.is_a?(::Array)
174
+ value = value.map { |item| item.respond_to?(:to_h) ? item.to_h : item }
175
+ elsif value.respond_to?(:to_h)
176
+ value = value.to_h
177
+ end
178
+
179
+ acc[field.api_name] = value
180
+ end
181
+
182
+ # Inject union discriminant if this instance was coerced from a discriminated union
183
+ # and the discriminant key is not already present in the result
184
+ discriminant_key = instance_variable_get(:@_fern_union_discriminant_key)
185
+ discriminant_value = instance_variable_get(:@_fern_union_discriminant_value)
186
+ result[discriminant_key] = discriminant_value if discriminant_key && discriminant_value && !result.key?(discriminant_key)
187
+
188
+ result
189
+ end
190
+
191
+ def ==(other)
192
+ self.class == other.class && to_h == other.to_h
193
+ end
194
+
195
+ # @return [String]
196
+ def inspect
197
+ attrs = @data.map do |name, value|
198
+ field = self.class.fields[name] || self.class.extra_fields[name]
199
+ display_value = field&.sensitive? ? "[REDACTED]" : value.inspect
200
+ "#{name}=#{display_value}"
201
+ end
202
+
203
+ "#<#{self.class.name}:0x#{object_id&.to_s(16)} #{attrs.join(" ")}>"
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Voltaria
4
+ module Internal
5
+ module Types
6
+ # @abstract
7
+ module Type
8
+ include Voltaria::Internal::JSON::Serializable
9
+
10
+ # Coerces a value to this type
11
+ #
12
+ # @param value [unknown]
13
+ # @option strict [Boolean] If we should strictly coerce this value
14
+ def coerce(value, strict: strict?)
15
+ raise NotImplementedError
16
+ end
17
+
18
+ # Returns if strictness is on for this type, defaults to `false`
19
+ #
20
+ # @return [Boolean]
21
+ def strict?
22
+ @strict ||= false
23
+ end
24
+
25
+ # Enable strictness by default for this type
26
+ #
27
+ # @return [void]
28
+ def strict!
29
+ @strict = true
30
+ self
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end