restforce 5.0.6 → 6.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +4 -13
- data/.github/funding.yml +1 -0
- data/.github/workflows/build.yml +23 -0
- data/.github/workflows/faraday.yml +27 -0
- data/.rubocop.yml +2 -2
- data/CHANGELOG.md +68 -0
- data/Gemfile +15 -6
- data/README.md +61 -7
- data/UPGRADING.md +29 -0
- data/lib/restforce/abstract_client.rb +1 -0
- data/lib/restforce/collection.rb +20 -2
- data/lib/restforce/concerns/api.rb +2 -1
- data/lib/restforce/concerns/base.rb +2 -2
- data/lib/restforce/concerns/composite_api.rb +104 -0
- data/lib/restforce/concerns/connection.rb +1 -1
- data/lib/restforce/concerns/picklists.rb +1 -1
- data/lib/restforce/config.rb +12 -10
- data/lib/restforce/error_code.rb +30 -9
- data/lib/restforce/file_part.rb +12 -4
- data/lib/restforce/middleware/authentication.rb +1 -0
- data/lib/restforce/middleware/caching.rb +140 -15
- data/lib/restforce/middleware/gzip.rb +4 -0
- data/lib/restforce/middleware/json_request.rb +90 -0
- data/lib/restforce/middleware/json_response.rb +85 -0
- data/lib/restforce/middleware/logger.rb +6 -2
- data/lib/restforce/middleware/raise_error.rb +10 -1
- data/lib/restforce/version.rb +1 -1
- data/lib/restforce.rb +11 -7
- data/restforce.gemspec +8 -16
- data/spec/fixtures/sobject/list_view_results_success_response.json +151 -0
- data/spec/integration/abstract_client_spec.rb +42 -30
- data/spec/integration/data/client_spec.rb +6 -2
- data/spec/spec_helper.rb +10 -0
- data/spec/support/client_integration.rb +7 -7
- data/spec/support/concerns.rb +1 -1
- data/spec/support/middleware.rb +1 -2
- data/spec/unit/collection_spec.rb +22 -4
- data/spec/unit/concerns/api_spec.rb +22 -15
- data/spec/unit/concerns/authentication_spec.rb +6 -6
- data/spec/unit/concerns/base_spec.rb +1 -1
- data/spec/unit/concerns/composite_api_spec.rb +169 -0
- data/spec/unit/concerns/connection_spec.rb +1 -1
- data/spec/unit/concerns/streaming_spec.rb +4 -4
- data/spec/unit/config_spec.rb +2 -2
- data/spec/unit/middleware/authentication/jwt_bearer_spec.rb +24 -8
- data/spec/unit/middleware/authentication/password_spec.rb +12 -4
- data/spec/unit/middleware/authentication/token_spec.rb +12 -4
- data/spec/unit/middleware/authentication_spec.rb +8 -8
- data/spec/unit/middleware/authorization_spec.rb +5 -1
- data/spec/unit/middleware/custom_headers_spec.rb +6 -2
- data/spec/unit/middleware/gzip_spec.rb +60 -16
- data/spec/unit/middleware/instance_url_spec.rb +2 -2
- data/spec/unit/middleware/logger_spec.rb +1 -1
- data/spec/unit/middleware/raise_error_spec.rb +20 -10
- data/spec/unit/sobject_spec.rb +9 -5
- metadata +55 -172
- data/.circleci/config.yml +0 -56
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b88661434383f16571c2b240a9c2482035058a34adadfe39a86b4fecb7382ca
|
4
|
+
data.tar.gz: bb4ac026f9b7fbd43f7b5237760e341d9d351c5433ca19009daa24794be251f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da66afd00a69c078e3883f76ae0039d2742911de7b0ca8cb6b14b15801695973a25deec7e71fede838d9193a60224510eee625b72b06c827d6f0467111757023
|
7
|
+
data.tar.gz: 86cad2e9fecc5610dae221bdec91a8a1868b9333d7aa552b5fb21af928352c73cad50ea64f63f26cd61ea50048784f5c3dad20b04d798c70aee2f0295c1721bd
|
data/.github/dependabot.yml
CHANGED
@@ -4,16 +4,7 @@ updates:
|
|
4
4
|
directory: "/"
|
5
5
|
schedule:
|
6
6
|
interval: daily
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
- 1.10.0
|
12
|
-
- 1.11.0
|
13
|
-
- 1.12.0
|
14
|
-
- 1.12.1
|
15
|
-
- 1.9.0
|
16
|
-
- dependency-name: webmock
|
17
|
-
versions:
|
18
|
-
- 3.12.0
|
19
|
-
- 3.12.1
|
7
|
+
- package-ecosystem: 'github-actions'
|
8
|
+
directory: '/'
|
9
|
+
schedule:
|
10
|
+
interval: 'daily'
|
data/.github/funding.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
github: [restforce]
|
@@ -0,0 +1,23 @@
|
|
1
|
+
name: 'Build'
|
2
|
+
on: push
|
3
|
+
jobs:
|
4
|
+
lint-and-test:
|
5
|
+
runs-on: ubuntu-latest
|
6
|
+
strategy:
|
7
|
+
fail-fast: false
|
8
|
+
matrix:
|
9
|
+
ruby_version: ['2.7', '3.0', '3.1', '3.2']
|
10
|
+
steps:
|
11
|
+
- name: Checkout code
|
12
|
+
uses: actions/checkout@v3
|
13
|
+
- name: Setup Ruby ${{ matrix.ruby_version }}
|
14
|
+
uses: ruby/setup-ruby@v1
|
15
|
+
with:
|
16
|
+
bundler-cache: true
|
17
|
+
ruby-version: ${{ matrix.ruby_version }}
|
18
|
+
- name: Install dependencies
|
19
|
+
run: bundle install
|
20
|
+
- name: Lint Ruby files
|
21
|
+
run: bundle exec rubocop
|
22
|
+
- name: Run RSpec tests
|
23
|
+
run: bundle exec rspec
|
@@ -0,0 +1,27 @@
|
|
1
|
+
name: 'Test against supported Faraday minor versions'
|
2
|
+
on: push
|
3
|
+
jobs:
|
4
|
+
lint-and-test:
|
5
|
+
runs-on: ubuntu-latest
|
6
|
+
strategy:
|
7
|
+
fail-fast: false
|
8
|
+
matrix:
|
9
|
+
# Normally, we only test the most recent patch version for any given minor version.
|
10
|
+
# For v2.0.x, we test v2.0.0 and v2.0.1 because v2.0.0 has a special behaviour where
|
11
|
+
# the Net::HTTP adapter is not included. See
|
12
|
+
# https://github.com/lostisland/faraday/blob/main/UPGRADING.md#faraday-20.
|
13
|
+
faraday_version: ['1.1.0', '1.2.0', '1.3.1', '1.4.1', '1.5.1', '1.6.0', '1.7.2', '1.8.0', '1.9.3', '1.10.0', '2.0.0', '2.0.1', '2.1.0', '2.2.0', '2.3.0', '2.4.0', '2.5.0', '2.6.0', '2.7.0']
|
14
|
+
env:
|
15
|
+
FARADAY_VERSION: ~> ${{ matrix.faraday_version }}
|
16
|
+
steps:
|
17
|
+
- name: Checkout code
|
18
|
+
uses: actions/checkout@v3
|
19
|
+
- name: Setup Ruby ${{ matrix.ruby_version }}
|
20
|
+
uses: ruby/setup-ruby@v1
|
21
|
+
with:
|
22
|
+
bundler-cache: true
|
23
|
+
ruby-version: 3.1
|
24
|
+
- name: Install dependencies
|
25
|
+
run: bundle install
|
26
|
+
- name: Run RSpec tests
|
27
|
+
run: bundle exec rspec
|
data/.rubocop.yml
CHANGED
@@ -8,7 +8,7 @@ AllCops:
|
|
8
8
|
- .*/**/*
|
9
9
|
- vendor/**/*
|
10
10
|
NewCops: enable
|
11
|
-
TargetRubyVersion: 2.
|
11
|
+
TargetRubyVersion: 2.7
|
12
12
|
|
13
13
|
# Limit lines to 90 characters.
|
14
14
|
Layout/LineLength:
|
@@ -71,4 +71,4 @@ Naming/FileName:
|
|
71
71
|
- Guardfile
|
72
72
|
|
73
73
|
Lint/UriEscapeUnescape:
|
74
|
-
Enabled: false
|
74
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,71 @@
|
|
1
|
+
# 6.2.1 (Jan 18 2023)
|
2
|
+
|
3
|
+
* When a response claims to be gzipped but isn't, return the body as it is and don't explode (@timrogers)
|
4
|
+
|
5
|
+
# 6.2.0 (Jan 18 2023)
|
6
|
+
|
7
|
+
* Add support for `faraday` v2.7.x (@timrogers)
|
8
|
+
* Expose the Faraday `Response` on `CompositeAPIError`s (@shravan097)
|
9
|
+
|
10
|
+
# 6.1.0 (Nov 18 2022)
|
11
|
+
|
12
|
+
* Add support for `faraday` v2.5.x and v2.6.x (@marcrohloff, @timrogers)
|
13
|
+
|
14
|
+
# 6.0.0 (Sep 9 2022)
|
15
|
+
|
16
|
+
__This version contains breaking changes. For help with upgrading, see [`UPGRADING.md`](https://github.com/restforce/restforce/blob/main/UPGRADING.md).__
|
17
|
+
|
18
|
+
* __⚠️ Drop support for Ruby 2.6__, since [Ruby 2.6 has reached its end-of-life](https://www.ruby-lang.org/en/downloads/) (@timrogers)
|
19
|
+
* __⚠️ Drop compatability with `faraday` versions before `v1.1.0`__ (@timrogers)
|
20
|
+
* Add support for `faraday` versions `v2.0.0` onwards (@timrogers)
|
21
|
+
|
22
|
+
*This version was also released as [`v6.0.0.rc.1`](https://github.com/restforce/restforce/releases/tag/v6.0.0.rc.1) on Aug 4 2022.*
|
23
|
+
|
24
|
+
# 5.3.1 (Jul 19 2022)
|
25
|
+
|
26
|
+
* Handle the `EXCEEDED_ID_LIMIT` error returned by the Salesforce API (@timrogers, @yashshah1)
|
27
|
+
|
28
|
+
# 5.3.0 (May 30, 2022)
|
29
|
+
|
30
|
+
* Add support for Faraday v1.9.x and v1.10.0 (@magni-, @timrogers)
|
31
|
+
* Follow redirects during authentication to support Lightning URLs (e.g. `*.lightning.force.com` instead of `*.my.salesforce.com`) (@nhocki)
|
32
|
+
|
33
|
+
# 5.2.4 (Mar 16, 2022)
|
34
|
+
|
35
|
+
* Fix `Restforce::Collection#size` for Salesforce APIs that use the `size` property to return the total number of results, instead of `totalSize` (@kwong-yw)
|
36
|
+
|
37
|
+
# 5.2.3 (Jan 17, 2022)
|
38
|
+
|
39
|
+
* Add official support for Ruby 3.1 (@timrogers)
|
40
|
+
* Fix handling of responses from the Composite API (@robob27)
|
41
|
+
* Fix dependencies to correctly declare that the gem doesn't work with [faraday](https://github.com/lostisland/faraday) `v1.9.0` or later (@timrogers)
|
42
|
+
|
43
|
+
# 5.2.2 (Dec 16, 2021)
|
44
|
+
|
45
|
+
* Handle the `MALFORMED_SEARCH` error returned by Salesforce (@timrogers)
|
46
|
+
|
47
|
+
# 5.2.1 (Dec 8, 2021)
|
48
|
+
|
49
|
+
* Handle the `OPERATION_TOO_LARGE` error returned by Salesforce (@timrogers)
|
50
|
+
* Handle the `INVALID_SIGNUP_COUNTRY` error returned by Salesforce (@timrogers)
|
51
|
+
|
52
|
+
## 5.2.0 (Oct 15, 2021)
|
53
|
+
|
54
|
+
* Add support for Salesforce's Composite API and Composite Batch API (@meenie, @amacdougall)
|
55
|
+
* Improve the performance of counting numbers of query results with `Restforce::Collection#count`, avoiding unnecessary API requests (@jhass)
|
56
|
+
|
57
|
+
## 5.1.1 (Oct 13, 2021)
|
58
|
+
|
59
|
+
* Handle the `INVALID_REPLICATION_DATE` error returned by Salesforce (@michaelwnyc)
|
60
|
+
* Handle the `BIG_OBJECT_UNSUPPORTED_OPERATION` error returned by Salesforce (@remon)
|
61
|
+
|
62
|
+
## 5.1.0 (Aug 26, 2021)
|
63
|
+
|
64
|
+
* Add official support for Ruby 3.0 (@timrogers)
|
65
|
+
* Drop support for Ruby 2.5, which has reached end-of-life (@timrogers)
|
66
|
+
* Handle the `QUERY_TIMEOUT` error returned by Salesforce (@timrogers)
|
67
|
+
* Remove unnecessary development dependencies for the gem, which can just be in the project's `Gemfile` (@timrogers)
|
68
|
+
|
1
69
|
## 5.0.6 (Jun 17, 2021)
|
2
70
|
|
3
71
|
* Handle the `API_DISABLED_FOR_ORG` error returned by Salesforce (@cmac)
|
data/Gemfile
CHANGED
@@ -3,11 +3,20 @@
|
|
3
3
|
source 'https://rubygems.org'
|
4
4
|
gemspec
|
5
5
|
|
6
|
+
faraday_version = ENV.fetch('FARADAY_VERSION', '~> 2.7.3')
|
7
|
+
|
8
|
+
# Enable us to explicitly pick a Faraday version when running tests
|
9
|
+
gem 'faraday', faraday_version
|
10
|
+
gem 'faraday-typhoeus', '~> 0.2.1' unless faraday_version.start_with?("~> 1")
|
11
|
+
gem 'faye' unless RUBY_PLATFORM == 'java'
|
12
|
+
gem 'guard-rspec'
|
13
|
+
gem 'guard-rubocop'
|
6
14
|
gem 'jruby-openssl', platforms: :jruby
|
7
|
-
gem 'jwt'
|
8
15
|
gem 'rake'
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
16
|
+
gem 'rspec', '~> 3.12.0'
|
17
|
+
gem 'rspec-collection_matchers', '~> 1.2.0'
|
18
|
+
gem 'rspec-its', '~> 1.3.0'
|
19
|
+
gem 'rspec_junit_formatter', '~> 0.6.0'
|
20
|
+
gem 'rubocop', '~> 1.43.0'
|
21
|
+
gem 'simplecov', '~> 0.22.0'
|
22
|
+
gem 'webmock', '~> 3.18.1'
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Restforce
|
2
2
|
|
3
|
-
[![CircleCI](https://circleci.com/gh/restforce/restforce.svg?style=svg)](https://circleci.com/gh/restforce/restforce)
|
4
|
-
![](https://img.shields.io/gem/dt/restforce.svg)
|
3
|
+
[![CircleCI](https://circleci.com/gh/restforce/restforce.svg?style=svg)](https://circleci.com/gh/restforce/restforce)
|
4
|
+
![Downloads](https://img.shields.io/gem/dt/restforce.svg)
|
5
5
|
|
6
6
|
Restforce is a ruby gem for the [Salesforce REST api](http://www.salesforce.com/us/developer/docs/api_rest/index.htm).
|
7
7
|
|
@@ -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', '~>
|
30
|
+
gem 'restforce', '~> 6.2.1'
|
29
31
|
|
30
32
|
And then execute:
|
31
33
|
|
@@ -35,8 +37,10 @@ Or install it yourself as:
|
|
35
37
|
|
36
38
|
$ gem install restforce
|
37
39
|
|
38
|
-
__As of version
|
40
|
+
__As of version 6.0.0, this gem is only compatible with Ruby 2.7.0 and later.__ If you're using an earlier Ruby version:
|
39
41
|
|
42
|
+
* for Ruby 2.6, use version 5.3.1 or earlier
|
43
|
+
* for Ruby 2.5, use version 5.0.6 or earlier
|
40
44
|
* for Ruby 2.4, use version 4.3.0 or earlier
|
41
45
|
* for Ruby 2.3, use version 3.2.0 or earlier
|
42
46
|
* for Ruby versions 2.2, 2.1 and 2.0, use version 2.5.3 or earlier
|
@@ -147,6 +151,8 @@ export SALESFORCE_API_VERSION="41.0"
|
|
147
151
|
client = Restforce.new
|
148
152
|
```
|
149
153
|
|
154
|
+
**Note:** Restforce library does not cache JWT Bearer tokens automatically. This means that every instantiation of the Restforce class will be treated as a new login by Salesforce. Remember that Salesforce enforces [rate limits on login requests](https://help.salesforce.com/s/articleView?id=000312767&type=1). If you are building an application that will instantiate the Restforce class more than this specified rate limit, you might want to consider caching the Bearer token either in-memory or in your own storage by leveraging the `authentication_callback` method.
|
155
|
+
|
150
156
|
#### Sandbox Organizations
|
151
157
|
|
152
158
|
You can connect to sandbox organizations by specifying a host. The default host is
|
@@ -188,7 +194,7 @@ end
|
|
188
194
|
|
189
195
|
By default, the gem defaults to using Version 26.0 (Winter '13) of the Salesforce API. This maintains backwards compatibility for existing users.
|
190
196
|
|
191
|
-
__We strongly suggest configuring Restforce to use the most recent API version
|
197
|
+
__We strongly suggest configuring Restforce to use the [most recent API version](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_versions.htm), to get the best Salesforce API experience__ - for example, some more recently-added API endpoints will not be available without moving to a more recent
|
192
198
|
version. If you're trying to use a method that is unavailable with your API version,
|
193
199
|
Restforce will raise an `APIVersionError`.
|
194
200
|
|
@@ -290,6 +296,8 @@ client.find('Account', '1234', 'Some_External_Id_Field__c')
|
|
290
296
|
# => #<Restforce::SObject Id="001D000000INjVe" Name="Test" LastModifiedBy="005G0000002f8FHIAY" ... >
|
291
297
|
```
|
292
298
|
|
299
|
+
`find` raises an error if nothing is found.
|
300
|
+
|
293
301
|
### select
|
294
302
|
|
295
303
|
`select` allows the fetching of a specific list of fields from a single object. It requires an `external_id` lookup, but is often much faster than an arbitrary query.
|
@@ -459,7 +467,7 @@ info.user_id
|
|
459
467
|
|
460
468
|
### File Uploads
|
461
469
|
|
462
|
-
Using the new [Blob Data](
|
470
|
+
Using the new [Blob Data](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_insert_update_blob.htm) api feature (500mb limit):
|
463
471
|
|
464
472
|
```ruby
|
465
473
|
client.create('Document', FolderId: '00lE0000000FJ6H',
|
@@ -477,7 +485,7 @@ client.create('Document', FolderId: '00lE0000000FJ6H',
|
|
477
485
|
Body: Base64::encode64(File.read('image.jpg'))
|
478
486
|
```
|
479
487
|
|
480
|
-
_See also: [Inserting or updating blob data](
|
488
|
+
_See also: [Inserting or updating blob data](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_sobject_insert_update_blob.htm)_
|
481
489
|
|
482
490
|
* * *
|
483
491
|
|
@@ -572,6 +580,52 @@ end
|
|
572
580
|
Boom, you're now receiving push notifications when Accounts are
|
573
581
|
created/updated.
|
574
582
|
|
583
|
+
#### Composite API
|
584
|
+
|
585
|
+
Restforce supports the [Composite API](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_composite_composite.htm).
|
586
|
+
This feature permits the user to send a composite object—that is, a complex
|
587
|
+
object with nested children—in a single API call. Up to 25 requests may be
|
588
|
+
included in a single composite.
|
589
|
+
|
590
|
+
Note that `GET` is not yet implemented for this API.
|
591
|
+
|
592
|
+
```ruby
|
593
|
+
# build up an array of requests:
|
594
|
+
requests << {
|
595
|
+
method: :update,
|
596
|
+
sobject: sobject, # e.g. "Contact"
|
597
|
+
reference_id: reference_id,
|
598
|
+
data: data
|
599
|
+
}
|
600
|
+
|
601
|
+
# send every 25 requests as a subrequest in a single composite call
|
602
|
+
requests.each_slice(25).map do |req_slice|
|
603
|
+
client.composite do |subrequest|
|
604
|
+
req_slice.each do |r|
|
605
|
+
subrequest.send *r.values
|
606
|
+
end
|
607
|
+
end
|
608
|
+
end
|
609
|
+
|
610
|
+
# note that we're using `map` to return an array of each responses to each
|
611
|
+
# composite call; 100 requests will produce 4 responses
|
612
|
+
```
|
613
|
+
|
614
|
+
#### Composite Batch API
|
615
|
+
|
616
|
+
Restforce supports the [Composite Batch API](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_composite_batch.htm).
|
617
|
+
This feature permits up to 25 subrequests in a single request, though each
|
618
|
+
subrequest counts against the API limit. On the other hand, it has fewer
|
619
|
+
limitations than the Composite API.
|
620
|
+
|
621
|
+
```
|
622
|
+
client.batch do |subrequests|
|
623
|
+
subrequests.create('Object', name: 'test')
|
624
|
+
subrequests.update('Object', id: '123', name: 'test')
|
625
|
+
subrequests.destroy('Object', '123')
|
626
|
+
end
|
627
|
+
```
|
628
|
+
|
575
629
|
#### Replaying Events
|
576
630
|
|
577
631
|
Since API version 37.0, Salesforce stores events for 24 hours and they can be
|
data/UPGRADING.md
CHANGED
@@ -1,3 +1,32 @@
|
|
1
|
+
# Upgrading from Restforce 5.x to 6.x
|
2
|
+
|
3
|
+
__There are two breaking changes introduced in Restforce 6.x__. In this guide, you'll learn about these changes and what you should check in your code to make sure that it will work with the latest version of the library.
|
4
|
+
|
5
|
+
## Versions of `faraday` before `v1.1.0` are no longer supported
|
6
|
+
|
7
|
+
__Likelyhood of impact__: Moderate
|
8
|
+
|
9
|
+
Restforce uses a gem called [`faraday`](https://github.com/lostisland/faraday) to make HTTP requests to Salesforce.
|
10
|
+
|
11
|
+
Up until now, Restforce has supported Faraday versions between v0.9.0 and v1.10.0.
|
12
|
+
|
13
|
+
In Restforce 6.x, we drop support for Faraday versions before v1.1.0, and add support for Faraday v2.x.
|
14
|
+
|
15
|
+
This will allow you to use the latest versions of Faraday and benefit from security patches, new features, etc., but you may need to adapt your code. The impact of this change will depend on your project:
|
16
|
+
|
17
|
+
* If Restforce is the only part of your project using Faraday - that is, your own code doesn't use Faraday and none of your other gems use Faraday - then you shouldn't need to do anything special. Just upgrade Restforce, and everything should be handled automatically.
|
18
|
+
* If your own code uses Faraday or another gem you use depends on Faraday, and you're currently using a Faraday version before v1.1.0, you will need to upgrade your Faraday version. If possible, you should upgrade to the latest version (v2.4.0 at the time of writing). This may require you to adapt your code (see [here](https://github.com/lostisland/faraday/blob/main/UPGRADING.md) for Faraday's instructions) or upgrade other gems you depend on.
|
19
|
+
|
20
|
+
## Ruby 2.6 is no longer supported
|
21
|
+
|
22
|
+
__Likelyhood of impact__: Moderate
|
23
|
+
|
24
|
+
Ruby 2.6 is no longer officially supported as an active version of the Ruby language. That means that it will not receive patches and security fixes.
|
25
|
+
|
26
|
+
Accordingly, we've dropped support for Ruby 2.6 and earlier in the Restforce library. The gemspec now specifies that only 2.7 onwards is supported, and this will be enforced by RubyGems.
|
27
|
+
|
28
|
+
Before you update to Restforce 6.x, you'll need to switch to Ruby 2.7 or later. The current version of Ruby at the time of wriing is 3.1.
|
29
|
+
|
1
30
|
# Upgrading from Restforce 4.x to 5.x
|
2
31
|
|
3
32
|
__There are three breaking changes introduced in Restforce 5.x__. In this guide, you'll learn about these changes and what you should check in your code to make sure that it will work with the latest version of the library.
|
data/lib/restforce/collection.rb
CHANGED
@@ -27,12 +27,30 @@ 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.
|
33
|
+
#
|
34
|
+
# Most of the Salesforce API returns this in the `totalSize` attribute. For
|
35
|
+
# some reason, the [List View Results](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_listviewresults.htm)
|
36
|
+
# endpoint (and maybe others?!) uses the `size` attribute.
|
31
37
|
def size
|
32
|
-
@raw_page['totalSize']
|
38
|
+
@raw_page['totalSize'] || @raw_page['size']
|
33
39
|
end
|
34
40
|
alias length size
|
35
41
|
|
42
|
+
def count(*args)
|
43
|
+
# By default, `Enumerable`'s `#count` uses `#each`, which means going through all
|
44
|
+
# of the pages of results, one by one. Instead, we can use `#size` which we have
|
45
|
+
# already overridden to work in a smarter, more efficient way. This only works for
|
46
|
+
# the simple version of `#count` with no arguments. When called with an argument or
|
47
|
+
# a block, you need to know what the items in the collection actually are, so we
|
48
|
+
# call `super` and end up iterating through each item in the collection.
|
49
|
+
return size unless block_given? || !args.empty?
|
50
|
+
|
51
|
+
super
|
52
|
+
end
|
53
|
+
|
36
54
|
# Returns true if the size of the Collection is zero.
|
37
55
|
def empty?
|
38
56
|
size.zero?
|
@@ -380,7 +380,7 @@ module Restforce
|
|
380
380
|
end
|
381
381
|
else
|
382
382
|
api_patch "sobjects/#{sobject}/#{field}/" \
|
383
|
-
|
383
|
+
"#{ERB::Util.url_encode(external_id)}", attrs
|
384
384
|
end
|
385
385
|
|
386
386
|
response.body.respond_to?(:fetch) ? response.body.fetch('id', true) : true
|
@@ -429,6 +429,7 @@ module Restforce
|
|
429
429
|
# field - External ID field to use (default: nil).
|
430
430
|
#
|
431
431
|
# Returns the Restforce::SObject sobject record.
|
432
|
+
# Raises NotFoundError if nothing is found.
|
432
433
|
def find(sobject, id, field = nil)
|
433
434
|
url = if field
|
434
435
|
"sobjects/#{sobject}/#{field}/#{ERB::Util.url_encode(id)}"
|
@@ -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.new(last_error['body'][0]['errorCode'], response)
|
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
|
@@ -43,7 +43,7 @@ module Restforce
|
|
43
43
|
# Caches GET requests.
|
44
44
|
builder.use Restforce::Middleware::Caching, cache, options if cache
|
45
45
|
# Follows 30x redirects.
|
46
|
-
builder.use
|
46
|
+
builder.use Faraday::FollowRedirects::Middleware
|
47
47
|
# Raises errors for 40x responses.
|
48
48
|
builder.use Restforce::Middleware::RaiseError
|
49
49
|
# Parses returned JSON response into a hash.
|
@@ -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/config.rb
CHANGED
@@ -91,29 +91,31 @@ module Restforce
|
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
94
|
-
option :api_version, default: lambda { ENV
|
94
|
+
option :api_version, default: lambda { ENV.fetch('SALESFORCE_API_VERSION', '26.0') }
|
95
95
|
|
96
96
|
# The username to use during login.
|
97
|
-
option :username, default: lambda { ENV
|
97
|
+
option :username, default: lambda { ENV.fetch('SALESFORCE_USERNAME', nil) }
|
98
98
|
|
99
99
|
# The password to use during login.
|
100
|
-
option :password, default: lambda { ENV
|
100
|
+
option :password, default: lambda { ENV.fetch('SALESFORCE_PASSWORD', nil) }
|
101
101
|
|
102
102
|
# The security token to use during login.
|
103
|
-
option :security_token, default: lambda {
|
103
|
+
option :security_token, default: lambda {
|
104
|
+
ENV.fetch('SALESFORCE_SECURITY_TOKEN', nil)
|
105
|
+
}
|
104
106
|
|
105
107
|
# The OAuth client id
|
106
|
-
option :client_id, default: lambda { ENV
|
108
|
+
option :client_id, default: lambda { ENV.fetch('SALESFORCE_CLIENT_ID', nil) }
|
107
109
|
|
108
110
|
# The OAuth client secret
|
109
|
-
option :client_secret, default: lambda { ENV
|
111
|
+
option :client_secret, default: lambda { ENV.fetch('SALESFORCE_CLIENT_SECRET', nil) }
|
110
112
|
|
111
113
|
# The private key for JWT authentication
|
112
114
|
option :jwt_key
|
113
115
|
|
114
|
-
#
|
115
|
-
# Defaults to
|
116
|
-
option :host, default: lambda { ENV
|
116
|
+
# The login host.
|
117
|
+
# Defaults to login.salesforce.com.
|
118
|
+
option :host, default: lambda { ENV.fetch('SALESFORCE_HOST', 'login.salesforce.com') }
|
117
119
|
|
118
120
|
option :oauth_token
|
119
121
|
option :refresh_token
|
@@ -139,7 +141,7 @@ module Restforce
|
|
139
141
|
# Faraday adapter to use. Defaults to Faraday.default_adapter.
|
140
142
|
option :adapter, default: lambda { Faraday.default_adapter }
|
141
143
|
|
142
|
-
option :proxy_uri, default: lambda { ENV
|
144
|
+
option :proxy_uri, default: lambda { ENV.fetch('SALESFORCE_PROXY_URI', nil) }
|
143
145
|
|
144
146
|
# A Proc that is called with the response body after a successful authentication.
|
145
147
|
option :authentication_callback
|