restforce 5.1.1 → 5.2.0
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.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +5 -0
 - data/README.md +49 -1
 - data/lib/restforce/abstract_client.rb +1 -0
 - data/lib/restforce/collection.rb +15 -1
 - data/lib/restforce/concerns/composite_api.rb +104 -0
 - data/lib/restforce/version.rb +1 -1
 - data/lib/restforce.rb +2 -0
 - data/spec/unit/concerns/composite_api_spec.rb +143 -0
 - metadata +5 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: f30c54c95fc530b6278faf751728b2793971c8aa0e43862f6719753a1d62d4fa
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: c7ac3a654170e2cb883325b12f4e6315d0c0423f9673142c93c87cf5de2003b8
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 4a731ebfe6e755b66479f8e85434a017d50a81c084f048e1454f3714bb047c36f8362976c904a91890b8f62099a3238689fe93f200468670b8f4dafc51cb5aa5
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: f5e8cd87fc4eccd486828627dfa1c769107e5869f7cf60088d5c7ee2cde2207d9bf33794a4f1639b98cfccfee54734d583dcbdbea8d841442a7086ff00b9e315
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,3 +1,8 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ## 5.2.0 (Oct 15, 2021)
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            * Add support for Salesforce's Composite API and Composite Batch API (@meenie, @amacdougall)
         
     | 
| 
      
 4 
     | 
    
         
            +
            * Improve the performance of counting numbers of query results with `Restforce::Collection#count`, avoiding unnecessary API requests (@jhass)
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
       1 
6 
     | 
    
         
             
            ## 5.1.1 (Oct 13, 2021)
         
     | 
| 
       2 
7 
     | 
    
         | 
| 
       3 
8 
     | 
    
         
             
            * Handle the `INVALID_REPLICATION_DATE` error returned by Salesforce (@michaelwnyc)
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -12,6 +12,8 @@ Features include: 
     | 
|
| 
       12 
12 
     | 
    
         
             
            * Support for parent-to-child relationships.
         
     | 
| 
       13 
13 
     | 
    
         
             
            * Support for aggregate queries.
         
     | 
| 
       14 
