restforce 5.1.1 → 5.2.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|