diff --git a/CHANGELOG.md b/CHANGELOG.md index 4571499..a3c49c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ Given a version number MAJOR.MINOR.PATCH, increment: ## [Unreleased] +### Added +- IndividualAccountRequest resource +- IndividualAccountRequest::Address sub-resource +- IndividualAccountAttachment resource ### Fixed - camelCase files diff --git a/Gemfile.lock b/Gemfile.lock index 034f3da..90b3680 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,4 +40,4 @@ DEPENDENCIES starkinfra! BUNDLED WITH - 2.1.4 + 2.5.3 diff --git a/README.md b/README.md index f1472be..17f70d0 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,8 @@ This SDK version is compatible with the Stark Infra API v2. - [Identity](#identity) - [IndividualIdentity](#create-individualidentities): Create individual identities - [IndividualDocument](#create-individualdocuments): Create individual documents + - [IndividualAccountRequest](#create-individualaccountrequests): Open individual account requests + - [IndividualAccountAttachment](#create-individualaccountattachments): Attach documents to account requests - [Webhook](#webhook): - [Webhook](#create-a-webhook-subscription): Configure your webhook endpoints and subscriptions - [WebhookEvents](#process-webhook-events): Manage Webhook events @@ -2739,6 +2741,193 @@ log = StarkInfra::IndividualDocument::Log.get('5155165527080960') puts log ``` +### Create IndividualAccountRequests + +You can open an account request for an individual by passing their identifying data, structured address and monthly income. + +```ruby +require('starkinfra') + +requests = StarkInfra::IndividualAccountRequest.create([ + StarkInfra::IndividualAccountRequest.new( + name: 'Tony Stark', + tax_id: '012.345.678-90', + income: 1000000, + address: StarkInfra::IndividualAccountRequest::Address.new( + street: 'Rua do Estilo Barroco', + number: '648', + neighborhood: 'Santo Amaro', + city: 'SP', + state: 'SP', + zip_code: '05724005' + ), + tags: ['employees', 'monthly'] + ) +]) + +requests.each do |request| + puts request +end +``` + +### Query IndividualAccountRequests + +You can query multiple individual account requests according to filters. + +```ruby +require('starkinfra') + +requests = StarkInfra::IndividualAccountRequest.query( + after: '2022-01-01', + before: '2022-03-01' +) + +requests.each do |request| + puts request +end +``` + +### Get an IndividualAccountRequest + +After its creation, information on an individual account request may be retrieved by its id. + +```ruby +require('starkinfra') + +request = StarkInfra::IndividualAccountRequest.get('5155165527080960') + +puts request +``` + +### Update an IndividualAccountRequest + +You can update an individual account request by passing its id and the fields to replace. + +```ruby +require('starkinfra') + +request = StarkInfra::IndividualAccountRequest.update( + '5155165527080960', + name: 'Tony Stark Updated' +) + +puts request +``` + +### Query IndividualAccountRequest logs + +You can query individual account request logs to better understand their life cycles. + +```ruby +require('starkinfra') + +logs = StarkInfra::IndividualAccountRequest::Log.query(limit: 50) + +logs.each do |log| + puts log +end +``` + +### Get an IndividualAccountRequest log + +You can also get a specific log by its id. + +```ruby +require('starkinfra') + +log = StarkInfra::IndividualAccountRequest::Log.get('5155165527080960') + +puts log +``` + +### Create IndividualAccountAttachments + +You can attach supporting documents to an individual account request by passing the raw image bytes and a MIME content type. + +```ruby +require('starkinfra') + +attachments = StarkInfra::IndividualAccountAttachment.create([ + StarkInfra::IndividualAccountAttachment.new( + type: 'identity-front', + content: File.binread('identity-front.png'), + content_type: 'image/png', + account_request_id: '5656565656565656' + ) +]) + +attachments.each do |attachment| + puts attachment +end +``` + +### Query IndividualAccountAttachments + +You can query multiple individual account attachments according to filters. + +```ruby +require('starkinfra') + +attachments = StarkInfra::IndividualAccountAttachment.query( + after: '2022-01-01', + before: '2022-03-01' +) + +attachments.each do |attachment| + puts attachment +end +``` + +### Get an IndividualAccountAttachment + +After its creation, information on an individual account attachment may be retrieved by its id. + +```ruby +require('starkinfra') + +attachment = StarkInfra::IndividualAccountAttachment.get('5155165527080960') + +puts attachment +``` + +### Cancel an IndividualAccountAttachment + +You can delete an individual account attachment by passing its id. + +```ruby +require('starkinfra') + +attachment = StarkInfra::IndividualAccountAttachment.cancel('5155165527080960') + +puts attachment +``` + +### Query IndividualAccountAttachment logs + +You can query individual account attachment logs to better understand their life cycles. + +```ruby +require('starkinfra') + +logs = StarkInfra::IndividualAccountAttachment::Log.query(limit: 50) + +logs.each do |log| + puts log +end +``` + +### Get an IndividualAccountAttachment log + +You can also get a specific log by its id. + +```ruby +require('starkinfra') + +log = StarkInfra::IndividualAccountAttachment::Log.get('5155165527080960') + +puts log +``` + ### Webhook ### Create a webhook subscription diff --git a/lib/individual_account_attachment/individual_account_attachment.rb b/lib/individual_account_attachment/individual_account_attachment.rb new file mode 100644 index 0000000..cec7ffb --- /dev/null +++ b/lib/individual_account_attachment/individual_account_attachment.rb @@ -0,0 +1,178 @@ +# frozen_string_literal: true + +require('starkcore') +require_relative('../utils/rest') + +module StarkInfra + # # IndividualAccountAttachment object + # + # Supporting document (identity document, driver's license) attached to an IndividualAccountRequest + # for the account-approval flow. The caller uploads the raw image bytes and a MIME content type; + # the SDK encodes them as a data: URL before sending. + # + # When you initialize an IndividualAccountAttachment, the entity will not be automatically + # created in the Stark Infra API. The 'create' function sends the objects + # to the Stark Infra API and returns the list of created objects. + # + # ## Parameters (required): + # - type [string]: type of the IndividualAccountAttachment. Options: 'drivers-license-front', 'drivers-license-back', 'identity-front', 'identity-back' + # - content [string]: raw image bytes of the picture. After encoding, becomes a data:;base64, URL. + # - account_request_id [string]: id of the parent IndividualAccountRequest. ex: '5656565656565656' + # + # ## Parameters (optional): + # - content_type [string, default nil]: content MIME type. Consumed as input only to build the data: URL; never sent as its own wire field. ex: 'image/png' or 'image/jpeg' + # - tags [list of strings, default nil]: list of strings for reference when searching for IndividualAccountAttachments. ex: ['employees', 'monthly'] + # + # ## Attributes (return-only): + # - id [string]: unique id returned when the IndividualAccountAttachment is created. ex: '5656565656565656' + # - status [string]: current status of the IndividualAccountAttachment. Options: 'created', 'success', 'failed', 'deleted' + # - created [DateTime]: creation datetime for the IndividualAccountAttachment. ex: DateTime.new(2020, 3, 10, 10, 30, 0, 0) + class IndividualAccountAttachment < StarkCore::Utils::Resource + attr_reader :type, :content, :content_type, :account_request_id, :tags, :id, :status, :created + def initialize(type:, content:, account_request_id:, content_type: nil, tags: nil, id: nil, status: nil, created: nil) + super(id) + @type = type + @account_request_id = account_request_id + @tags = tags + @status = status + @created = StarkCore::Utils::Checks.check_datetime(created) + @content = content + @content_type = content_type + + if @content_type + @content = "data:#{content_type};base64,#{Base64.encode64(content)}" + @content_type = nil + end + end + + # # Create IndividualAccountAttachments + # + # Send a list of IndividualAccountAttachment objects for creation at the Stark Infra API + # + # ## Parameters (required): + # - attachments [list of IndividualAccountAttachment objects]: list of IndividualAccountAttachment objects to be created in the API. + # + # ## Parameters (optional): + # - user [Organization/Project object, default nil]: Organization or Project object. Not necessary if StarkInfra.user was set before function call + # + # ## Return: + # - list of IndividualAccountAttachment objects with updated attributes + def self.create(attachments, user: nil) + StarkInfra::Utils::Rest.post(entities: attachments, user: user, **resource) + end + + # # Retrieve a specific IndividualAccountAttachment + # + # Receive a single IndividualAccountAttachment object previously created in the Stark Infra API by its id + # + # ## Parameters (required): + # - id [string]: object unique id. ex: '5656565656565656' + # + # ## Parameters (optional): + # - user [Organization/Project object, default nil]: Organization or Project object. Not necessary if StarkInfra.user was set before function call + # + # ## Return: + # - IndividualAccountAttachment object with updated attributes + def self.get(id, user: nil) + StarkInfra::Utils::Rest.get_id(id: id, user: user, **resource) + end + + # # Retrieve IndividualAccountAttachments + # + # Receive a generator of IndividualAccountAttachment objects previously created in the Stark Infra API + # + # ## Parameters (optional): + # - limit [integer, default nil]: maximum number of objects to be retrieved. Unlimited if nil. ex: 35 + # - after [Date or string, default nil]: date filter for objects created only after specified date. ex: Date.new(2020, 3, 10) + # - before [Date or string, default nil]: date filter for objects created only before specified date. ex: Date.new(2020, 3, 10) + # - status [string, default nil]: filter for status of retrieved objects. ex: 'created' + # - tags [list of strings, default nil]: tags to filter retrieved objects. ex: ['tony', 'stark'] + # - ids [list of strings, default nil]: list of ids to filter retrieved objects. ex: ['5656565656565656', '4545454545454545'] + # - user [Organization/Project object, default nil]: Organization or Project object. Not necessary if StarkInfra.user was set before function call + # + # ## Return: + # - generator of IndividualAccountAttachment objects with updated attributes + def self.query(limit: nil, after: nil, before: nil, status: nil, tags: nil, ids: nil, user: nil) + after = StarkCore::Utils::Checks.check_date(after) + before = StarkCore::Utils::Checks.check_date(before) + StarkInfra::Utils::Rest.get_stream( + limit: limit, + after: after, + before: before, + status: status, + tags: tags, + ids: ids, + user: user, + **resource + ) + end + + # # Retrieve paged IndividualAccountAttachments + # + # Receive a list of up to 100 IndividualAccountAttachment objects previously created in the Stark Infra API and the cursor to the next page. + # Use this function instead of query if you want to manually page your requests. + # + # ## Parameters (optional): + # - cursor [string, default nil]: cursor returned on the previous page function call + # - limit [integer, default 100]: maximum number of objects to be retrieved. Max = 100. ex 35 + # - after [Date or string, default nil]: date filter for objects created only after specified date. ex: Date.new(2020, 3, 10) + # - before [Date or string, default nil]: date filter for objects created only before specified date. ex: Date.new(2020, 3, 10) + # - status [string, default nil]: filter for status of retrieved objects. ex: 'created' + # - tags [list of strings, default nil]: tags to filter retrieved objects. ex: ['tony', 'stark'] + # - ids [list of strings, default nil]: list of ids to filter retrieved objects. ex: ['5656565656565656', '4545454545454545'] + # - user [Organization/Project object, default nil]: Organization or Project object. Not necessary if StarkInfra.user was set before function call + # + # ## Return: + # - list of IndividualAccountAttachment objects with updated attributes + # - cursor to retrieve the next page of IndividualAccountAttachment objects + def self.page(cursor: nil, limit: nil, after: nil, before: nil, status: nil, tags: nil, ids: nil, user: nil) + after = StarkCore::Utils::Checks.check_date(after) + before = StarkCore::Utils::Checks.check_date(before) + StarkInfra::Utils::Rest.get_page( + cursor: cursor, + limit: limit, + after: after, + before: before, + status: status, + tags: tags, + ids: ids, + user: user, + **resource + ) + end + + # # Cancel an IndividualAccountAttachment entity + # + # Cancel an IndividualAccountAttachment entity previously created in the Stark Infra API + # + # ## Parameters (required): + # - id [string]: IndividualAccountAttachment unique id. ex: '5656565656565656' + # + # ## Parameters (optional): + # - user [Organization/Project object, default nil]: Organization or Project object. Not necessary if StarkInfra.user was set before function call + # + # ## Return: + # - canceled IndividualAccountAttachment object + def self.cancel(id, user: nil) + StarkInfra::Utils::Rest.delete_id(id: id, user: user, **resource) + end + + def self.resource + { + resource_name: 'IndividualAccountAttachment', + resource_maker: proc { |json| + IndividualAccountAttachment.new( + type: json['type'], + content: json['content'], + content_type: json['content_type'], + account_request_id: json['account_request_id'], + tags: json['tags'], + id: json['id'], + status: json['status'], + created: json['created'] + ) + } + } + end + end +end diff --git a/lib/individual_account_attachment/log.rb b/lib/individual_account_attachment/log.rb new file mode 100644 index 0000000..ac98fb1 --- /dev/null +++ b/lib/individual_account_attachment/log.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +require('starkcore') +require_relative('../utils/rest') +require_relative('individual_account_attachment') + +module StarkInfra + class IndividualAccountAttachment + # # IndividualAccountAttachment::Log object + # + # Every time an IndividualAccountAttachment entity is modified, a corresponding IndividualAccountAttachment::Log + # is generated for the entity. This Log is never generated by the user, but it can be retrieved to check + # additional information on the IndividualAccountAttachment. + # + # ## Attributes (return-only): + # - id [string]: unique id returned when the log is created. ex: '5656565656565656' + # - attachment [IndividualAccountAttachment]: IndividualAccountAttachment entity to which the log refers to. + # - errors [list of strings]: list of errors linked to this IndividualAccountAttachment event. + # - type [string]: type of the IndividualAccountAttachment event which triggered the log creation. Options: 'created', 'success', 'failed', 'deleted' + # - created [DateTime]: creation datetime for the log. ex: DateTime.new(2020, 3, 10, 10, 30, 0, 0) + class Log < StarkCore::Utils::Resource + attr_reader :id, :attachment, :errors, :type, :created + def initialize(id: nil, attachment: nil, errors: nil, type: nil, created: nil) + super(id) + @attachment = attachment + @errors = errors + @type = type + @created = StarkCore::Utils::Checks.check_datetime(created) + end + + # # Retrieve a specific IndividualAccountAttachment::Log + # + # Receive a single IndividualAccountAttachment::Log object previously created by the Stark Infra API by passing its id + # + # ## Parameters (required): + # - id [string]: object unique id. ex: '5656565656565656' + # + # ## Parameters (optional): + # - user [Organization/Project object, default nil]: Organization or Project object. Not necessary if StarkInfra.user was set before function call + # + # ## Return: + # - IndividualAccountAttachment::Log object with updated attributes + def self.get(id, user: nil) + StarkInfra::Utils::Rest.get_id(id: id, user: user, **resource) + end + + # # Retrieve IndividualAccountAttachment::Logs + # + # Receive a generator of IndividualAccountAttachment::Log objects previously created in the Stark Infra API + # + # ## Parameters (optional): + # - limit [integer, default nil]: maximum number of objects to be retrieved. Unlimited if nil. ex: 35 + # - after [Date or string, default nil]: date filter for objects created only after specified date. ex: Date.new(2020, 3, 10) + # - before [Date or string, default nil]: date filter for objects created only before specified date. ex: Date.new(2020, 3, 10) + # - types [list of strings, default nil]: filter for log event types. Options: 'created', 'success', 'failed', 'deleted' + # - attachment_ids [list of strings, default nil]: list of IndividualAccountAttachment ids to filter logs. ex: ['5656565656565656', '4545454545454545'] + # - user [Organization/Project object, default nil]: Organization or Project object. Not necessary if StarkInfra.user was set before function call + # + # ## Return: + # - generator of IndividualAccountAttachment::Log objects with updated attributes + def self.query(limit: nil, after: nil, before: nil, types: nil, attachment_ids: nil, user: nil) + after = StarkCore::Utils::Checks.check_date(after) + before = StarkCore::Utils::Checks.check_date(before) + StarkInfra::Utils::Rest.get_stream( + limit: limit, + after: after, + before: before, + types: types, + attachment_ids: attachment_ids, + user: user, + **resource + ) + end + + # # Retrieve paged IndividualAccountAttachment::Logs + # + # Receive a list of up to 100 IndividualAccountAttachment::Log objects previously created in the Stark Infra API and the cursor to the next page. + # Use this function instead of query if you want to manually page your requests. + # + # ## Parameters (optional): + # - cursor [string, default nil]: cursor returned on the previous page function call + # - limit [integer, default 100]: maximum number of objects to be retrieved. Max = 100. ex: 35 + # - after [Date or string, default nil]: date filter for objects created only after specified date. ex: Date.new(2020, 3, 10) + # - before [Date or string, default nil]: date filter for objects created only before specified date. ex: Date.new(2020, 3, 10) + # - types [list of strings, default nil]: filter for log event types. Options: 'created', 'success', 'failed', 'deleted' + # - attachment_ids [list of strings, default nil]: list of IndividualAccountAttachment ids to filter logs. ex: ['5656565656565656', '4545454545454545'] + # - user [Organization/Project object, default nil]: Organization or Project object. Not necessary if StarkInfra.user was set before function call + # + # ## Return: + # - list of IndividualAccountAttachment::Log objects with updated attributes + # - cursor to retrieve the next page of Log objects + def self.page(cursor: nil, limit: nil, after: nil, before: nil, types: nil, attachment_ids: nil, user: nil) + after = StarkCore::Utils::Checks.check_date(after) + before = StarkCore::Utils::Checks.check_date(before) + StarkInfra::Utils::Rest.get_page( + cursor: cursor, + limit: limit, + after: after, + before: before, + types: types, + attachment_ids: attachment_ids, + user: user, + **resource + ) + end + + def self.resource + attachment_maker = StarkInfra::IndividualAccountAttachment.resource[:resource_maker] + { + resource_name: 'IndividualAccountAttachmentLog', + resource_maker: proc { |json| + Log.new( + id: json['id'], + attachment: StarkCore::Utils::API.from_api_json(attachment_maker, json['attachment']), + errors: json['errors'], + type: json['type'], + created: json['created'] + ) + } + } + end + end + end +end diff --git a/lib/individual_account_request/individual_account_request.rb b/lib/individual_account_request/individual_account_request.rb new file mode 100644 index 0000000..e81cb88 --- /dev/null +++ b/lib/individual_account_request/individual_account_request.rb @@ -0,0 +1,258 @@ +# frozen_string_literal: true + +require('starkcore') +require_relative('../utils/rest') + +module StarkInfra + # # IndividualAccountRequest object + # + # Request to open a Stark Infra account for an individual. The caller submits the individual's + # identifying data and income, and the API runs the approval flow asynchronously. Supporting + # documents are uploaded as IndividualAccountAttachment objects referencing this request. + # + # When you initialize an IndividualAccountRequest, the entity will not be automatically + # created in the Stark Infra API. The 'create' function sends the objects + # to the Stark Infra API and returns the list of created objects. + # + # ## Parameters (required): + # - name [string]: full legal name of the individual. ex: 'Tony Stark' + # - tax_id [string]: individual's Brazilian CPF. ex: '012.345.678-90' or '01234567890' + # - address [IndividualAccountRequest::Address object]: structured residential address. + # - income [integer]: monthly income in cents. Must be >= 0. ex: 1000000 (= R$ 10,000.00) + # + # ## Parameters (optional): + # - tags [list of strings, default nil]: list of strings for reference when searching for IndividualAccountRequests. ex: ['employees', 'monthly'] + # + # ## Attributes (return-only): + # - id [string]: unique id returned when the IndividualAccountRequest is created. ex: '5656565656565656' + # - status [string]: current status of the IndividualAccountRequest. Options: 'approved', 'created', 'denied', 'processing', 'updated' + # - account_type [string]: account type of the request. Always 'individual' for this resource. ex: 'individual' + # - flags [list of strings]: server-side review flags. Empty unless the request triggered a manual-review condition. + # - created [DateTime]: creation datetime for the IndividualAccountRequest. ex: DateTime.new(2020, 3, 10, 10, 30, 0, 0) + # - updated [DateTime]: latest update datetime for the IndividualAccountRequest. ex: DateTime.new(2020, 3, 10, 10, 30, 0, 0) + class IndividualAccountRequest < StarkCore::Utils::Resource + attr_reader :name, :tax_id, :address, :income, :tags, :id, :status, :account_type, :flags, :created, :updated + def initialize( + name:, tax_id:, address:, income:, tags: nil, + id: nil, status: nil, account_type: nil, flags: nil, created: nil, updated: nil + ) + super(id) + @name = name + @tax_id = tax_id + @address = IndividualAccountRequest::Address.parse_address(address) + @income = income + @tags = tags + @status = status + @account_type = account_type + @flags = flags + @created = StarkCore::Utils::Checks.check_datetime(created) + @updated = StarkCore::Utils::Checks.check_datetime(updated) + end + + # # Create IndividualAccountRequests + # + # Send a list of IndividualAccountRequest objects for creation at the Stark Infra API + # + # ## Parameters (required): + # - requests [list of IndividualAccountRequest objects]: list of IndividualAccountRequest objects to be created in the API. + # + # ## Parameters (optional): + # - user [Organization/Project object, default nil]: Organization or Project object. Not necessary if StarkInfra.user was set before function call + # + # ## Return: + # - list of IndividualAccountRequest objects with updated attributes + def self.create(requests, user: nil) + # Output-only fields (id, status, account_type, flags, created, updated) are + # rejected by the API on POST with "Unknown parameters in JSON: ...". Strip + # them from a shallow dup so the caller's objects are unmodified. The core + # serializer (StarkCore::Utils::API.build_entity_hash) skips nil ivars. + stripped = requests.map do |req| + copy = req.dup + %i[@id @status @account_type @flags @created @updated].each do |ivar| + copy.instance_variable_set(ivar, nil) + end + copy + end + StarkInfra::Utils::Rest.post(entities: stripped, user: user, **resource) + end + + # # Retrieve a specific IndividualAccountRequest + # + # Receive a single IndividualAccountRequest object previously created in the Stark Infra API by its id + # + # ## Parameters (required): + # - id [string]: object unique id. ex: '5656565656565656' + # + # ## Parameters (optional): + # - user [Organization/Project object, default nil]: Organization or Project object. Not necessary if StarkInfra.user was set before function call + # + # ## Return: + # - IndividualAccountRequest object with updated attributes + def self.get(id, user: nil) + StarkInfra::Utils::Rest.get_id(id: id, user: user, **resource) + end + + # # Retrieve IndividualAccountRequests + # + # Receive a generator of IndividualAccountRequest objects previously created in the Stark Infra API + # + # ## Parameters (optional): + # - limit [integer, default nil]: maximum number of objects to be retrieved. Unlimited if nil. ex: 35 + # - after [Date or string, default nil]: date filter for objects created only after specified date. ex: Date.new(2020, 3, 10) + # - before [Date or string, default nil]: date filter for objects created only before specified date. ex: Date.new(2020, 3, 10) + # - status [string, default nil]: filter for status of retrieved objects. ex: 'created' + # - tags [list of strings, default nil]: tags to filter retrieved objects. ex: ['tony', 'stark'] + # - ids [list of strings, default nil]: list of ids to filter retrieved objects. ex: ['5656565656565656', '4545454545454545'] + # - user [Organization/Project object, default nil]: Organization or Project object. Not necessary if StarkInfra.user was set before function call + # + # ## Return: + # - generator of IndividualAccountRequest objects with updated attributes + def self.query(limit: nil, after: nil, before: nil, status: nil, tags: nil, ids: nil, user: nil) + after = StarkCore::Utils::Checks.check_date(after) + before = StarkCore::Utils::Checks.check_date(before) + StarkInfra::Utils::Rest.get_stream( + limit: limit, + after: after, + before: before, + status: status, + tags: tags, + ids: ids, + user: user, + **resource + ) + end + + # # Retrieve paged IndividualAccountRequests + # + # Receive a list of up to 100 IndividualAccountRequest objects previously created in the Stark Infra API and the cursor to the next page. + # Use this function instead of query if you want to manually page your requests. + # + # ## Parameters (optional): + # - cursor [string, default nil]: cursor returned on the previous page function call + # - limit [integer, default 100]: maximum number of objects to be retrieved. Max = 100. ex 35 + # - after [Date or string, default nil]: date filter for objects created only after specified date. ex: Date.new(2020, 3, 10) + # - before [Date or string, default nil]: date filter for objects created only before specified date. ex: Date.new(2020, 3, 10) + # - status [string, default nil]: filter for status of retrieved objects. ex: 'created' + # - tags [list of strings, default nil]: tags to filter retrieved objects. ex: ['tony', 'stark'] + # - ids [list of strings, default nil]: list of ids to filter retrieved objects. ex: ['5656565656565656', '4545454545454545'] + # - user [Organization/Project object, default nil]: Organization or Project object. Not necessary if StarkInfra.user was set before function call + # + # ## Return: + # - list of IndividualAccountRequest objects with updated attributes + # - cursor to retrieve the next page of IndividualAccountRequest objects + def self.page(cursor: nil, limit: nil, after: nil, before: nil, status: nil, tags: nil, ids: nil, user: nil) + after = StarkCore::Utils::Checks.check_date(after) + before = StarkCore::Utils::Checks.check_date(before) + StarkInfra::Utils::Rest.get_page( + cursor: cursor, + limit: limit, + after: after, + before: before, + status: status, + tags: tags, + ids: ids, + user: user, + **resource + ) + end + + # # Update an IndividualAccountRequest entity + # + # Update an IndividualAccountRequest by passing its id. + # + # ## Parameters (required): + # - id [string]: IndividualAccountRequest unique id. ex: '5656565656565656' + # + # ## Parameters (optional): + # - name [string, default nil]: replace the legal name. ex: 'Tony Stark' + # - tax_id [string, default nil]: replace the CPF. ex: '012.345.678-90' + # - address [IndividualAccountRequest::Address object, default nil]: replace the address as a whole object. + # - income [integer, default nil]: replace monthly income in cents. ex: 1000000 + # - status [string, default nil]: manual state transition. ex: 'processing' + # - tags [list of strings, default nil]: replace tag list. ex: ['employees', 'monthly'] + # - user [Organization/Project object, default nil]: Organization or Project object. Not necessary if StarkInfra.user was set before function call + # + # ## Return: + # - target IndividualAccountRequest with updated attributes + def self.update(id, name: nil, tax_id: nil, address: nil, income: nil, status: nil, tags: nil, user: nil) + StarkInfra::Utils::Rest.patch_id( + id: id, + name: name, + tax_id: tax_id, + address: address, + income: income, + status: status, + tags: tags, + user: user, + **resource + ) + end + + def self.resource + { + resource_name: 'IndividualAccountRequest', + resource_maker: proc { |json| + IndividualAccountRequest.new( + id: json['id'], + name: json['name'], + tax_id: json['tax_id'], + address: json['address'], + income: json['income'], + tags: json['tags'], + status: json['status'], + account_type: json['account_type'], + flags: json['flags'], + created: json['created'], + updated: json['updated'] + ) + } + } + end + + # # IndividualAccountRequest::Address object + # + # Structured residential address embedded as the `address` field on an IndividualAccountRequest. + # Serialized as a nested JSON object on the wire (never flattened). + # + # ## Parameters (required): + # - street [string]: street name. ex: 'Rua do Estilo Barroco' + # - number [string]: street number. ex: '648' + # - neighborhood [string]: neighborhood / district. ex: 'Santo Amaro' + # - city [string]: city. ex: 'SP' + # - state [string]: state (BR 2-letter code). ex: 'SP' + # - zip_code [string]: ZIP code (BR CEP). ex: '05724005' + class Address < StarkCore::Utils::SubResource + attr_reader :street, :number, :neighborhood, :city, :state, :zip_code + def initialize(street: nil, number: nil, neighborhood: nil, city: nil, state: nil, zip_code: nil) + @street = street + @number = number + @neighborhood = neighborhood + @city = city + @state = state + @zip_code = zip_code + end + + def self.parse_address(address) + return address if address.nil? || address.is_a?(IndividualAccountRequest::Address) + + StarkCore::Utils::API.from_api_json(resource[:resource_maker], address) + end + + def self.resource + { + resource_name: 'Address', + resource_maker: proc { |json| + Address.new( + street: json['street'], + number: json['number'], + neighborhood: json['neighborhood'], + city: json['city'], + state: json['state'], + zip_code: json['zip_code'] + ) + } + } + end + end + end +end diff --git a/lib/individual_account_request/log.rb b/lib/individual_account_request/log.rb new file mode 100644 index 0000000..8eb5323 --- /dev/null +++ b/lib/individual_account_request/log.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +require('starkcore') +require_relative('../utils/rest') +require_relative('individual_account_request') + +module StarkInfra + class IndividualAccountRequest + # # IndividualAccountRequest::Log object + # + # Every time an IndividualAccountRequest entity is modified, a corresponding IndividualAccountRequest::Log + # is generated for the entity. This Log is never generated by the user, but it can be retrieved to check + # additional information on the IndividualAccountRequest. + # + # ## Attributes (return-only): + # - id [string]: unique id returned when the log is created. ex: '5656565656565656' + # - request [IndividualAccountRequest]: IndividualAccountRequest entity to which the log refers to. + # - errors [list of strings]: list of errors linked to this IndividualAccountRequest event. + # - type [string]: type of the IndividualAccountRequest event which triggered the log creation. Options: 'approved', 'created', 'denied', 'processing', 'updated' + # - created [DateTime]: creation datetime for the log. ex: DateTime.new(2020, 3, 10, 10, 30, 0, 0) + class Log < StarkCore::Utils::Resource + attr_reader :id, :request, :errors, :type, :created + def initialize(id: nil, request: nil, errors: nil, type: nil, created: nil) + super(id) + @request = request + @errors = errors + @type = type + @created = StarkCore::Utils::Checks.check_datetime(created) + end + + # # Retrieve a specific IndividualAccountRequest::Log + # + # Receive a single IndividualAccountRequest::Log object previously created by the Stark Infra API by passing its id + # + # ## Parameters (required): + # - id [string]: object unique id. ex: '5656565656565656' + # + # ## Parameters (optional): + # - user [Organization/Project object, default nil]: Organization or Project object. Not necessary if StarkInfra.user was set before function call + # + # ## Return: + # - IndividualAccountRequest::Log object with updated attributes + def self.get(id, user: nil) + StarkInfra::Utils::Rest.get_id(id: id, user: user, **resource) + end + + # # Retrieve IndividualAccountRequest::Logs + # + # Receive a generator of IndividualAccountRequest::Log objects previously created in the Stark Infra API + # + # ## Parameters (optional): + # - limit [integer, default nil]: maximum number of objects to be retrieved. Unlimited if nil. ex: 35 + # - after [Date or string, default nil]: date filter for objects created only after specified date. ex: Date.new(2020, 3, 10) + # - before [Date or string, default nil]: date filter for objects created only before specified date. ex: Date.new(2020, 3, 10) + # - types [list of strings, default nil]: filter for log event types. Options: 'approved', 'created', 'denied', 'processing', 'updated' + # - account_request_ids [list of strings, default nil]: list of IndividualAccountRequest ids to filter logs. ex: ['5656565656565656', '4545454545454545'] + # - user [Organization/Project object, default nil]: Organization or Project object. Not necessary if StarkInfra.user was set before function call + # + # ## Return: + # - generator of IndividualAccountRequest::Log objects with updated attributes + def self.query(limit: nil, after: nil, before: nil, types: nil, account_request_ids: nil, user: nil) + after = StarkCore::Utils::Checks.check_date(after) + before = StarkCore::Utils::Checks.check_date(before) + StarkInfra::Utils::Rest.get_stream( + limit: limit, + after: after, + before: before, + types: types, + account_request_ids: account_request_ids, + user: user, + **resource + ) + end + + # # Retrieve paged IndividualAccountRequest::Logs + # + # Receive a list of up to 100 IndividualAccountRequest::Log objects previously created in the Stark Infra API and the cursor to the next page. + # Use this function instead of query if you want to manually page your requests. + # + # ## Parameters (optional): + # - cursor [string, default nil]: cursor returned on the previous page function call + # - limit [integer, default 100]: maximum number of objects to be retrieved. Max = 100. ex: 35 + # - after [Date or string, default nil]: date filter for objects created only after specified date. ex: Date.new(2020, 3, 10) + # - before [Date or string, default nil]: date filter for objects created only before specified date. ex: Date.new(2020, 3, 10) + # - types [list of strings, default nil]: filter for log event types. Options: 'approved', 'created', 'denied', 'processing', 'updated' + # - account_request_ids [list of strings, default nil]: list of IndividualAccountRequest ids to filter logs. ex: ['5656565656565656', '4545454545454545'] + # - user [Organization/Project object, default nil]: Organization or Project object. Not necessary if StarkInfra.user was set before function call + # + # ## Return: + # - list of IndividualAccountRequest::Log objects with updated attributes + # - cursor to retrieve the next page of Log objects + def self.page(cursor: nil, limit: nil, after: nil, before: nil, types: nil, account_request_ids: nil, user: nil) + after = StarkCore::Utils::Checks.check_date(after) + before = StarkCore::Utils::Checks.check_date(before) + StarkInfra::Utils::Rest.get_page( + cursor: cursor, + limit: limit, + after: after, + before: before, + types: types, + account_request_ids: account_request_ids, + user: user, + **resource + ) + end + + def self.resource + request_maker = StarkInfra::IndividualAccountRequest.resource[:resource_maker] + { + resource_name: 'IndividualAccountRequestLog', + resource_maker: proc { |json| + Log.new( + id: json['id'], + request: StarkCore::Utils::API.from_api_json(request_maker, json['request']), + errors: json['errors'], + type: json['type'], + created: json['created'] + ) + } + } + end + end + end +end diff --git a/lib/starkinfra.rb b/lib/starkinfra.rb index 112c1af..96dd847 100644 --- a/lib/starkinfra.rb +++ b/lib/starkinfra.rb @@ -13,6 +13,10 @@ require_relative('credit_preview/credit_note_preview') require_relative('credit_signer/credit_signer') require_relative('dynamic_brcode/dynamic_brcode') +require_relative('individual_account_request/individual_account_request') +require_relative('individual_account_request/log') +require_relative('individual_account_attachment/individual_account_attachment') +require_relative('individual_account_attachment/log') require_relative('individual_document/individual_document') require_relative('individual_document/log') require_relative('individual_identity/individual_identity') diff --git a/test/example_generator.rb b/test/example_generator.rb index b883236..4bba06b 100644 --- a/test/example_generator.rb +++ b/test/example_generator.rb @@ -438,4 +438,46 @@ def self.credit_holmes_example tags: ["SDK Ruby Test"] ) end + + def self.individual_account_request_address_example + StarkInfra::IndividualAccountRequest::Address.new( + street: 'Rua do Estilo Barroco', + number: '648', + neighborhood: 'Santo Amaro', + city: 'SP', + state: 'SP', + zip_code: '05724005' + ) + end + + def self.individual_account_request_example( + name: 'Tony Stark', + tax_id: '012.345.678-90', + address: nil, + income: 1_000_000, + tags: ['SDK Ruby Test'] + ) + StarkInfra::IndividualAccountRequest.new( + name: name, + tax_id: tax_id, + address: address.nil? ? individual_account_request_address_example : address, + income: income, + tags: tags + ) + end + + def self.individual_account_attachment_example( + account_request_id:, + type: 'identity-front', + content_type: 'image/png', + tags: ['SDK Ruby Test'] + ) + StarkInfra::IndividualAccountAttachment.new( + type: type, + content: individual_document_image('front'), + content_type: content_type, + account_request_id: account_request_id, + tags: tags + ) + end end diff --git a/test/starkinfra/test_individualaccountattachment.rb b/test/starkinfra/test_individualaccountattachment.rb new file mode 100644 index 0000000..0e54516 --- /dev/null +++ b/test/starkinfra/test_individualaccountattachment.rb @@ -0,0 +1,221 @@ +# frozen_string_literal: true + +require_relative('../test_helper.rb') +require_relative('../example_generator.rb') + + +describe(StarkInfra::IndividualAccountAttachment, '#individual-account-attachment#') do + # --- happy-path CRUD verbs (contract §Endpoints) --- + + # M1: create accepts a list and returns the same shape with server-assigned + # id, status, created populated. M4: get(id) returns a single attachment. + # Each attachment test creates a FRESH parent IndividualAccountRequest to + # avoid "attachment already sent" on a polluted parent. + it 'create and get' do + parent = StarkInfra::IndividualAccountRequest.create( + [ExampleGenerator.individual_account_request_example] + )[0] + + attachment = StarkInfra::IndividualAccountAttachment.create( + [ExampleGenerator.individual_account_attachment_example(account_request_id: parent.id)] + )[0] + + expect(attachment.id).wont_be_nil + expect(attachment.status).wont_be_nil + + attachment_get = StarkInfra::IndividualAccountAttachment.get(attachment.id) + expect(attachment.id).must_equal(attachment_get.id) + end + + # M5: query returns an iterable accepting limit, after, before, status, tags, ids. + it 'query' do + attachments = StarkInfra::IndividualAccountAttachment.query(limit: 5).to_a + attachments.each do |attachment| + expect(attachment.id).wont_be_nil + end + end + + it 'query params' do + attachments = StarkInfra::IndividualAccountAttachment.query( + limit: 4, + after: '2022-01-01', + before: '2022-02-01', + status: 'created', + tags: %w[1 2 3], + ids: %w[1 2 3] + ).to_a + expect(attachments.length).must_equal(0) + end + + # M6: page returns [items, cursor] and accepts the query params plus cursor. + it 'page' do + ids = [] + cursor = nil + (0..1).step(1) do + attachments, cursor = StarkInfra::IndividualAccountAttachment.page(limit: 5, cursor: cursor) + attachments.each do |attachment| + expect(ids).wont_include(attachment.id) + ids << attachment.id + end + break if cursor.nil? + end + expect(ids.length).must_equal(10) + end + + it 'page params' do + attachments = StarkInfra::IndividualAccountAttachment.page( + limit: 4, + after: '2022-01-01', + before: '2022-02-01', + status: 'created', + tags: %w[1 2 3], + ids: %w[1 2 3] + ).to_a + expect(attachments.length).must_equal(2) + end + + # M7: cancel maps to DELETE and returns status 'deleted' (sandbox uses + # 'deleted', not 'canceled'); cancel is IDEMPOTENT — a second cancel succeeds. + it 'create and cancel is idempotent' do + parent = StarkInfra::IndividualAccountRequest.create( + [ExampleGenerator.individual_account_request_example] + )[0] + attachment = StarkInfra::IndividualAccountAttachment.create( + [ExampleGenerator.individual_account_attachment_example(account_request_id: parent.id)] + )[0] + + canceled = StarkInfra::IndividualAccountAttachment.cancel(attachment.id) + expect(canceled.id).must_equal(attachment.id) + expect(canceled.status).must_equal('deleted') + + # Second cancel on the already-deleted attachment succeeds without error. + canceled_again = StarkInfra::IndividualAccountAttachment.cancel(attachment.id) + expect(canceled_again.id).must_equal(attachment.id) + end + + # --- M2 / M3: data-URL encoding client-side; contentType is input-only --- + it 'constructor encodes content and contentType into a data URL' do + parent = StarkInfra::IndividualAccountRequest.create( + [ExampleGenerator.individual_account_request_example] + )[0] + image = ExampleGenerator.individual_document_image('front') + + attachment_input = StarkInfra::IndividualAccountAttachment.new( + type: 'identity-front', + content: image, + content_type: 'image/png', + account_request_id: parent.id + ) + attachment = StarkInfra::IndividualAccountAttachment.create([attachment_input])[0] + expect(attachment.id).wont_be_nil + end + + # M3: contentType is input-only — not serialized as its own wire field, so a + # deserialized response object does not expose it as a populated attribute. + it 'contentType is not present on the deserialized response object' do + parent = StarkInfra::IndividualAccountRequest.create( + [ExampleGenerator.individual_account_request_example] + )[0] + attachment = StarkInfra::IndividualAccountAttachment.create( + [ExampleGenerator.individual_account_attachment_example(account_request_id: parent.id)] + )[0] + fetched = StarkInfra::IndividualAccountAttachment.get(attachment.id) + # content_type is consumed client-side; the server never echoes it back. + expect(fetched.content_type).must_be_nil if fetched.respond_to?(:content_type) + end + + # --- M11: type enum --- + it 'type enum is a member of the documented set' do + valid = %w[drivers-license-front drivers-license-back identity-front identity-back] + attachments = StarkInfra::IndividualAccountAttachment.query(limit: 5).to_a + attachments.each do |attachment| + expect(valid).must_include(attachment.type) unless attachment.type.nil? + end + end + + # --- M12: datetime field parsed to native type --- + # check_datetime parses an ISO string to DateTime (core-repos/ruby/lib/utils/checks.rb:79,84); + # DateTime is NOT a subclass of Time in Ruby, so assert non-nil (parse succeeded) rather + # than pinning a concrete class — code-agnostic per the v3 contract. + it 'created parses to native datetime type' do + parent = StarkInfra::IndividualAccountRequest.create( + [ExampleGenerator.individual_account_request_example] + )[0] + attachment = StarkInfra::IndividualAccountAttachment.create( + [ExampleGenerator.individual_account_attachment_example(account_request_id: parent.id)] + )[0] + expect(attachment.created).wont_be_nil + end + + # --- error cases (M13): assert the mapped exception TYPE is raised, + # never a specific error-code string. --- + + # M11 also: 'selfie' / any non-enum type is rejected with InputErrors. + it 'create with invalid type raises InputErrors' do + parent = StarkInfra::IndividualAccountRequest.create( + [ExampleGenerator.individual_account_request_example] + )[0] + assert_raises(StarkInfra::Error::InputErrors) do + StarkInfra::IndividualAccountAttachment.create([ + StarkInfra::IndividualAccountAttachment.new( + type: 'not-a-real-type', + content: ExampleGenerator.individual_document_image('front'), + content_type: 'image/png', + account_request_id: parent.id + ) + ]) + end + end + + it 'create with empty content raises InputErrors' do + parent = StarkInfra::IndividualAccountRequest.create( + [ExampleGenerator.individual_account_request_example] + )[0] + assert_raises(StarkInfra::Error::InputErrors) do + StarkInfra::IndividualAccountAttachment.create([ + StarkInfra::IndividualAccountAttachment.new( + type: 'identity-front', + content: '', + content_type: 'image/png', + account_request_id: parent.id + ) + ]) + end + end + + # contentType missing when content provided — code is client-serialization + # dependent; assert only that InputErrors is raised. + it 'create with missing contentType raises InputErrors' do + parent = StarkInfra::IndividualAccountRequest.create( + [ExampleGenerator.individual_account_request_example] + )[0] + assert_raises(StarkInfra::Error::InputErrors) do + StarkInfra::IndividualAccountAttachment.create([ + StarkInfra::IndividualAccountAttachment.new( + type: 'identity-front', + content: ExampleGenerator.individual_document_image('front'), + account_request_id: parent.id + ) + ]) + end + end + + it 'create with unknown accountRequestId raises InputErrors' do + assert_raises(StarkInfra::Error::InputErrors) do + StarkInfra::IndividualAccountAttachment.create([ + StarkInfra::IndividualAccountAttachment.new( + type: 'identity-front', + content: ExampleGenerator.individual_document_image('front'), + content_type: 'image/png', + account_request_id: '0' + ) + ]) + end + end + + it 'get with unknown id raises InputErrors' do + assert_raises(StarkInfra::Error::InputErrors) do + StarkInfra::IndividualAccountAttachment.get('0') + end + end +end diff --git a/test/starkinfra/test_individualaccountattachment_log.rb b/test/starkinfra/test_individualaccountattachment_log.rb new file mode 100644 index 0000000..31d49df --- /dev/null +++ b/test/starkinfra/test_individualaccountattachment_log.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +require_relative('../test_helper.rb') + + +describe(StarkInfra::IndividualAccountAttachment::Log, '#individual-account-attachment/log#') do + # M9: Log is read-only under .log with get/query/page; the + # `attachment` field is the parent type, not a string id. + it 'query logs' do + logs = StarkInfra::IndividualAccountAttachment::Log.query(limit: 10, types: 'created').to_a + expect(logs.length).must_equal(10) + logs.each do |log| + expect(log.id).wont_be_nil + expect(log.type).must_equal('created') + # exercises the recursive parent rebuild via API.from_api_json: + expect(log.attachment.status).wont_be_nil + end + end + + it 'page' do + ids = [] + cursor = nil + (0..1).step(1) do + logs, cursor = StarkInfra::IndividualAccountAttachment::Log.page(limit: 5, cursor: cursor) + logs.each do |log| + expect(ids).wont_include(log.id) + ids << log.id + end + break if cursor.nil? + end + expect(ids.length).must_equal(10) + end + + it 'query and get' do + log = StarkInfra::IndividualAccountAttachment::Log.query(limit: 1).to_a[0] + next if log.nil? + + get_log = StarkInfra::IndividualAccountAttachment::Log.get(log.id) + expect(log.id).must_equal(get_log.id) + end + + # M10: Log.query and Log.page accept limit, after, before, types, attachmentIds + # — the parent-id filter is named attachmentIds, NOT accountRequestIds. + it 'query params' do + logs = StarkInfra::IndividualAccountAttachment::Log.query( + limit: 1, + after: '2023-01-01', + before: '2023-01-02', + types: ['created'], + attachment_ids: ['1'] + ).to_a + expect(logs.length).must_equal(0) + end + + it 'page params' do + logs = StarkInfra::IndividualAccountAttachment::Log.page( + limit: 1, + after: '2023-01-01', + before: '2023-01-02', + types: ['created'], + attachment_ids: ['1'] + ).to_a + expect(logs.length).must_equal(2) + end +end diff --git a/test/starkinfra/test_individualaccountrequest.rb b/test/starkinfra/test_individualaccountrequest.rb new file mode 100644 index 0000000..cae63f4 --- /dev/null +++ b/test/starkinfra/test_individualaccountrequest.rb @@ -0,0 +1,222 @@ +# frozen_string_literal: true + +require_relative('../test_helper.rb') +require_relative('../example_generator.rb') + + +describe(StarkInfra::IndividualAccountRequest, '#individual-account-request#') do + # --- happy-path CRUD verbs (contract §Endpoints) --- + + # M1: create accepts a list and returns the same shape with server-assigned + # id, status, accountType, created, updated populated. + it 'create and get' do + request = StarkInfra::IndividualAccountRequest.create( + [ExampleGenerator.individual_account_request_example] + )[0] + + expect(request.id).wont_be_nil + expect(request.status).wont_be_nil + expect(request.account_type).must_equal('individual') + + # M3: get(id) returns a single IndividualAccountRequest by id. + request_get = StarkInfra::IndividualAccountRequest.get(request.id) + expect(request.id).must_equal(request_get.id) + end + + # M4: query returns an iterable of IndividualAccountRequest accepting + # limit, after, before, status, tags, ids. + it 'query' do + requests = StarkInfra::IndividualAccountRequest.query(limit: 5).to_a + requests.each do |request| + expect(request.id).wont_be_nil + end + end + + it 'query params' do + requests = StarkInfra::IndividualAccountRequest.query( + limit: 4, + after: '2022-01-01', + before: '2022-02-01', + status: 'created', + tags: %w[1 2 3], + ids: %w[1 2 3] + ).to_a + expect(requests.length).must_equal(0) + end + + # M5: page returns [items, cursor] and accepts the query params plus cursor. + it 'page' do + ids = [] + cursor = nil + (0..1).step(1) do + requests, cursor = StarkInfra::IndividualAccountRequest.page(limit: 5, cursor: cursor) + requests.each do |request| + expect(ids).wont_include(request.id) + ids << request.id + end + break if cursor.nil? + end + expect(ids.length).must_equal(10) + end + + it 'page params' do + requests = StarkInfra::IndividualAccountRequest.page( + limit: 4, + after: '2022-01-01', + before: '2022-02-01', + status: 'created', + tags: %w[1 2 3], + ids: %w[1 2 3] + ).to_a + expect(requests.length).must_equal(2) + end + + # M6: update(id, ...) PATCHes the request, replacing (not deep-merging) the + # address object. Self-contained: create a fresh record and patch name/address + # (NOT status — status patch is exercised only as an error case below). + it 'create and update' do + request = StarkInfra::IndividualAccountRequest.create( + [ExampleGenerator.individual_account_request_example] + )[0] + + new_address = StarkInfra::IndividualAccountRequest::Address.new( + street: 'Rua Nova', + number: '900', + neighborhood: 'Centro', + city: 'SP', + state: 'SP', + zip_code: '01310-100' + ) + + updated = StarkInfra::IndividualAccountRequest.update( + request.id, + name: 'Tony Stark Updated', + address: new_address + ) + expect(updated.id).must_equal(request.id) + end + + # --- M2: address is a structured object, never flattened --- + it 'address is constructed as a structured object' do + address = StarkInfra::IndividualAccountRequest::Address.new( + street: 'Rua do Estilo Barroco', + number: '648', + neighborhood: 'Santo Amaro', + city: 'SP', + state: 'SP', + zip_code: '05724005' + ) + expect(address.street).must_equal('Rua do Estilo Barroco') + expect(address.number).must_equal('648') + expect(address.zip_code).must_equal('05724005') + + request = StarkInfra::IndividualAccountRequest.create( + [ExampleGenerator.individual_account_request_example] + )[0] + # The address round-trips as an object, not as flattened addressStreet/etc. + expect(request.id).wont_be_nil + end + + # --- M7: status enum membership --- + it 'status enum is a member of the sandbox-emitted set' do + valid = %w[approved created denied processing updated] + requests = StarkInfra::IndividualAccountRequest.query(limit: 5).to_a + requests.each do |request| + expect(valid).must_include(request.status) unless request.status.nil? + end + end + + # --- M10: datetime fields parsed to native datetime type --- + # check_datetime parses an ISO string to DateTime (core-repos/ruby/lib/utils/checks.rb:79,84); + # DateTime is NOT a subclass of Time in Ruby, so assert non-nil (parse succeeded) rather + # than pinning a concrete class — code-agnostic per the v3 contract. + it 'datetime fields parse to native type' do + request = StarkInfra::IndividualAccountRequest.create( + [ExampleGenerator.individual_account_request_example] + )[0] + expect(request.created).wont_be_nil + expect(request.updated).wont_be_nil unless request.updated.nil? + end + + # --- M11: output-only fields are not serialized into the POST body --- + # Passing accountType/status/id/created/updated to the constructor must not + # crash and must not be rejected by the API; the server-assigned values win. + it 'output-only fields passed to constructor are ignored by the API' do + request_input = StarkInfra::IndividualAccountRequest.new( + name: 'Tony Stark', + tax_id: '012.345.678-90', + income: 1_000_000, + address: StarkInfra::IndividualAccountRequest::Address.new( + street: 'Rua do Estilo Barroco', + number: '648', + neighborhood: 'Santo Amaro', + city: 'SP', + state: 'SP', + zip_code: '05724005' + ), + status: 'approved', + account_type: 'individual', + flags: ['ignored'], + id: '999', + created: '2020-01-01T00:00:00.000000+00:00', + updated: '2020-01-01T00:00:00.000000+00:00' + ) + request = StarkInfra::IndividualAccountRequest.create([request_input])[0] + # Server assigns its own id, ignoring the client-supplied one. + expect(request.id).wont_equal('999') + end + + # --- error cases (M12): assert the mapped exception TYPE is raised, + # never a specific error-code string. --- + + it 'create with empty name raises InputErrors' do + assert_raises(StarkInfra::Error::InputErrors) do + StarkInfra::IndividualAccountRequest.create( + [ExampleGenerator.individual_account_request_example(name: '')] + ) + end + end + + it 'create with invalid taxId raises InputErrors' do + assert_raises(StarkInfra::Error::InputErrors) do + StarkInfra::IndividualAccountRequest.create( + [ExampleGenerator.individual_account_request_example(tax_id: '000.000.000-00')] + ) + end + end + + it 'create with incomplete address raises InputErrors' do + bad_address = StarkInfra::IndividualAccountRequest::Address.new( + street: 'Rua do Estilo Barroco' + # missing number, neighborhood, city, state, zip_code + ) + assert_raises(StarkInfra::Error::InputErrors) do + StarkInfra::IndividualAccountRequest.create( + [ExampleGenerator.individual_account_request_example(address: bad_address)] + ) + end + end + + it 'create with negative income raises InputErrors' do + assert_raises(StarkInfra::Error::InputErrors) do + StarkInfra::IndividualAccountRequest.create( + [ExampleGenerator.individual_account_request_example(income: -1)] + ) + end + end + + it 'update with invalid status raises InputErrors' do + request = StarkInfra::IndividualAccountRequest.create( + [ExampleGenerator.individual_account_request_example] + )[0] + assert_raises(StarkInfra::Error::InputErrors) do + StarkInfra::IndividualAccountRequest.update(request.id, status: 'not-a-real-status') + end + end + + it 'get with unknown id raises InputErrors' do + assert_raises(StarkInfra::Error::InputErrors) do + StarkInfra::IndividualAccountRequest.get('0') + end + end +end diff --git a/test/starkinfra/test_individualaccountrequest_log.rb b/test/starkinfra/test_individualaccountrequest_log.rb new file mode 100644 index 0000000..d28cd0c --- /dev/null +++ b/test/starkinfra/test_individualaccountrequest_log.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require_relative('../test_helper.rb') + + +describe(StarkInfra::IndividualAccountRequest::Log, '#individual-account-request/log#') do + # M8: Log is read-only under .log with get/query/page; the `request` + # field is the parent type, not a string id. + it 'query logs' do + logs = StarkInfra::IndividualAccountRequest::Log.query(limit: 10, types: 'created').to_a + expect(logs.length).must_equal(10) + logs.each do |log| + expect(log.id).wont_be_nil + expect(log.type).must_equal('created') + # exercises the recursive parent rebuild via API.from_api_json: + expect(log.request.status).wont_be_nil + end + end + + it 'page' do + ids = [] + cursor = nil + (0..1).step(1) do + logs, cursor = StarkInfra::IndividualAccountRequest::Log.page(limit: 5, cursor: cursor) + logs.each do |log| + expect(ids).wont_include(log.id) + ids << log.id + end + break if cursor.nil? + end + expect(ids.length).must_equal(10) + end + + it 'query and get' do + log = StarkInfra::IndividualAccountRequest::Log.query(limit: 1).to_a[0] + next if log.nil? + + get_log = StarkInfra::IndividualAccountRequest::Log.get(log.id) + expect(log.id).must_equal(get_log.id) + end + + # M9: Log.query and Log.page accept limit, after, before, types, accountRequestIds. + it 'query params' do + logs = StarkInfra::IndividualAccountRequest::Log.query( + limit: 1, + after: '2023-01-01', + before: '2023-01-02', + types: ['created'], + account_request_ids: ['1'] + ).to_a + expect(logs.length).must_equal(0) + end + + it 'page params' do + logs = StarkInfra::IndividualAccountRequest::Log.page( + limit: 1, + after: '2023-01-01', + before: '2023-01-02', + types: ['created'], + account_request_ids: ['1'] + ).to_a + expect(logs.length).must_equal(2) + end +end