lib/restforce/concerns/batch_api.rb
# frozen_string_literal: true require 'restforce/concerns/verbs' module Restforce module Concerns module BatchAPI extend Restforce::Concerns::Verbs define_verbs :post def batch(halt_on_error: false) subrequests = Subrequests.new(options) yield(subrequests) subrequests.requests.each_slice(25).map do |requests| properties = { batchRequests: requests, haltOnError: halt_on_error } response = api_post('composite/batch', properties.to_json) body = response.body results = body['results'] if halt_on_error && body['hasErrors'] last_error_index = results.rindex { |result| result['statusCode'] != 412 } last_error = results[last_error_index] raise BatchAPIError, last_error['result'][0]['errorCode'] end results.map(&:compact) end.flatten end def batch!(&block) batch(halt_on_error: true, &block) end class Subrequests def initialize(options) @options = options @requests = [] end attr_reader :options, :requests def create(sobject, attrs) requests << { method: 'POST', url: batch_api_path(sobject), richInput: attrs } end def update(sobject, attrs) id = attrs.fetch(attrs.keys.find { |k, v| k.to_s.casecmp?('id') }, nil) raise ArgumentError, 'Id field missing from attrs.' unless id attrs_without_id = attrs.reject { |k, v| k.to_s.casecmp?('id') } requests << { method: 'PATCH', url: batch_api_path("#{sobject}/#{id}"), richInput: attrs_without_id } end def destroy(sobject, id) requests << { method: 'DELETE', url: batch_api_path("#{sobject}/#{id}") } end def upsert(sobject, ext_field, attrs) raise ArgumentError, 'External id field missing.' unless ext_field ext_id = attrs.fetch(attrs.keys.find { |k, v| k.to_s.casecmp?(ext_field.to_s) }, nil) raise ArgumentError, 'External id missing from attrs.' unless ext_id attrs_without_ext_id = attrs.reject { |k, v| k.to_s.casecmp?(ext_field) } requests << { method: 'PATCH', url: batch_api_path("#{sobject}/#{ext_field}/#{ext_id}"), richInput: attrs_without_ext_id } end private def batch_api_path(path) "v#{options[:api_version]}/sobjects/#{path}" end end end end end