restforce 5.1.1 → 5.2.3
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/.circleci/config.yml +8 -3
- data/CHANGELOG.md +20 -0
- data/Gemfile +2 -2
- data/README.md +49 -1
- data/lib/restforce/abstract_client.rb +1 -0
- data/lib/restforce/collection.rb +15 -1
- data/lib/restforce/concerns/base.rb +2 -2
- data/lib/restforce/concerns/composite_api.rb +104 -0
- data/lib/restforce/error_code.rb +9 -0
- data/lib/restforce/version.rb +1 -1
- data/lib/restforce.rb +2 -0
- data/restforce.gemspec +4 -3
- data/spec/unit/concerns/composite_api_spec.rb +143 -0
- data/spec/unit/concerns/streaming_spec.rb +1 -1
- metadata +12 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a41bb3b7f6f9c7fc06a71266e685e82afae4d6e291553159a6909ed29485553
|
4
|
+
data.tar.gz: b7e01b436eafbd7be80dc4210fce034ff8ae08b4920241c84999b252d34501d2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b465fc051f6199309d6bf7c18328b6765a8c08fb24daf28e978bd478602a2b6988dbd3578c3cc6562d2b5066666fdb9ddb64b4bd9a73fe9297d5dc3a073388ca
|
7
|
+
data.tar.gz: b1cc8939cb52563e39959bf2df3b27d91f64dba0fc7b5afd1d80fea47a4cdc55b5c921a9eaeef39ba97923037cd9ef0a873e3eed96dca0e50581f73d6ab66f06
|
data/.circleci/config.yml
CHANGED
@@ -34,23 +34,28 @@ references:
|
|
34
34
|
destination: test-results
|
35
35
|
|
36
36
|
jobs:
|
37
|
+
build-ruby31:
|
38
|
+
docker:
|
39
|
+
- image: cimg/ruby:3.1
|
40
|
+
steps: *steps
|
37
41
|
build-ruby30:
|
38
42
|
docker:
|
39
|
-
- image:
|
43
|
+
- image: cimg/ruby:3.0
|
40
44
|
steps: *steps
|
41
45
|
build-ruby27:
|
42
46
|
docker:
|
43
|
-
- image:
|
47
|
+
- image: cimg/ruby:2.7
|
44
48
|
steps: *steps
|
45
49
|
build-ruby26:
|
46
50
|
docker:
|
47
|
-
- image:
|
51
|
+
- image: cimg/ruby:2.6
|
48
52
|
steps: *steps
|
49
53
|
|
50
54
|
workflows:
|
51
55
|
version: 2
|
52
56
|
tests:
|
53
57
|
jobs:
|
58
|
+
- build-ruby31
|
54
59
|
- build-ruby30
|
55
60
|
- build-ruby27
|
56
61
|
- build-ruby26
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
# 5.2.3 (Jan 17, 2022)
|
2
|
+
|
3
|
+
* Add official support for Ruby 3.1 (@timrogers)
|
4
|
+
* Fix handling of responses from the Composite API (@robob27)
|
5
|
+
* Fix dependencies to correctly declare that the gem doesn't work with [faraday](https://github.com/lostisland/faraday) `v1.9.0` or later (@timrogers)
|
6
|
+
|
7
|
+
# 5.2.2 (Dec 16, 2021)
|
8
|
+
|
9
|
+
* Handle the `MALFORMED_SEARCH` error returned by Salesforce (@timrogers)
|
10
|
+
|
11
|
+
# 5.2.1 (Dec 8, 2021)
|
12
|
+
|
13
|
+
* Handle the `OPERATION_TOO_LARGE` error returned by Salesforce (@timrogers)
|
14
|
+
* Handle the `INVALID_SIGNUP_COUNTRY` error returned by Salesforce (@timrogers)
|
15
|
+
|
16
|
+
## 5.2.0 (Oct 15, 2021)
|
17
|
+
|
18
|
+
* Add support for Salesforce's Composite API and Composite Batch API (@meenie, @amacdougall)
|
19
|
+
* Improve the performance of counting numbers of query results with `Restforce::Collection#count`, avoiding unnecessary API requests (@jhass)
|
20
|
+
|
1
21
|
## 5.1.1 (Oct 13, 2021)
|
2
22
|
|
3
23
|
* Handle the `INVALID_REPLICATION_DATE` error returned by Salesforce (@michaelwnyc)
|
data/Gemfile
CHANGED
@@ -11,7 +11,7 @@ gem 'rake'
|
|
11
11
|
gem 'rspec', '~> 3.10.0'
|
12
12
|
gem 'rspec-collection_matchers', '~> 1.2.0'
|
13
13
|
gem 'rspec-its', '~> 1.3.0'
|
14
|
-
gem 'rspec_junit_formatter', '~> 0.
|
15
|
-
gem 'rubocop', '~> 1.
|
14
|
+
gem 'rspec_junit_formatter', '~> 0.5.1'
|
15
|
+
gem 'rubocop', '~> 1.24.1'
|
16
16
|
gem 'simplecov', '~> 0.21.2'
|
17
17
|
gem 'webmock', '~> 3.14.0'
|
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.3'
|
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?
|
@@ -61,9 +61,9 @@ module Restforce
|
|
61
61
|
def initialize(opts = {})
|
62
62
|
raise ArgumentError, 'Please specify a hash of options' unless opts.is_a?(Hash)
|
63
63
|
|
64
|
-
@options = Restforce.configuration.options.
|
64
|
+
@options = Restforce.configuration.options.to_h do |option|
|
65
65
|
[option, Restforce.configuration.send(option)]
|
66
|
-
end
|
66
|
+
end
|
67
67
|
|
68
68
|
@options.merge! opts
|
69
69
|
yield builder if block_given?
|
@@ -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/error_code.rb
CHANGED
@@ -245,6 +245,8 @@ module Restforce
|
|
245
245
|
|
246
246
|
class InvalidSessionId < ResponseError; end
|
247
247
|
|
248
|
+
class InvalidSignupCountry < ResponseError; end
|
249
|
+
|
248
250
|
class InvalidStatus < ResponseError; end
|
249
251
|
|
250
252
|
class InvalidType < ResponseError; end
|
@@ -275,6 +277,8 @@ module Restforce
|
|
275
277
|
|
276
278
|
class MalformedQuery < ResponseError; end
|
277
279
|
|
280
|
+
class MalformedSearch < ResponseError; end
|
281
|
+
|
278
282
|
class ManagerNotDefined < ResponseError; end
|
279
283
|
|
280
284
|
class MassmailRetryLimitExceeded < ResponseError; end
|
@@ -331,6 +335,8 @@ module Restforce
|
|
331
335
|
|
332
336
|
class OpWithInvalidUserTypeException < ResponseError; end
|
333
337
|
|
338
|
+
class OperationTooLarge < ResponseError; end
|
339
|
+
|
334
340
|
class OptedOutOfMassMail < ResponseError; end
|
335
341
|
|
336
342
|
class PackageLicenseRequired < ResponseError; end
|
@@ -531,6 +537,7 @@ module Restforce
|
|
531
537
|
"INVALID_REPLICATION_DATE" => InvalidReplicationDate,
|
532
538
|
"INVALID_SAVE_AS_ACTIVITY_FLAG" => InvalidSaveAsActivityFlag,
|
533
539
|
"INVALID_SESSION_ID" => InvalidSessionId,
|
540
|
+
"INVALID_SIGNUP_COUNTRY" => InvalidSignupCountry,
|
534
541
|
"INVALID_STATUS" => InvalidStatus,
|
535
542
|
"INVALID_TYPE" => InvalidType,
|
536
543
|
"INVALID_TYPE_FOR_OPERATION" => InvalidTypeForOperation,
|
@@ -546,6 +553,7 @@ module Restforce
|
|
546
553
|
"LOGIN_MUST_USE_SECURITY_TOKEN" => LoginMustUseSecurityToken,
|
547
554
|
"MALFORMED_ID" => MalformedId,
|
548
555
|
"MALFORMED_QUERY" => MalformedQuery,
|
556
|
+
"MALFORMED_SEARCH" => MalformedSearch,
|
549
557
|
"MANAGER_NOT_DEFINED" => ManagerNotDefined,
|
550
558
|
"MASSMAIL_RETRY_LIMIT_EXCEEDED" => MassmailRetryLimitExceeded,
|
551
559
|
"MASS_MAIL_LIMIT_EXCEEDED" => MassMailLimitExceeded,
|
@@ -574,6 +582,7 @@ module Restforce
|
|
574
582
|
"NUMBER_OUTSIDE_VALID_RANGE" => NumberOutsideValidRange,
|
575
583
|
"NUM_HISTORY_FIELDS_BY_SOBJECT_EXCEEDED" => NumHistoryFieldsBySobjectExceeded,
|
576
584
|
"OP_WITH_INVALID_USER_TYPE_EXCEPTION" => OpWithInvalidUserTypeException,
|
585
|
+
"OPERATION_TOO_LARGE" => OperationTooLarge,
|
577
586
|
"OPTED_OUT_OF_MASS_MAIL" => OptedOutOfMassMail,
|
578
587
|
"PACKAGE_LICENSE_REQUIRED" => PackageLicenseRequired,
|
579
588
|
"PLATFORM_EVENT_ENCRYPTION_ERROR" => PlatformEventEncryptionError,
|
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
|
data/restforce.gemspec
CHANGED
@@ -19,13 +19,14 @@ Gem::Specification.new do |gem|
|
|
19
19
|
|
20
20
|
gem.metadata = {
|
21
21
|
'source_code_uri' => 'https://github.com/restforce/restforce',
|
22
|
-
'changelog_uri' => 'https://github.com/restforce/restforce/blob/master/CHANGELOG.md'
|
22
|
+
'changelog_uri' => 'https://github.com/restforce/restforce/blob/master/CHANGELOG.md',
|
23
|
+
'rubygems_mfa_required' => 'true'
|
23
24
|
}
|
24
25
|
|
25
26
|
gem.required_ruby_version = '>= 2.6'
|
26
27
|
|
27
|
-
gem.add_dependency 'faraday', '
|
28
|
+
gem.add_dependency 'faraday', '< 1.9.0', '>= 0.9.0'
|
28
29
|
gem.add_dependency 'faraday_middleware', ['>= 0.8.8', '<= 2.0']
|
29
|
-
gem.add_dependency 'hashie', '>= 1.2.0', '<
|
30
|
+
gem.add_dependency 'hashie', '>= 1.2.0', '< 6.0'
|
30
31
|
gem.add_dependency 'jwt', ['>= 1.5.6']
|
31
32
|
end
|
@@ -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
|
@@ -87,7 +87,7 @@ describe Restforce::Concerns::Streaming, event_machine: true do
|
|
87
87
|
end
|
88
88
|
|
89
89
|
it 'connects to the streaming api' do
|
90
|
-
client.stub authenticate!:
|
90
|
+
client.stub authenticate!: double(access_token: 'secret2')
|
91
91
|
faye_double = double('Faye::Client')
|
92
92
|
Faye::Client.
|
93
93
|
should_receive(:new).
|
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.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tim Rogers
|
@@ -9,15 +9,15 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-01-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: faraday
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - "
|
18
|
+
- - "<"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version:
|
20
|
+
version: 1.9.0
|
21
21
|
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
23
|
version: 0.9.0
|
@@ -25,9 +25,9 @@ dependencies:
|
|
25
25
|
prerelease: false
|
26
26
|
version_requirements: !ruby/object:Gem::Requirement
|
27
27
|
requirements:
|
28
|
-
- - "
|
28
|
+
- - "<"
|
29
29
|
- !ruby/object:Gem::Version
|
30
|
-
version:
|
30
|
+
version: 1.9.0
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: 0.9.0
|
@@ -60,7 +60,7 @@ dependencies:
|
|
60
60
|
version: 1.2.0
|
61
61
|
- - "<"
|
62
62
|
- !ruby/object:Gem::Version
|
63
|
-
version: '
|
63
|
+
version: '6.0'
|
64
64
|
type: :runtime
|
65
65
|
prerelease: false
|
66
66
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -70,7 +70,7 @@ dependencies:
|
|
70
70
|
version: 1.2.0
|
71
71
|
- - "<"
|
72
72
|
- !ruby/object:Gem::Version
|
73
|
-
version: '
|
73
|
+
version: '6.0'
|
74
74
|
- !ruby/object:Gem::Dependency
|
75
75
|
name: jwt
|
76
76
|
requirement: !ruby/object:Gem::Requirement
|
@@ -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
|
@@ -236,6 +238,7 @@ licenses:
|
|
236
238
|
metadata:
|
237
239
|
source_code_uri: https://github.com/restforce/restforce
|
238
240
|
changelog_uri: https://github.com/restforce/restforce/blob/master/CHANGELOG.md
|
241
|
+
rubygems_mfa_required: 'true'
|
239
242
|
post_install_message:
|
240
243
|
rdoc_options: []
|
241
244
|
require_paths:
|
@@ -310,6 +313,7 @@ test_files:
|
|
310
313
|
- spec/unit/concerns/batch_api_spec.rb
|
311
314
|
- spec/unit/concerns/caching_spec.rb
|
312
315
|
- spec/unit/concerns/canvas_spec.rb
|
316
|
+
- spec/unit/concerns/composite_api_spec.rb
|
313
317
|
- spec/unit/concerns/connection_spec.rb
|
314
318
|
- spec/unit/concerns/streaming_spec.rb
|
315
319
|
- spec/unit/config_spec.rb
|