# frozen_string_literal: true require_relative 'context' require_relative 'finder_methods' require_relative 'pagination_methods' require_relative 'persistence' require_relative 'query_methods' module ErpIntegration module Fulfil class ApiResource include Context include Enumerable include FinderMethods include PaginationMethods include Persistence include QueryMethods attr_accessor :resource_klass delegate :client, :model_name, to: 'self.class' def initialize(resource_klass) @resource_klass = resource_klass end # The `client` exposes the `ErpIntegration::Fulfil::Client` to the class. # @return [ErpIntegration::Fulfil::Client] The HTTP client for Fulfil. def self.client Client.new( api_key: config.fulfil_api_key, merchant_id: config.fulfil_merchant_id, logger: config.logger ) end # The `config` exposes the gem's configuration to the `ApiResource`. # @return [ErpIntegration::Configuration] The gem's configuration object. def self.config ErpIntegration.config end # Fulfil doesn't use logical naming conventions. However, we do need # the name of a model to build the resource URI. # # By manually setting the model name, we allow the `Fulfil::Connection` # module to connect to the correct API endpoint. # # @param name [String] The logical path name in Fulfil. # @return [String] The model name def self.model_name=(name) instance_variable_set(:@model_name, name) end def self.model_name instance_variable_get(:@model_name) end # The `where` and `includes` methods lazyly build a search/read query # for Fulfil. By calling `all`, the prepared search/read query will actually # be executed and the results will be fetched. # @return [Array] An enumerable collection object with all API results. def all return @results if defined?(@results) @results = client.put( api_resource_path, Query.new( fields: selected_fields, filters: where_clauses, alternative_filters: or_clauses, limit: limit_value, offset: calculated_offset ) ).map { |item| resource_klass.new(item) } end # As with the `all` method, the `query methods` lazyly build a search/read # or search/count query for Fulfil. # By calling `count`, the prepared search/count will actually be executed and # the result will be fetched # @return [Integer] The count of records that match with the query in Fulfil def count return @count if defined?(@count) @count = client.put( "model/#{model_name}/search_count", Query.new( fields: nil, filters: where_clauses, alternative_filters: or_clauses ).to_h.except(:fields) ) end # The `each` method turns the `ApiResource` instance into an enumerable object. # For more information, see https://ruby-doc.org/core-3.0.2/Enumerable.html def each(&block) all.each(&block) end # Iterates over each page of results and yields it for processing. # # It fetches each page of results and yields it to the provided block for # processing. The loop continues until there are no more results to process. # # @example # ErpIntegration::SalesOrder.select(:id, :total_amount, :state).find_each do |orders| # orders.each do |order| # puts "#{order.id},$#{order.total_amount['decimal']},#{order.state}" # end # end # # => 5887403,$478.12,draft # => 5884252,$1497.03,draft # ... # => 5742565,$78.75,draft # # @yield [results] Yields the current page of results to the provided block # for processing. # @yieldparam results [Array] An array of results for the current page. # @return [nil] def find_each page = 1 while (results = clone.page(page)).any? yield results page += 1 end end private # Builds the relative resource path and adds the context if needed. # # @return [String] def api_resource_path base_path = "model/#{model_name}/search_read" return base_path unless context? "#{base_path}?context=#{context.to_json}" end end end end