14 
     | 
    
         
             
            * Support for the [Streaming API](#streaming)
         
     | 
| 
      
 15 
     | 
    
         
            +
            * Support for the [Composite API](#composite-api)
         
     | 
| 
      
 16 
     | 
    
         
            +
            * Support for the [Composite Batch API](#composite-batch-api)
         
     | 
| 
       15 
17 
     | 
    
         
             
            * Support for the GetUpdated API
         
     | 
| 
       16 
18 
     | 
    
         
             
            * Support for blob data types.
         
     | 
| 
       17 
19 
     | 
    
         
             
            * Support for GZIP compression.
         
     | 
| 
         @@ -25,7 +27,7 @@ Features include: 
     | 
|
| 
       25 
27 
     | 
    
         | 
| 
       26 
28 
     | 
    
         
             
            Add this line to your application's Gemfile:
         
     | 
| 
       27 
29 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
                gem 'restforce', '~> 5. 
     | 
| 
      
 30 
     | 
    
         
            +
                gem 'restforce', '~> 5.2.0'
         
     | 
| 
       29 
31 
     | 
    
         | 
| 
       30 
32 
     | 
    
         
             
            And then execute:
         
     | 
| 
       31 
33 
     | 
    
         | 
| 
         @@ -573,6 +575,52 @@ end 
     | 
|
| 
       573 
575 
     | 
    
         
             
            Boom, you're now receiving push notifications when Accounts are
         
     | 
| 
       574 
576 
     | 
    
         
             
            created/updated.
         
     | 
| 
       575 
577 
     | 
    
         | 
| 
      
 578 
     | 
    
         
            +
            #### Composite API
         
     | 
| 
      
 579 
     | 
    
         
            +
             
     | 
| 
      
 580 
     | 
    
         
            +
            Restforce supports the [Composite API](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_composite_composite.htm).
         
     | 
| 
      
 581 
     | 
    
         
            +
            This feature permits the user to send a composite object—that is, a complex
         
     | 
| 
      
 582 
     | 
    
         
            +
            object with nested children—in a single API call. Up to 25 requests may be
         
     | 
| 
      
 583 
     | 
    
         
            +
            included in a single composite.
         
     | 
| 
      
 584 
     | 
    
         
            +
             
     | 
| 
      
 585 
     | 
    
         
            +
            Note that `GET` is not yet implemented for this API.
         
     | 
| 
      
 586 
     | 
    
         
            +
             
     | 
| 
      
 587 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 588 
     | 
    
         
            +
            # build up an array of requests:
         
     | 
| 
      
 589 
     | 
    
         
            +
            requests << {
         
     | 
| 
      
 590 
     | 
    
         
            +
              method: :update,
         
     | 
| 
      
 591 
     | 
    
         
            +
              sobject: sobject, # e.g. "Contact"
         
     | 
| 
      
 592 
     | 
    
         
            +
              reference_id: reference_id,
         
     | 
| 
      
 593 
     | 
    
         
            +
              data: data
         
     | 
| 
      
 594 
     | 
    
         
            +
            }
         
     | 
| 
      
 595 
     | 
    
         
            +
             
     | 
| 
      
 596 
     | 
    
         
            +
            # send every 25 requests as a subrequest in a single composite call
         
     | 
| 
      
 597 
     | 
    
         
            +
            requests.each_slice(25).map do |req_slice|
         
     | 
| 
      
 598 
     | 
    
         
            +
              client.composite do |subrequest|
         
     | 
| 
      
 599 
     | 
    
         
            +
                req_slice.each do |r|
         
     | 
| 
      
 600 
     | 
    
         
            +
                  subrequest.send *r.values
         
     | 
| 
      
 601 
     | 
    
         
            +
                end
         
     | 
| 
      
 602 
     | 
    
         
            +
              end
         
     | 
| 
      
 603 
     | 
    
         
            +
            end
         
     | 
| 
      
 604 
     | 
    
         
            +
             
     | 
| 
      
 605 
     | 
    
         
            +
            # note that we're using `map` to return an array of each responses to each
         
     | 
| 
      
 606 
     | 
    
         
            +
            # composite call; 100 requests will produce 4 responses
         
     | 
| 
      
 607 
     | 
    
         
            +
            ```
         
     | 
| 
      
 608 
     | 
    
         
            +
             
     | 
| 
      
 609 
     | 
    
         
            +
            #### Composite Batch API
         
     | 
| 
      
 610 
     | 
    
         
            +
             
     | 
| 
      
 611 
     | 
    
         
            +
            Restforce supports the [Composite Batch API](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_composite_batch.htm).
         
     | 
| 
      
 612 
     | 
    
         
            +
            This feature permits up to 25 subrequests in a single request, though each
         
     | 
| 
      
 613 
     | 
    
         
            +
            subrequest counts against the API limit. On the other hand, it has fewer
         
     | 
| 
      
 614 
     | 
    
         
            +
            limitations than the Composite API.
         
     | 
| 
      
 615 
     | 
    
         
            +
             
     | 
| 
      
 616 
     | 
    
         
            +
            ```
         
     | 
| 
      
 617 
     | 
    
         
            +
            client.batch do |subrequests|
         
     | 
| 
      
 618 
     | 
    
         
            +
              subrequests.create('Object', name: 'test')
         
     | 
| 
      
 619 
     | 
    
         
            +
              subrequests.update('Object', id: '123', name: 'test')
         
     | 
| 
      
 620 
     | 
    
         
            +
              subrequests.destroy('Object', '123')
         
     | 
| 
      
 621 
     | 
    
         
            +
            end
         
     | 
| 
      
 622 
     | 
    
         
            +
            ```
         
     | 
| 
      
 623 
     | 
    
         
            +
             
     | 
| 
       576 
624 
     | 
    
         
             
            #### Replaying Events
         
     | 
| 
       577 
625 
     | 
    
         | 
| 
       578 
626 
     | 
    
         
             
            Since API version 37.0, Salesforce stores events for 24 hours and they can be
         
     | 
    
        data/lib/restforce/collection.rb
    CHANGED
    
    | 
         @@ -27,12 +27,26 @@ module Restforce 
     | 
|
| 
       27 
27 
     | 
    
         
             
                  @raw_page['records'].size
         
     | 
| 
       28 
28 
     | 
    
         
             
                end
         
     | 
| 
       29 
29 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
                # Return the  
     | 
| 
      
 30 
     | 
    
         
            +
                # Return the number of items in the Collection without making any additional
         
     | 
| 
      
 31 
     | 
    
         
            +
                # requests and going through all of the pages of results, one by one. Instead,
         
     | 
| 
      
 32 
     | 
    
         
            +
                # we can rely on the total count of results which Salesforce returns.
         
     | 
| 
       31 
33 
     | 
    
         
             
                def size
         
     | 
| 
       32 
34 
     | 
    
         
             
                  @raw_page['totalSize']
         
     | 
| 
       33 
35 
     | 
    
         
             
                end
         
     | 
| 
       34 
36 
     | 
    
         
             
                alias length size
         
     | 
| 
       35 
37 
     | 
    
         | 
| 
      
 38 
     | 
    
         
            +
                def count(*args)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  # By default, `Enumerable`'s `#count` uses `#each`, which means going through all
         
     | 
| 
      
 40 
     | 
    
         
            +
                  # of the pages of results, one by one. Instead, we can use `#size` which we have
         
     | 
| 
      
 41 
     | 
    
         
            +
                  # already overridden to work in a smarter, more efficient way. This only works for
         
     | 
| 
      
 42 
     | 
    
         
            +
                  # the simple version of `#count` with no arguments. When called with an argument or
         
     | 
| 
      
 43 
     | 
    
         
            +
                  # a block, you need to know what the items in the collection actually are, so we
         
     | 
| 
      
 44 
     | 
    
         
            +
                  # call `super` and end up iterating through each item in the collection.
         
     | 
| 
      
 45 
     | 
    
         
            +
                  return size unless block_given? || !args.empty?
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  super
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
       36 
50 
     | 
    
         
             
                # Returns true if the size of the Collection is zero.
         
     | 
| 
       37 
51 
     | 
    
         
             
                def empty?
         
     | 
| 
       38 
52 
     | 
    
         
             
                  size.zero?
         
     | 
| 
         @@ -0,0 +1,104 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'restforce/concerns/verbs'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module Restforce
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Concerns
         
     | 
| 
      
 7 
     | 
    
         
            +
                module CompositeAPI
         
     | 
| 
      
 8 
     | 
    
         
            +
                  extend Restforce::Concerns::Verbs
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  define_verbs :post
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def composite(all_or_none: false, collate_subrequests: false)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    subrequests = Subrequests.new(options)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    yield(subrequests)
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    if subrequests.requests.length > 25
         
     | 
| 
      
 17 
     | 
    
         
            +
                      raise ArgumentError, 'Cannot have more than 25 subrequests.'
         
     | 
| 
      
 18 
     | 
    
         
            +
                    end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                    properties = {
         
     | 
| 
      
 21 
     | 
    
         
            +
                      compositeRequest: subrequests.requests,
         
     | 
| 
      
 22 
     | 
    
         
            +
                      allOrNone: all_or_none,
         
     | 
| 
      
 23 
     | 
    
         
            +
                      collateSubrequests: collate_subrequests
         
     | 
| 
      
 24 
     | 
    
         
            +
                    }
         
     | 
| 
      
 25 
     | 
    
         
            +
                    response = api_post('composite', properties.to_json)
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    results = response.body['CompositeResponse']
         
     | 
| 
      
 28 
     | 
    
         
            +
                    has_errors = results.any? { |result| result['HttpStatusCode'].digits.last == 4 }
         
     | 
| 
      
 29 
     | 
    
         
            +
                    if all_or_none && has_errors
         
     | 
| 
      
 30 
     | 
    
         
            +
                      last_error_index = results.rindex { |result| result['HttpStatusCode'] != 412 }
         
     | 
| 
      
 31 
     | 
    
         
            +
                      last_error = results[last_error_index]
         
     | 
| 
      
 32 
     | 
    
         
            +
                      raise CompositeAPIError, last_error['Body'][0]['errorCode']
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    results
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  def composite!(collate_subrequests: false, &block)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    composite(all_or_none: true, collate_subrequests: collate_subrequests, &block)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  class Subrequests
         
     | 
| 
      
 43 
     | 
    
         
            +
                    def initialize(options)
         
     | 
| 
      
 44 
     | 
    
         
            +
                      @options = options
         
     | 
| 
      
 45 
     | 
    
         
            +
                      @requests = []
         
     | 
| 
      
 46 
     | 
    
         
            +
                    end
         
     | 
| 
      
 47 
     | 
    
         
            +
                    attr_reader :options, :requests
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    def create(sobject, reference_id, attrs)
         
     | 
| 
      
 50 
     | 
    
         
            +
                      requests << {
         
     | 
| 
      
 51 
     | 
    
         
            +
                        method: 'POST',
         
     | 
| 
      
 52 
     | 
    
         
            +
                        url: composite_api_path(sobject),
         
     | 
| 
      
 53 
     | 
    
         
            +
                        body: attrs,
         
     | 
| 
      
 54 
     | 
    
         
            +
                        referenceId: reference_id
         
     | 
| 
      
 55 
     | 
    
         
            +
                      }
         
     | 
| 
      
 56 
     | 
    
         
            +
                    end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    def update(sobject, reference_id, attrs)
         
     | 
| 
      
 59 
     | 
    
         
            +
                      id = attrs.fetch(attrs.keys.find { |k, _v| k.to_s.casecmp?('id') }, nil)
         
     | 
| 
      
 60 
     | 
    
         
            +
                      raise ArgumentError, 'Id field missing from attrs.' unless id
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                      attrs_without_id = attrs.reject { |k, _v| k.to_s.casecmp?('id') }
         
     | 
| 
      
 63 
     | 
    
         
            +
                      requests << {
         
     | 
| 
      
 64 
     | 
    
         
            +
                        method: 'PATCH',
         
     | 
| 
      
 65 
     | 
    
         
            +
                        url: composite_api_path("#{sobject}/#{id}"),
         
     | 
| 
      
 66 
     | 
    
         
            +
                        body: attrs_without_id,
         
     | 
| 
      
 67 
     | 
    
         
            +
                        referenceId: reference_id
         
     | 
| 
      
 68 
     | 
    
         
            +
                      }
         
     | 
| 
      
 69 
     | 
    
         
            +
                    end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                    def destroy(sobject, reference_id, id)
         
     | 
| 
      
 72 
     | 
    
         
            +
                      requests << {
         
     | 
| 
      
 73 
     | 
    
         
            +
                        method: 'DELETE',
         
     | 
| 
      
 74 
     | 
    
         
            +
                        url: composite_api_path("#{sobject}/#{id}"),
         
     | 
| 
      
 75 
     | 
    
         
            +
                        referenceId: reference_id
         
     | 
| 
      
 76 
     | 
    
         
            +
                      }
         
     | 
| 
      
 77 
     | 
    
         
            +
                    end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                    def upsert(sobject, reference_id, ext_field, attrs)
         
     | 
| 
      
 80 
     | 
    
         
            +
                      raise ArgumentError, 'External id field missing.' unless ext_field
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                      ext_id = attrs.fetch(attrs.keys.find do |k, _v|
         
     | 
| 
      
 83 
     | 
    
         
            +
                        k.to_s.casecmp?(ext_field.to_s)
         
     | 
| 
      
 84 
     | 
    
         
            +
                      end, nil)
         
     | 
| 
      
 85 
     | 
    
         
            +
                      raise ArgumentError, 'External id missing from attrs.' unless ext_id
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                      attrs_without_ext_id = attrs.reject { |k, _v| k.to_s.casecmp?(ext_field) }
         
     | 
| 
      
 88 
     | 
    
         
            +
                      requests << {
         
     | 
| 
      
 89 
     | 
    
         
            +
                        method: 'PATCH',
         
     | 
| 
      
 90 
     | 
    
         
            +
                        url: composite_api_path("#{sobject}/#{ext_field}/#{ext_id}"),
         
     | 
| 
      
 91 
     | 
    
         
            +
                        body: attrs_without_ext_id,
         
     | 
| 
      
 92 
     | 
    
         
            +
                        referenceId: reference_id
         
     | 
| 
      
 93 
     | 
    
         
            +
                      }
         
     | 
| 
      
 94 
     | 
    
         
            +
                    end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                    private
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                    def composite_api_path(path)
         
     | 
| 
      
 99 
     | 
    
         
            +
                      "/services/data/v#{options[:api_version]}/sobjects/#{path}"
         
     | 
| 
      
 100 
     | 
    
         
            +
                    end
         
     | 
| 
      
 101 
     | 
    
         
            +
                  end
         
     | 
| 
      
 102 
     | 
    
         
            +
                end
         
     | 
| 
      
 103 
     | 
    
         
            +
              end
         
     | 
| 
      
 104 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/restforce/version.rb
    CHANGED
    
    
    
        data/lib/restforce.rb
    CHANGED
    
    | 
         @@ -32,6 +32,7 @@ module Restforce 
     | 
|
| 
       32 
32 
     | 
    
         
             
                autoload :Base,           'restforce/concerns/base'
         
     | 
| 
       33 
33 
     | 
    
         
             
                autoload :API,            'restforce/concerns/api'
         
     | 
| 
       34 
34 
     | 
    
         
             
                autoload :BatchAPI,       'restforce/concerns/batch_api'
         
     | 
| 
      
 35 
     | 
    
         
            +
                autoload :CompositeAPI,   'restforce/concerns/composite_api'
         
     | 
| 
       35 
36 
     | 
    
         
             
              end
         
     | 
| 
       36 
37 
     | 
    
         | 
| 
       37 
38 
     | 
    
         
             
              module Data
         
     | 
| 
         @@ -48,6 +49,7 @@ module Restforce 
     | 
|
| 
       48 
49 
     | 
    
         
             
              UnauthorizedError   = Class.new(Faraday::ClientError)
         
     | 
| 
       49 
50 
     | 
    
         
             
              APIVersionError     = Class.new(Error)
         
     | 
| 
       50 
51 
     | 
    
         
             
              BatchAPIError       = Class.new(Error)
         
     | 
| 
      
 52 
     | 
    
         
            +
              CompositeAPIError   = Class.new(Error)
         
     | 
| 
       51 
53 
     | 
    
         | 
| 
       52 
54 
     | 
    
         
             
              # Inherit from Faraday::ResourceNotFound for backwards-compatibility
         
     | 
| 
       53 
55 
     | 
    
         
             
              # Consumers of this library that rescue and handle Faraday::ResourceNotFound
         
     | 
| 
         @@ -0,0 +1,143 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            describe Restforce::Concerns::CompositeAPI do
         
     | 
| 
      
 6 
     | 
    
         
            +
              let(:endpoint) { 'composite' }
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              before do
         
     | 
| 
      
 9 
     | 
    
         
            +
                client.should_receive(:options).and_return(api_version: 38.0)
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              shared_examples_for 'composite requests' do
         
     | 
| 
      
 13 
     | 
    
         
            +
                it '#create' do
         
     | 
| 
      
 14 
     | 
    
         
            +
                  client.
         
     | 
| 
      
 15 
     | 
    
         
            +
                    should_receive(:api_post).
         
     | 
| 
      
 16 
     | 
    
         
            +
                    with(endpoint, { compositeRequest: [
         
     | 
| 
      
 17 
     | 
    
         
            +
                      {
         
     | 
| 
      
 18 
     | 
    
         
            +
                        method: 'POST',
         
     | 
| 
      
 19 
     | 
    
         
            +
                        url: '/services/data/v38.0/sobjects/Object',
         
     | 
| 
      
 20 
     | 
    
         
            +
                        body: { name: 'test' },
         
     | 
| 
      
 21 
     | 
    
         
            +
                        referenceId: 'create_ref'
         
     | 
| 
      
 22 
     | 
    
         
            +
                      }
         
     | 
| 
      
 23 
     | 
    
         
            +
                    ], allOrNone: all_or_none, collateSubrequests: false }.to_json).
         
     | 
| 
      
 24 
     | 
    
         
            +
                    and_return(response)
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  client.send(method) do |subrequests|
         
     | 
| 
      
 27 
     | 
    
         
            +
                    subrequests.create('Object', 'create_ref', name: 'test')
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                it '#update' do
         
     | 
| 
      
 32 
     | 
    
         
            +
                  client.
         
     | 
| 
      
 33 
     | 
    
         
            +
                    should_receive(:api_post).
         
     | 
| 
      
 34 
     | 
    
         
            +
                    with(endpoint, { compositeRequest: [
         
     | 
| 
      
 35 
     | 
    
         
            +
                      {
         
     | 
| 
      
 36 
     | 
    
         
            +
                        method: 'PATCH',
         
     | 
| 
      
 37 
     | 
    
         
            +
                        url: '/services/data/v38.0/sobjects/Object/123',
         
     | 
| 
      
 38 
     | 
    
         
            +
                        body: { name: 'test' },
         
     | 
| 
      
 39 
     | 
    
         
            +
                        referenceId: 'update_ref'
         
     | 
| 
      
 40 
     | 
    
         
            +
                      }
         
     | 
| 
      
 41 
     | 
    
         
            +
                    ], allOrNone: all_or_none, collateSubrequests: false }.to_json).
         
     | 
| 
      
 42 
     | 
    
         
            +
                    and_return(response)
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  client.send(method) do |subrequests|
         
     | 
| 
      
 45 
     | 
    
         
            +
                    subrequests.update('Object', 'update_ref', id: '123', name: 'test')
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                it '#destroy' do
         
     | 
| 
      
 50 
     | 
    
         
            +
                  client.
         
     | 
| 
      
 51 
     | 
    
         
            +
                    should_receive(:api_post).
         
     | 
| 
      
 52 
     | 
    
         
            +
                    with(endpoint, { compositeRequest: [
         
     | 
| 
      
 53 
     | 
    
         
            +
                      {
         
     | 
| 
      
 54 
     | 
    
         
            +
                        method: 'DELETE',
         
     | 
| 
      
 55 
     | 
    
         
            +
                        url: '/services/data/v38.0/sobjects/Object/123',
         
     | 
| 
      
 56 
     | 
    
         
            +
                        referenceId: 'destroy_ref'
         
     | 
| 
      
 57 
     | 
    
         
            +
                      }
         
     | 
| 
      
 58 
     | 
    
         
            +
                    ], allOrNone: all_or_none, collateSubrequests: false }.to_json).
         
     | 
| 
      
 59 
     | 
    
         
            +
                    and_return(response)
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  client.send(method) do |subrequests|
         
     | 
| 
      
 62 
     | 
    
         
            +
                    subrequests.destroy('Object', 'destroy_ref', '123')
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                it '#upsert' do
         
     | 
| 
      
 67 
     | 
    
         
            +
                  client.
         
     | 
| 
      
 68 
     | 
    
         
            +
                    should_receive(:api_post).
         
     | 
| 
      
 69 
     | 
    
         
            +
                    with(endpoint, { compositeRequest: [
         
     | 
| 
      
 70 
     | 
    
         
            +
                      {
         
     | 
| 
      
 71 
     | 
    
         
            +
                        method: 'PATCH',
         
     | 
| 
      
 72 
     | 
    
         
            +
                        url: '/services/data/v38.0/sobjects/Object/extIdField__c/456',
         
     | 
| 
      
 73 
     | 
    
         
            +
                        body: { name: 'test' },
         
     | 
| 
      
 74 
     | 
    
         
            +
                        referenceId: 'upsert_ref'
         
     | 
| 
      
 75 
     | 
    
         
            +
                      }
         
     | 
| 
      
 76 
     | 
    
         
            +
                    ], allOrNone: all_or_none, collateSubrequests: false }.to_json).
         
     | 
| 
      
 77 
     | 
    
         
            +
                    and_return(response)
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                  client.send(method) do |subrequests|
         
     | 
| 
      
 80 
     | 
    
         
            +
                    subrequests.upsert('Object', 'upsert_ref', 'extIdField__c',
         
     | 
| 
      
 81 
     | 
    
         
            +
                                       extIdField__c: '456', name: 'test')
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
                end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                it 'multiple subrequests' do
         
     | 
| 
      
 86 
     | 
    
         
            +
                  client.
         
     | 
| 
      
 87 
     | 
    
         
            +
                    should_receive(:api_post).
         
     | 
| 
      
 88 
     | 
    
         
            +
                    with(endpoint, { compositeRequest: [
         
     | 
| 
      
 89 
     | 
    
         
            +
                      {
         
     | 
| 
      
 90 
     | 
    
         
            +
                        method: 'POST',
         
     | 
| 
      
 91 
     | 
    
         
            +
                        url: '/services/data/v38.0/sobjects/Object',
         
     | 
| 
      
 92 
     | 
    
         
            +
                        body: { name: 'test' },
         
     | 
| 
      
 93 
     | 
    
         
            +
                        referenceId: 'create_ref'
         
     | 
| 
      
 94 
     | 
    
         
            +
                      },
         
     | 
| 
      
 95 
     | 
    
         
            +
                      {
         
     | 
| 
      
 96 
     | 
    
         
            +
                        method: 'PATCH',
         
     | 
| 
      
 97 
     | 
    
         
            +
                        url: '/services/data/v38.0/sobjects/Object/123',
         
     | 
| 
      
 98 
     | 
    
         
            +
                        body: { name: 'test' },
         
     | 
| 
      
 99 
     | 
    
         
            +
                        referenceId: 'update_ref'
         
     | 
| 
      
 100 
     | 
    
         
            +
                      },
         
     | 
| 
      
 101 
     | 
    
         
            +
                      {
         
     | 
| 
      
 102 
     | 
    
         
            +
                        method: 'DELETE',
         
     | 
| 
      
 103 
     | 
    
         
            +
                        url: '/services/data/v38.0/sobjects/Object/123',
         
     | 
| 
      
 104 
     | 
    
         
            +
                        referenceId: 'destroy_ref'
         
     | 
| 
      
 105 
     | 
    
         
            +
                      }
         
     | 
| 
      
 106 
     | 
    
         
            +
                    ], allOrNone: all_or_none, collateSubrequests: false }.to_json).
         
     | 
| 
      
 107 
     | 
    
         
            +
                    and_return(response)
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                  client.send(method) do |subrequests|
         
     | 
| 
      
 110 
     | 
    
         
            +
                    subrequests.create('Object', 'create_ref', name: 'test')
         
     | 
| 
      
 111 
     | 
    
         
            +
                    subrequests.update('Object', 'update_ref', id: '123', name: 'test')
         
     | 
| 
      
 112 
     | 
    
         
            +
                    subrequests.destroy('Object', 'destroy_ref', '123')
         
     | 
| 
      
 113 
     | 
    
         
            +
                  end
         
     | 
| 
      
 114 
     | 
    
         
            +
                end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                it 'fails if more than 25 requests' do
         
     | 
| 
      
 117 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 118 
     | 
    
         
            +
                    client.send(method) do |subrequests|
         
     | 
| 
      
 119 
     | 
    
         
            +
                      26.times do |i|
         
     | 
| 
      
 120 
     | 
    
         
            +
                        subrequests.upsert('Object', "upsert_ref_#{i}", 'extIdField__c',
         
     | 
| 
      
 121 
     | 
    
         
            +
                                           extIdField__c: '456', name: 'test')
         
     | 
| 
      
 122 
     | 
    
         
            +
                      end
         
     | 
| 
      
 123 
     | 
    
         
            +
                    end
         
     | 
| 
      
 124 
     | 
    
         
            +
                  end.to raise_error(ArgumentError)
         
     | 
| 
      
 125 
     | 
    
         
            +
                end
         
     | 
| 
      
 126 
     | 
    
         
            +
              end
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
              describe '#composite' do
         
     | 
| 
      
 129 
     | 
    
         
            +
                let(:method) { :composite }
         
     | 
| 
      
 130 
     | 
    
         
            +
                let(:all_or_none) { false }
         
     | 
| 
      
 131 
     | 
    
         
            +
                let(:response) { double('Faraday::Response', body: { 'CompositeResponse' => [] }) }
         
     | 
| 
      
 132 
     | 
    
         
            +
                it_behaves_like 'composite requests'
         
     | 
| 
      
 133 
     | 
    
         
            +
              end
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
              describe '#composite!' do
         
     | 
| 
      
 136 
     | 
    
         
            +
                let(:method) { :composite! }
         
     | 
| 
      
 137 
     | 
    
         
            +
                let(:all_or_none) { true }
         
     | 
| 
      
 138 
     | 
    
         
            +
                let(:response) do
         
     | 
| 
      
 139 
     | 
    
         
            +
                  double('Faraday::Response', body: { 'CompositeResponse' => [] })
         
     | 
| 
      
 140 
     | 
    
         
            +
                end
         
     | 
| 
      
 141 
     | 
    
         
            +
                it_behaves_like 'composite requests'
         
     | 
| 
      
 142 
     | 
    
         
            +
              end
         
     | 
| 
      
 143 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: restforce
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 5. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 5.2.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Tim Rogers
         
     | 
| 
         @@ -9,7 +9,7 @@ authors: 
     | 
|
| 
       9 
9 
     | 
    
         
             
            autorequire:
         
     | 
| 
       10 
10 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       11 
11 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       12 
     | 
    
         
            -
            date: 2021-10- 
     | 
| 
      
 12 
     | 
    
         
            +
            date: 2021-10-15 00:00:00.000000000 Z
         
     | 
| 
       13 
13 
     | 
    
         
             
            dependencies:
         
     | 
| 
       14 
14 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       15 
15 
     | 
    
         
             
              name: faraday
         
     | 
| 
         @@ -121,6 +121,7 @@ files: 
     | 
|
| 
       121 
121 
     | 
    
         
             
            - lib/restforce/concerns/batch_api.rb
         
     | 
| 
       122 
122 
     | 
    
         
             
            - lib/restforce/concerns/caching.rb
         
     | 
| 
       123 
123 
     | 
    
         
             
            - lib/restforce/concerns/canvas.rb
         
     | 
| 
      
 124 
     | 
    
         
            +
            - lib/restforce/concerns/composite_api.rb
         
     | 
| 
       124 
125 
     | 
    
         
             
            - lib/restforce/concerns/connection.rb
         
     | 
| 
       125 
126 
     | 
    
         
             
            - lib/restforce/concerns/picklists.rb
         
     | 
| 
       126 
127 
     | 
    
         
             
            - lib/restforce/concerns/streaming.rb
         
     | 
| 
         @@ -209,6 +210,7 @@ files: 
     | 
|
| 
       209 
210 
     | 
    
         
             
            - spec/unit/concerns/batch_api_spec.rb
         
     | 
| 
       210 
211 
     | 
    
         
             
            - spec/unit/concerns/caching_spec.rb
         
     | 
| 
       211 
212 
     | 
    
         
             
            - spec/unit/concerns/canvas_spec.rb
         
     | 
| 
      
 213 
     | 
    
         
            +
            - spec/unit/concerns/composite_api_spec.rb
         
     | 
| 
       212 
214 
     | 
    
         
             
            - spec/unit/concerns/connection_spec.rb
         
     | 
| 
       213 
215 
     | 
    
         
             
            - spec/unit/concerns/streaming_spec.rb
         
     | 
| 
       214 
216 
     | 
    
         
             
            - spec/unit/config_spec.rb
         
     | 
| 
         @@ -310,6 +312,7 @@ test_files: 
     | 
|
| 
       310 
312 
     | 
    
         
             
            - spec/unit/concerns/batch_api_spec.rb
         
     | 
| 
       311 
313 
     | 
    
         
             
            - spec/unit/concerns/caching_spec.rb
         
     | 
| 
       312 
314 
     | 
    
         
             
            - spec/unit/concerns/canvas_spec.rb
         
     | 
| 
      
 315 
     | 
    
         
            +
            - spec/unit/concerns/composite_api_spec.rb
         
     | 
| 
       313 
316 
     | 
    
         
             
            - spec/unit/concerns/connection_spec.rb
         
     | 
| 
       314 
317 
     | 
    
         
             
            - spec/unit/concerns/streaming_spec.rb
         
     | 
| 
       315 
318 
     | 
    
         
             
            - spec/unit/config_spec.rb
         
     |