restforce 5.1.0 → 5.2.2
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 +19 -0
- data/Gemfile +1 -1
- data/README.md +50 -2
- 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/concerns/picklists.rb +1 -1
- data/lib/restforce/error_code.rb +15 -0
- data/lib/restforce/version.rb +1 -1
- data/lib/restforce.rb +2 -0
- data/restforce.gemspec +3 -2
- data/spec/unit/concerns/composite_api_spec.rb +143 -0
- data/spec/unit/concerns/streaming_spec.rb +1 -1
- metadata +9 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc09f4586a150366a59119a46d6ccee41a72f6b63efa96224e7fe9bb31a65574
|
4
|
+
data.tar.gz: 71c415929a9ecb427cf528fbb1ac57630cb503cdad2ecd2b17769f1000b174dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac1bd7610aec45c963fad5feac34f0f4bc0bd847bbd80bc4b2ad7895cf7a0ee35d68904419ffb5ea2b74d205a7897a06558e7661d1e03c9ed00644438047b87b
|
7
|
+
data.tar.gz: c3107f9a4a125e1fb89f107477eab41379a4347fce3689204e1d2dd3817a7cf0c465cfabdb25cf01246cfe428719147b7ccbba73b330d4211856712fb744a6a0
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
# 5.2.2 (Dec 16, 2021)
|
2
|
+
|
3
|
+
* Handle the `MALFORMED_SEARCH` error returned by Salesforce (@timrogers)
|
4
|
+
|
5
|
+
# 5.2.1 (Dec 8, 2021)
|
6
|
+
|
7
|
+
* Handle the `OPERATION_TOO_LARGE` error returned by Salesforce (@timrogers)
|
8
|
+
* Handle the `INVALID_SIGNUP_COUNTRY` error returned by Salesforce (@timrogers)
|
9
|
+
|
10
|
+
## 5.2.0 (Oct 15, 2021)
|
11
|
+
|
12
|
+
* Add support for Salesforce's Composite API and Composite Batch API (@meenie, @amacdougall)
|
13
|
+
* Improve the performance of counting numbers of query results with `Restforce::Collection#count`, avoiding unnecessary API requests (@jhass)
|
14
|
+
|
15
|
+
## 5.1.1 (Oct 13, 2021)
|
16
|
+
|
17
|
+
* Handle the `INVALID_REPLICATION_DATE` error returned by Salesforce (@michaelwnyc)
|
18
|
+
* Handle the `BIG_OBJECT_UNSUPPORTED_OPERATION` error returned by Salesforce (@remon)
|
19
|
+
|
1
20
|
## 5.1.0 (Aug 26, 2021)
|
2
21
|
|
3
22
|
* Add official support for Ruby 3.0 (@timrogers)
|
data/Gemfile
CHANGED
@@ -12,6 +12,6 @@ gem 'rspec', '~> 3.10.0'
|
|
12
12
|
gem 'rspec-collection_matchers', '~> 1.2.0'
|
13
13
|
gem 'rspec-its', '~> 1.3.0'
|
14
14
|
gem 'rspec_junit_formatter', '~> 0.4.1'
|
15
|
-
gem 'rubocop', '~> 1.
|
15
|
+
gem 'rubocop', '~> 1.23.0'
|
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.2'
|
29
31
|
|
30
32
|
And then execute:
|
31
33
|
|
@@ -35,7 +37,7 @@ Or install it yourself as:
|
|
35
37
|
|
36
38
|
$ gem install restforce
|
37
39
|
|
38
|
-
__As of version 5.
|
40
|
+
__As of version 5.1.0, this gem is only compatible with Ruby 2.6.0 and later.__ If you're using an earlier Ruby version:
|
39
41
|
|
40
42
|
* for Ruby 2.5, use version 5.0.6 or earlier
|
41
43
|
* for Ruby 2.4, use version 4.3.0 or earlier
|
@@ -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
|
@@ -85,7 +85,7 @@ module Restforce
|
|
85
85
|
def valid?(picklist_entry)
|
86
86
|
valid_for = picklist_entry['validFor'].ljust(16, 'A').unpack1('m').
|
87
87
|
unpack('C*')
|
88
|
-
(valid_for[index >> 3] & (0x80 >> index % 8)).positive?
|
88
|
+
(valid_for[index >> 3] & (0x80 >> (index % 8))).positive?
|
89
89
|
end
|
90
90
|
end
|
91
91
|
end
|
data/lib/restforce/error_code.rb
CHANGED
@@ -31,6 +31,8 @@ module Restforce
|
|
31
31
|
|
32
32
|
class BccSelfNotAllowedIfBccComplianceEnabled < ResponseError; end
|
33
33
|
|
34
|
+
class BigObjectUnsupportedOperation < ResponseError; end
|
35
|
+
|
34
36
|
class CannotCascadeProductActive < ResponseError; end
|
35
37
|
|
36
38
|
class CannotChangeFieldTypeOfApexReferencedField < ResponseError; end
|
@@ -237,10 +239,14 @@ module Restforce
|
|
237
239
|
|
238
240
|
class InvalidReadOnlyUserDml < ResponseError; end
|
239
241
|
|
242
|
+
class InvalidReplicationDate < ResponseError; end
|
243
|
+
|
240
244
|
class InvalidSaveAsActivityFlag < ResponseError; end
|
241
245
|
|
242
246
|
class InvalidSessionId < ResponseError; end
|
243
247
|
|
248
|
+
class InvalidSignupCountry < ResponseError; end
|
249
|
+
|
244
250
|
class InvalidStatus < ResponseError; end
|
245
251
|
|
246
252
|
class InvalidType < ResponseError; end
|
@@ -271,6 +277,8 @@ module Restforce
|
|
271
277
|
|
272
278
|
class MalformedQuery < ResponseError; end
|
273
279
|
|
280
|
+
class MalformedSearch < ResponseError; end
|
281
|
+
|
274
282
|
class ManagerNotDefined < ResponseError; end
|
275
283
|
|
276
284
|
class MassmailRetryLimitExceeded < ResponseError; end
|
@@ -327,6 +335,8 @@ module Restforce
|
|
327
335
|
|
328
336
|
class OpWithInvalidUserTypeException < ResponseError; end
|
329
337
|
|
338
|
+
class OperationTooLarge < ResponseError; end
|
339
|
+
|
330
340
|
class OptedOutOfMassMail < ResponseError; end
|
331
341
|
|
332
342
|
class PackageLicenseRequired < ResponseError; end
|
@@ -413,6 +423,7 @@ module Restforce
|
|
413
423
|
BccNotAllowedIfBccComplianceEnabled,
|
414
424
|
"BCC_SELF_NOT_ALLOWED_IF_BCC_COMPLIANCE_ENABLED" =>
|
415
425
|
BccSelfNotAllowedIfBccComplianceEnabled,
|
426
|
+
"BIG_OBJECT_UNSUPPORTED_OPERATION" => BigObjectUnsupportedOperation,
|
416
427
|
"CANNOT_CASCADE_PRODUCT_ACTIVE" => CannotCascadeProductActive,
|
417
428
|
"CANNOT_CHANGE_FIELD_TYPE_OF_APEX_REFERENCED_FIELD" =>
|
418
429
|
CannotChangeFieldTypeOfApexReferencedField,
|
@@ -523,8 +534,10 @@ module Restforce
|
|
523
534
|
"INVALID_PARTNER_NETWORK_STATUS" => InvalidPartnerNetworkStatus,
|
524
535
|
"INVALID_PERSON_ACCOUNT_OPERATION" => InvalidPersonAccountOperation,
|
525
536
|
"INVALID_READ_ONLY_USER_DML" => InvalidReadOnlyUserDml,
|
537
|
+
"INVALID_REPLICATION_DATE" => InvalidReplicationDate,
|
526
538
|
"INVALID_SAVE_AS_ACTIVITY_FLAG" => InvalidSaveAsActivityFlag,
|
527
539
|
"INVALID_SESSION_ID" => InvalidSessionId,
|
540
|
+
"INVALID_SIGNUP_COUNTRY" => InvalidSignupCountry,
|
528
541
|
"INVALID_STATUS" => InvalidStatus,
|
529
542
|
"INVALID_TYPE" => InvalidType,
|
530
543
|
"INVALID_TYPE_FOR_OPERATION" => InvalidTypeForOperation,
|
@@ -540,6 +553,7 @@ module Restforce
|
|
540
553
|
"LOGIN_MUST_USE_SECURITY_TOKEN" => LoginMustUseSecurityToken,
|
541
554
|
"MALFORMED_ID" => MalformedId,
|
542
555
|
"MALFORMED_QUERY" => MalformedQuery,
|
556
|
+
"MALFORMED_SEARCH" => MalformedSearch,
|
543
557
|
"MANAGER_NOT_DEFINED" => ManagerNotDefined,
|
544
558
|
"MASSMAIL_RETRY_LIMIT_EXCEEDED" => MassmailRetryLimitExceeded,
|
545
559
|
"MASS_MAIL_LIMIT_EXCEEDED" => MassMailLimitExceeded,
|
@@ -568,6 +582,7 @@ module Restforce
|
|
568
582
|
"NUMBER_OUTSIDE_VALID_RANGE" => NumberOutsideValidRange,
|
569
583
|
"NUM_HISTORY_FIELDS_BY_SOBJECT_EXCEEDED" => NumHistoryFieldsBySobjectExceeded,
|
570
584
|
"OP_WITH_INVALID_USER_TYPE_EXCEPTION" => OpWithInvalidUserTypeException,
|
585
|
+
"OPERATION_TOO_LARGE" => OperationTooLarge,
|
571
586
|
"OPTED_OUT_OF_MASS_MAIL" => OptedOutOfMassMail,
|
572
587
|
"PACKAGE_LICENSE_REQUIRED" => PackageLicenseRequired,
|
573
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
28
|
gem.add_dependency 'faraday', '<= 2.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.2
|
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-
|
12
|
+
date: 2021-12-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: faraday
|
@@ -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:
|
@@ -251,7 +254,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
251
254
|
- !ruby/object:Gem::Version
|
252
255
|
version: '0'
|
253
256
|
requirements: []
|
254
|
-
rubygems_version: 3.
|
257
|
+
rubygems_version: 3.2.22
|
255
258
|
signing_key:
|
256
259
|
specification_version: 4
|
257
260
|
summary: A lightweight Ruby client for the Salesforce REST API
|
@@ -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
|