restforce 2.5.4 → 4.0.0
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 +56 -0
- data/.rubocop.yml +27 -14
- data/.rubocop_todo.yml +128 -81
- data/CHANGELOG.md +37 -3
- data/CONTRIBUTING.md +3 -3
- data/Gemfile +4 -2
- data/Guardfile +3 -1
- data/LICENSE +1 -1
- data/README.md +120 -19
- data/Rakefile +2 -1
- data/lib/restforce.rb +23 -1
- data/lib/restforce/abstract_client.rb +3 -0
- data/lib/restforce/attachment.rb +3 -0
- data/lib/restforce/client.rb +2 -0
- data/lib/restforce/collection.rb +3 -1
- data/lib/restforce/concerns/api.rb +20 -14
- data/lib/restforce/concerns/authentication.rb +2 -0
- data/lib/restforce/concerns/base.rb +2 -0
- data/lib/restforce/concerns/batch_api.rb +87 -0
- data/lib/restforce/concerns/caching.rb +4 -2
- data/lib/restforce/concerns/canvas.rb +3 -0
- data/lib/restforce/concerns/connection.rb +26 -20
- data/lib/restforce/concerns/picklists.rb +9 -6
- data/lib/restforce/concerns/streaming.rb +60 -1
- data/lib/restforce/concerns/verbs.rb +3 -1
- data/lib/restforce/config.rb +4 -1
- data/lib/restforce/data/client.rb +2 -0
- data/lib/restforce/document.rb +3 -0
- data/lib/restforce/mash.rb +2 -0
- data/lib/restforce/middleware.rb +2 -0
- data/lib/restforce/middleware/authentication.rb +8 -6
- data/lib/restforce/middleware/authentication/password.rb +2 -0
- data/lib/restforce/middleware/authentication/token.rb +2 -0
- data/lib/restforce/middleware/authorization.rb +3 -1
- data/lib/restforce/middleware/caching.rb +3 -1
- data/lib/restforce/middleware/custom_headers.rb +2 -0
- data/lib/restforce/middleware/gzip.rb +5 -3
- data/lib/restforce/middleware/instance_url.rb +7 -3
- data/lib/restforce/middleware/logger.rb +2 -0
- data/lib/restforce/middleware/mashify.rb +2 -0
- data/lib/restforce/middleware/multipart.rb +8 -4
- data/lib/restforce/middleware/raise_error.rb +26 -8
- data/lib/restforce/patches/parts.rb +2 -0
- data/lib/restforce/signed_request.rb +3 -0
- data/lib/restforce/sobject.rb +3 -0
- data/lib/restforce/tooling/client.rb +5 -3
- data/lib/restforce/upload_io.rb +2 -0
- data/lib/restforce/version.rb +3 -1
- data/restforce.gemspec +19 -12
- data/spec/fixtures/sobject/sobject_describe_success_response.json +48 -1
- data/spec/integration/abstract_client_spec.rb +51 -7
- data/spec/integration/data/client_spec.rb +24 -5
- data/spec/spec_helper.rb +2 -0
- data/spec/support/client_integration.rb +2 -0
- data/spec/support/concerns.rb +2 -0
- data/spec/support/event_machine.rb +2 -0
- data/spec/support/fixture_helpers.rb +4 -2
- data/spec/support/matchers.rb +2 -0
- data/spec/support/middleware.rb +3 -1
- data/spec/support/mock_cache.rb +4 -2
- data/spec/unit/abstract_client_spec.rb +2 -0
- data/spec/unit/attachment_spec.rb +2 -0
- data/spec/unit/collection_spec.rb +5 -3
- data/spec/unit/concerns/api_spec.rb +40 -11
- data/spec/unit/concerns/authentication_spec.rb +4 -2
- data/spec/unit/concerns/base_spec.rb +2 -0
- data/spec/unit/concerns/batch_api_spec.rb +107 -0
- data/spec/unit/concerns/caching_spec.rb +2 -0
- data/spec/unit/concerns/canvas_spec.rb +3 -1
- data/spec/unit/concerns/connection_spec.rb +5 -3
- data/spec/unit/concerns/streaming_spec.rb +115 -1
- data/spec/unit/config_spec.rb +10 -8
- data/spec/unit/data/client_spec.rb +2 -0
- data/spec/unit/document_spec.rb +2 -0
- data/spec/unit/mash_spec.rb +3 -1
- data/spec/unit/middleware/authentication/password_spec.rb +2 -0
- data/spec/unit/middleware/authentication/token_spec.rb +2 -0
- data/spec/unit/middleware/authentication_spec.rb +3 -1
- data/spec/unit/middleware/authorization_spec.rb +2 -0
- data/spec/unit/middleware/custom_headers_spec.rb +3 -1
- data/spec/unit/middleware/gzip_spec.rb +4 -2
- data/spec/unit/middleware/instance_url_spec.rb +2 -0
- data/spec/unit/middleware/logger_spec.rb +2 -0
- data/spec/unit/middleware/mashify_spec.rb +3 -1
- data/spec/unit/middleware/raise_error_spec.rb +34 -11
- data/spec/unit/signed_request_spec.rb +2 -0
- data/spec/unit/sobject_spec.rb +5 -3
- data/spec/unit/tooling/client_spec.rb +2 -0
- metadata +38 -20
- data/.travis.yml +0 -16
- data/Gemfile.travis +0 -8
data/CONTRIBUTING.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# Contributing
|
2
2
|
|
3
3
|
We love pull requests from everyone. By participating in this project, you
|
4
|
-
agree to abide by our [code of conduct](https://github.com/
|
4
|
+
agree to abide by our [code of conduct](https://github.com/restforce/restforce/blob/master/CODE_OF_CONDUCT.md).
|
5
5
|
|
6
6
|
Fork, then clone the repo:
|
7
7
|
|
8
|
-
git clone git@github.com:
|
8
|
+
git clone git@github.com:restforce/restforce.git
|
9
9
|
|
10
10
|
Set up your machine:
|
11
11
|
|
@@ -23,7 +23,7 @@ Make your change. Add tests for your change. Make the tests pass:
|
|
23
23
|
|
24
24
|
script/test
|
25
25
|
|
26
|
-
Push to your fork and [submit a pull request](https://github.com/
|
26
|
+
Push to your fork and [submit a pull request](https://github.com/restforce/restforce/compare/).
|
27
27
|
|
28
28
|
At this point you're waiting on us. We like to at least comment on pull requests
|
29
29
|
within a few days. We may suggest
|
data/Gemfile
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
source 'https://rubygems.org'
|
2
4
|
gemspec
|
3
5
|
|
6
|
+
gem 'faraday', '~> 0.17.0'
|
7
|
+
gem 'jruby-openssl', platforms: :jruby
|
4
8
|
gem 'rake'
|
5
|
-
gem 'jruby-openssl', :platforms => :jruby
|
6
|
-
gem 'faraday', '~> 0.11.0'
|
7
9
|
|
8
10
|
group :development do
|
9
11
|
gem 'guard-rspec'
|
data/Guardfile
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
guard 'rspec', cmd: 'bundle exec rspec', all_on_start: false, all_after_pass: false do
|
2
4
|
watch(%r{^spec/.+_spec\.rb$})
|
3
5
|
watch('spec/spec_helper.rb') { "spec" }
|
@@ -8,6 +10,6 @@ guard 'rspec', cmd: 'bundle exec rspec', all_on_start: false, all_after_pass: fa
|
|
8
10
|
end
|
9
11
|
|
10
12
|
guard :rubocop, all_on_start: false do
|
11
|
-
watch(
|
13
|
+
watch(/.+\.rb$/)
|
12
14
|
watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
|
13
15
|
end
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Restforce
|
2
2
|
|
3
|
-
[![
|
3
|
+
[![CircleCI](https://circleci.com/gh/restforce/restforce.svg?style=svg)](https://circleci.com/gh/restforce/restforce) [![Code Climate](https://codeclimate.com/github/restforce/restforce.png)](https://codeclimate.com/github/restforce/restforce) [![Dependency Status](https://gemnasium.com/restforce/restforce.png)](https://gemnasium.com/restforce/restforce)
|
4
4
|
![](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).
|
@@ -19,13 +19,13 @@ Features include:
|
|
19
19
|
* Support for dependent picklists.
|
20
20
|
* Support for decoding [Force.com Canvas](http://www.salesforce.com/us/developer/docs/platform_connectpre/canvas_framework.pdf) signed requests. (NEW!)
|
21
21
|
|
22
|
-
[Official Website](http://restforce.org/) | [Documentation](http://rubydoc.info/gems/restforce/frames) | [Changelog](https://github.com/
|
22
|
+
[Official Website](http://restforce.org/) | [Documentation](http://rubydoc.info/gems/restforce/frames) | [Changelog](https://github.com/restforce/restforce/tree/master/CHANGELOG.md)
|
23
23
|
|
24
24
|
## Installation
|
25
25
|
|
26
26
|
Add this line to your application's Gemfile:
|
27
27
|
|
28
|
-
gem 'restforce', '~>
|
28
|
+
gem 'restforce', '~> 4.0.0'
|
29
29
|
|
30
30
|
And then execute:
|
31
31
|
|
@@ -35,15 +35,15 @@ Or install it yourself as:
|
|
35
35
|
|
36
36
|
$ gem install restforce
|
37
37
|
|
38
|
-
__As of [version
|
38
|
+
__As of [version 4.0.0](https://github.com/restforce/restforce/blob/master/CHANGELOG.md#400-oct-9-2019), this gem is only compatible with Ruby 2.4.0 and later.__ You'll need to use version 3.2.0 or earlier if you're running on Ruby 2.3. If you're running on Ruby 2.2, 2.1 or 2.0, use version 2.5.3 or earlier. For Ruby 1.9.3, you'll need to manually specify that you wish to use version 2.4.2.
|
39
39
|
|
40
|
-
This gem is versioned using [Semantic Versioning](http://semver.org/), so you can be confident when updating that there will not be breaking changes outside of a major version (following format MAJOR.MINOR.PATCH, so for instance moving from
|
40
|
+
This gem is versioned using [Semantic Versioning](http://semver.org/), so you can be confident when updating that there will not be breaking changes outside of a major version (following format MAJOR.MINOR.PATCH, so for instance moving from 3.1.0 to 4.0.0 would be allowed to include incompatible API changes). See the [changelog](https://github.com/restforce/restforce/tree/master/CHANGELOG.md) for details on what has changed in each version.
|
41
41
|
|
42
42
|
## Usage
|
43
43
|
|
44
44
|
Restforce is designed with flexibility and ease of use in mind. By default, all API calls will
|
45
45
|
return [Hashie::Mash](https://github.com/intridea/hashie/tree/v1.2.0) objects,
|
46
|
-
so you can do things like `client.query('select Id, (select Name from Children__r) from Account').Children__r.first.Name`.
|
46
|
+
so you can do things like `client.query('select Id, (select Name from Children__r) from Account').first.Children__r.first.Name`.
|
47
47
|
|
48
48
|
### Initialization
|
49
49
|
|
@@ -63,7 +63,7 @@ It is also important to note that the client object should not be reused across
|
|
63
63
|
```ruby
|
64
64
|
client = Restforce.new(oauth_token: 'access_token',
|
65
65
|
instance_url: 'instance url',
|
66
|
-
api_version: '
|
66
|
+
api_version: '41.0')
|
67
67
|
```
|
68
68
|
|
69
69
|
Although the above will work, you'll probably want to take advantage of the (re)authentication middleware by specifying `refresh_token`, `client_id`, `client_secret`, and `authentication_callback`:
|
@@ -75,7 +75,7 @@ client = Restforce.new(oauth_token: 'access_token',
|
|
75
75
|
client_id: 'client_id',
|
76
76
|
client_secret: 'client_secret',
|
77
77
|
authentication_callback: Proc.new { |x| Rails.logger.debug x.to_s },
|
78
|
-
api_version: '
|
78
|
+
api_version: '41.0')
|
79
79
|
```
|
80
80
|
|
81
81
|
The middleware will use the `refresh_token` automatically to acquire a new `access_token` if the existing `access_token` is invalid.
|
@@ -108,7 +108,7 @@ client = Restforce.new(username: 'foo',
|
|
108
108
|
security_token: 'security token',
|
109
109
|
client_id: 'client_id',
|
110
110
|
client_secret: 'client_secret',
|
111
|
-
api_version: '
|
111
|
+
api_version: '41.0')
|
112
112
|
```
|
113
113
|
|
114
114
|
You can also set the username, password, security token, client ID, client
|
@@ -120,7 +120,7 @@ export SALESFORCE_PASSWORD="password"
|
|
120
120
|
export SALESFORCE_SECURITY_TOKEN="security token"
|
121
121
|
export SALESFORCE_CLIENT_ID="client id"
|
122
122
|
export SALESFORCE_CLIENT_SECRET="client secret"
|
123
|
-
export SALESFORCE_API_VERSION="
|
123
|
+
export SALESFORCE_API_VERSION="41.0"
|
124
124
|
```
|
125
125
|
|
126
126
|
```ruby
|
@@ -138,7 +138,7 @@ client = Restforce.new(username: 'foo',
|
|
138
138
|
client_id: 'client_id',
|
139
139
|
client_secret: 'client_secret',
|
140
140
|
proxy_uri: 'http://proxy.example.com:123',
|
141
|
-
api_version: '
|
141
|
+
api_version: '41.0')
|
142
142
|
```
|
143
143
|
|
144
144
|
You may specify a username and password for the proxy with a URL along the lines of 'http://user:password@proxy.example.com:123'.
|
@@ -168,19 +168,19 @@ end
|
|
168
168
|
|
169
169
|
By default, the gem defaults to using Version 26.0 (Winter '13) of the Salesforce API. This maintains backwards compatibility for existing users.
|
170
170
|
|
171
|
-
__We strongly suggest configuring Restforce to use the most recent API version, currently Version
|
171
|
+
__We strongly suggest configuring Restforce to use the most recent API version, currently Version 41.0 (Winter '18) 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
|
172
172
|
version. If you're trying to use a method that is unavailable with your API version,
|
173
173
|
Restforce will raise an `APIVersionError`.
|
174
174
|
|
175
175
|
There are three ways to set the API version:
|
176
176
|
|
177
|
-
* Passing in an `api_version` option when instantiating `Restforce` (i.e. `Restforce.new(api_version: '
|
178
|
-
* Setting the `SALESFORCE_API_VERSION` environment variable (i.e. `export SALESFORCE_API_VERSION="
|
177
|
+
* Passing in an `api_version` option when instantiating `Restforce` (i.e. `Restforce.new(api_version: '41.0')`)
|
178
|
+
* Setting the `SALESFORCE_API_VERSION` environment variable (i.e. `export SALESFORCE_API_VERSION="41.0"`)
|
179
179
|
* Configuring the version globally with `Restforce.configure`:
|
180
180
|
|
181
181
|
```ruby
|
182
182
|
Restforce.configure do |config|
|
183
|
-
config.api_version = '
|
183
|
+
config.api_version = '41.0'
|
184
184
|
# ...
|
185
185
|
end
|
186
186
|
```
|
@@ -206,7 +206,7 @@ to include the `sforce-auto-assign` header in all client HTTP requests:
|
|
206
206
|
```ruby
|
207
207
|
client = Restforce.new(oauth_token: 'access_token',
|
208
208
|
instance_url: 'instance url',
|
209
|
-
api_version: '
|
209
|
+
api_version: '41.0',
|
210
210
|
request_headers: { 'sforce-auto-assign' => 'FALSE' })
|
211
211
|
|
212
212
|
```
|
@@ -473,6 +473,12 @@ document = client.query('select Id, Name, Body from Document').first
|
|
473
473
|
File.open(document.Name, 'wb') { |f| f.write(document.Body) }
|
474
474
|
```
|
475
475
|
|
476
|
+
**Note:** The example above is only applicable if your SOQL query returns a single Document record. If more than one record is returned,
|
477
|
+
the Body field contains an URL to retrieve the BLOB content for the first 2000 records returned. Subsequent records contain the BLOB content
|
478
|
+
in the Body field. This is confusing and hard to debug. See notes in [Issue #301](https://github.com/restforce/restforce/issues/301#issuecomment-298972959) explaining this detail.
|
479
|
+
**Executive Summary:** Don't retrieve the Body field in a SOQL query; instead, use the BLOB retrieval URL documented
|
480
|
+
in [SObject BLOB Retrieve](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_blob_retrieve.htm)
|
481
|
+
|
476
482
|
* * *
|
477
483
|
|
478
484
|
### Custom Apex REST endpoints
|
@@ -517,7 +523,7 @@ require 'faye'
|
|
517
523
|
# Initialize a client with your username/password/oauth token/etc.
|
518
524
|
client = Restforce.new(username: 'foo',
|
519
525
|
password: 'bar',
|
520
|
-
security_token: 'security token'
|
526
|
+
security_token: 'security token',
|
521
527
|
client_id: 'client_id',
|
522
528
|
client_secret: 'client_secret')
|
523
529
|
|
@@ -541,7 +547,102 @@ end
|
|
541
547
|
Boom, you're now receiving push notifications when Accounts are
|
542
548
|
created/updated.
|
543
549
|
|
544
|
-
|
550
|
+
#### Replaying Events
|
551
|
+
|
552
|
+
Since API version 37.0, Salesforce stores events for 24 hours and they can be
|
553
|
+
replayed if your application experienced some downtime.
|
554
|
+
|
555
|
+
In order to replay past events, all you need to do is specify the last known
|
556
|
+
event ID when subscribing and you will receive all events that happened since
|
557
|
+
that event ID:
|
558
|
+
|
559
|
+
```ruby
|
560
|
+
EM.run {
|
561
|
+
# Subscribe to the PushTopic.
|
562
|
+
client.subscribe 'AllAccounts', replay: 10 do |message|
|
563
|
+
puts message.inspect
|
564
|
+
end
|
565
|
+
}
|
566
|
+
```
|
567
|
+
|
568
|
+
In this specific case you will see events with replay ID 11, 12 and so on.
|
569
|
+
|
570
|
+
There are two magic values for the replay ID accepted by Salesforce:
|
571
|
+
|
572
|
+
* `-2`, for getting all the events that appeared in the last 24 hours
|
573
|
+
* `-1`, for getting only newer events
|
574
|
+
|
575
|
+
**Warning**: Only use a replay ID of a event from the last 24 hours otherwise
|
576
|
+
Salesforce will not send anything, including newer events. If in doubt, use one
|
577
|
+
of the two magic replay IDs mentioned above.
|
578
|
+
|
579
|
+
You might want to store the replay ID in some sort of datastore so you can
|
580
|
+
access it, for example between application restarts. In that case, there is the
|
581
|
+
option of passing a custom replay handler which responds to `[]` and `[]=`.
|
582
|
+
|
583
|
+
Below is a sample replay handler that stores the replay ID for each channel in
|
584
|
+
memory using a Hash, stores a timestamp and has some rudimentary logic that
|
585
|
+
will use one of the magic IDs depending on the value of the timestamp:
|
586
|
+
|
587
|
+
```ruby
|
588
|
+
class SimpleReplayHandler
|
589
|
+
|
590
|
+
MAX_AGE = 86_400 # 24 hours
|
591
|
+
|
592
|
+
INIT_REPLAY_ID = -1
|
593
|
+
DEFAULT_REPLAY_ID = -2
|
594
|
+
|
595
|
+
def initialize
|
596
|
+
@channels = {}
|
597
|
+
@last_modified = nil
|
598
|
+
end
|
599
|
+
|
600
|
+
# This method is called during the initial subscribe phase
|
601
|
+
# in order to send the correct replay ID.
|
602
|
+
def [](channel)
|
603
|
+
if @last_modified.nil?
|
604
|
+
puts "[#{channel}] No timestamp defined, sending magic replay ID #{INIT_REPLAY_ID}"
|
605
|
+
|
606
|
+
INIT_REPLAY_ID
|
607
|
+
elsif old_replay_id?
|
608
|
+
puts "[#{channel}] Old timestamp, sending magic replay ID #{DEFAULT_REPLAY_ID}"
|
609
|
+
|
610
|
+
DEFAULT_REPLAY_ID
|
611
|
+
else
|
612
|
+
@channels[channel]
|
613
|
+
end
|
614
|
+
end
|
615
|
+
|
616
|
+
def []=(channel, replay_id)
|
617
|
+
puts "[#{channel}] Writing replay ID: #{replay_id}"
|
618
|
+
|
619
|
+
@last_modified = Time.now
|
620
|
+
@channels[channel] = replay_id
|
621
|
+
end
|
622
|
+
|
623
|
+
def old_replay_id?
|
624
|
+
@last_modified.is_a?(Time) && Time.now - @last_modified > MAX_AGE
|
625
|
+
end
|
626
|
+
end
|
627
|
+
```
|
628
|
+
|
629
|
+
In order to use it, simply pass the object as the value of the `replay` option
|
630
|
+
of the subscription:
|
631
|
+
|
632
|
+
```ruby
|
633
|
+
EM.run {
|
634
|
+
# Subscribe to the PushTopic and use the custom replay handler to store any
|
635
|
+
# received replay ID.
|
636
|
+
client.subscribe 'AllAccounts', replay: SimpleReplayHandler.new do |message|
|
637
|
+
puts message.inspect
|
638
|
+
end
|
639
|
+
}
|
640
|
+
```
|
641
|
+
|
642
|
+
_See also_:
|
643
|
+
|
644
|
+
* [Force.com Streaming API docs](http://www.salesforce.com/us/developer/docs/api_streaming/index.htm)
|
645
|
+
* [Message Durability docs](https://developer.salesforce.com/docs/atlas.en-us.api_streaming.meta/api_streaming/using_streaming_api_durability.htm)
|
545
646
|
|
546
647
|
*Note:* Restforce's streaming implementation is known to be compatible with version `0.8.9` of the faye gem.
|
547
648
|
|
@@ -645,7 +746,7 @@ Callbacks.
|
|
645
746
|
|
646
747
|
We welcome all contributions - they help us make Restforce the best gem possible.
|
647
748
|
|
648
|
-
See our [CONTRIBUTING.md](https://github.com/
|
749
|
+
See our [CONTRIBUTING.md](https://github.com/restforce/restforce/blob/master/CONTRIBUTING.md) file for help with getting set up to work on the project locally.
|
649
750
|
|
650
751
|
1. Fork it
|
651
752
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
data/Rakefile
CHANGED
data/lib/restforce.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'faraday'
|
2
4
|
require 'faraday_middleware'
|
3
5
|
require 'json'
|
@@ -27,6 +29,7 @@ module Restforce
|
|
27
29
|
autoload :Verbs, 'restforce/concerns/verbs'
|
28
30
|
autoload :Base, 'restforce/concerns/base'
|
29
31
|
autoload :API, 'restforce/concerns/api'
|
32
|
+
autoload :BatchAPI, 'restforce/concerns/batch_api'
|
30
33
|
end
|
31
34
|
|
32
35
|
module Data
|
@@ -42,6 +45,25 @@ module Restforce
|
|
42
45
|
AuthenticationError = Class.new(Error)
|
43
46
|
UnauthorizedError = Class.new(Error)
|
44
47
|
APIVersionError = Class.new(Error)
|
48
|
+
BatchAPIError = Class.new(Error)
|
49
|
+
|
50
|
+
# Inherit from Faraday::Error::ResourceNotFound for backwards-compatibility
|
51
|
+
# Consumers of this library that rescue and handle Faraday::Error::ResourceNotFound
|
52
|
+
# can continue to do so.
|
53
|
+
NotFoundError = Class.new(Faraday::Error::ResourceNotFound)
|
54
|
+
|
55
|
+
# Inherit from Faraday::Error::ClientError for backwards-compatibility
|
56
|
+
# Consumers of this library that rescue and handle Faraday::Error::ClientError
|
57
|
+
# can continue to do so.
|
58
|
+
ResponseError = Class.new(Faraday::Error::ClientError)
|
59
|
+
MatchesMultipleError= Class.new(ResponseError)
|
60
|
+
EntityTooLargeError = Class.new(ResponseError)
|
61
|
+
|
62
|
+
module ErrorCode
|
63
|
+
def self.const_missing(constant_name)
|
64
|
+
const_set constant_name, Class.new(ResponseError)
|
65
|
+
end
|
66
|
+
end
|
45
67
|
|
46
68
|
class << self
|
47
69
|
# Alias for Restforce::Data::Client.new
|
@@ -72,7 +94,7 @@ module Restforce
|
|
72
94
|
self
|
73
95
|
end
|
74
96
|
end
|
75
|
-
Object.
|
97
|
+
Object.include Restforce::CoreExtensions unless Object.respond_to? :tap
|
76
98
|
end
|
77
99
|
|
78
100
|
if ENV['PROXY_URI']
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Restforce
|
2
4
|
class AbstractClient
|
3
5
|
include Restforce::Concerns::Base
|
@@ -5,5 +7,6 @@ module Restforce
|
|
5
7
|
include Restforce::Concerns::Authentication
|
6
8
|
include Restforce::Concerns::Caching
|
7
9
|
include Restforce::Concerns::API
|
10
|
+
include Restforce::Concerns::BatchAPI
|
8
11
|
end
|
9
12
|
end
|
data/lib/restforce/attachment.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Restforce
|
2
4
|
class Attachment < Restforce::SObject
|
3
5
|
# Public: Returns the body of the attachment.
|
@@ -15,6 +17,7 @@ module Restforce
|
|
15
17
|
|
16
18
|
def ensure_body
|
17
19
|
return true if self.Body?
|
20
|
+
|
18
21
|
raise 'You need to query the Body for the record first.'
|
19
22
|
end
|
20
23
|
end
|
data/lib/restforce/client.rb
CHANGED
data/lib/restforce/collection.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Restforce
|
2
4
|
class Collection
|
3
5
|
include Enumerable
|
@@ -29,7 +31,7 @@ module Restforce
|
|
29
31
|
def size
|
30
32
|
@raw_page['totalSize']
|
31
33
|
end
|
32
|
-
|
34
|
+
alias length size
|
33
35
|
|
34
36
|
# Return array of the elements on the current page
|
35
37
|
def current_page
|
@@ -1,3 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'erb'
|
1
4
|
require 'uri'
|
2
5
|
require 'restforce/concerns/verbs'
|
3
6
|
|
@@ -81,7 +84,7 @@ module Restforce
|
|
81
84
|
def get_updated(sobject, start_time, end_time)
|
82
85
|
start_time = start_time.utc.iso8601
|
83
86
|
end_time = end_time.utc.iso8601
|
84
|
-
url = "
|
87
|
+
url = "sobjects/#{sobject}/updated/?start=#{start_time}&end=#{end_time}"
|
85
88
|
api_get(url).body
|
86
89
|
end
|
87
90
|
|
@@ -101,7 +104,7 @@ module Restforce
|
|
101
104
|
def get_deleted(sobject, start_time, end_time)
|
102
105
|
start_time = start_time.utc.iso8601
|
103
106
|
end_time = end_time.utc.iso8601
|
104
|
-
url = "
|
107
|
+
url = "sobjects/#{sobject}/deleted/?start=#{start_time}&end=#{end_time}"
|
105
108
|
api_get(url).body
|
106
109
|
end
|
107
110
|
|
@@ -265,7 +268,7 @@ module Restforce
|
|
265
268
|
rescue *exceptions
|
266
269
|
false
|
267
270
|
end
|
268
|
-
|
271
|
+
alias insert create
|
269
272
|
|
270
273
|
# Public: Insert a new record.
|
271
274
|
#
|
@@ -283,7 +286,7 @@ module Restforce
|
|
283
286
|
def create!(sobject, attrs)
|
284
287
|
api_post("sobjects/#{sobject}", attrs).body['id']
|
285
288
|
end
|
286
|
-
|
289
|
+
alias insert! create!
|
287
290
|
|
288
291
|
# Public: Update a record.
|
289
292
|
#
|
@@ -316,8 +319,9 @@ module Restforce
|
|
316
319
|
# Returns true if the sobject was successfully updated.
|
317
320
|
# Raises an exception if an error is returned from Salesforce.
|
318
321
|
def update!(sobject, attrs)
|
319
|
-
id = attrs.fetch(attrs.keys.find { |k, v| k.to_s.
|
322
|
+
id = attrs.fetch(attrs.keys.find { |k, v| k.to_s.casecmp('id').zero? }, nil)
|
320
323
|
raise ArgumentError, 'ID field missing from provided attributes' unless id
|
324
|
+
|
321
325
|
attrs_without_id = attrs.reject { |k, v| k.to_s.casecmp("id").zero? }
|
322
326
|
api_patch "sobjects/#{sobject}/#{CGI.escape(id)}", attrs_without_id
|
323
327
|
true
|
@@ -363,7 +367,7 @@ module Restforce
|
|
363
367
|
def upsert!(sobject, field, attrs)
|
364
368
|
attrs = attrs.dup
|
365
369
|
external_id =
|
366
|
-
extract_case_insensitive_string_or_symbol_key_from_hash!(attrs, field)
|
370
|
+
extract_case_insensitive_string_or_symbol_key_from_hash!(attrs, field).to_s
|
367
371
|
if field.to_s != "Id" && (external_id.nil? || external_id.strip.empty?)
|
368
372
|
raise ArgumentError, 'Specified external ID field missing from provided ' \
|
369
373
|
'attributes'
|
@@ -375,10 +379,11 @@ module Restforce
|
|
375
379
|
api_post "sobjects/#{sobject}/#{field}", attrs
|
376
380
|
end
|
377
381
|
else
|
378
|
-
api_patch "sobjects/#{sobject}/#{field}
|
382
|
+
api_patch "sobjects/#{sobject}/#{field}/" \
|
383
|
+
"#{ERB::Util.url_encode(external_id)}", attrs
|
379
384
|
end
|
380
385
|
|
381
|
-
|
386
|
+
response.body.respond_to?(:fetch) ? response.body.fetch('id', true) : true
|
382
387
|
end
|
383
388
|
|
384
389
|
# Public: Delete a record.
|
@@ -412,7 +417,7 @@ module Restforce
|
|
412
417
|
# Returns true of the sobject was successfully deleted.
|
413
418
|
# Raises an exception if an error is returned from Salesforce.
|
414
419
|
def destroy!(sobject, id)
|
415
|
-
api_delete "sobjects/#{sobject}/#{
|
420
|
+
api_delete "sobjects/#{sobject}/#{ERB::Util.url_encode(id)}"
|
416
421
|
true
|
417
422
|
end
|
418
423
|
|
@@ -426,9 +431,9 @@ module Restforce
|
|
426
431
|
# Returns the Restforce::SObject sobject record.
|
427
432
|
def find(sobject, id, field = nil)
|
428
433
|
url = if field
|
429
|
-
"sobjects/#{sobject}/#{field}/#{
|
434
|
+
"sobjects/#{sobject}/#{field}/#{ERB::Util.url_encode(id)}"
|
430
435
|
else
|
431
|
-
"sobjects/#{sobject}/#{
|
436
|
+
"sobjects/#{sobject}/#{ERB::Util.url_encode(id)}"
|
432
437
|
end
|
433
438
|
api_get(url).body
|
434
439
|
end
|
@@ -444,11 +449,12 @@ module Restforce
|
|
444
449
|
#
|
445
450
|
def select(sobject, id, select, field = nil)
|
446
451
|
path = if field
|
447
|
-
"sobjects/#{sobject}/#{field}/#{
|
452
|
+
"sobjects/#{sobject}/#{field}/#{ERB::Util.url_encode(id)}"
|
448
453
|
else
|
449
|
-
"sobjects/#{sobject}/#{
|
454
|
+
"sobjects/#{sobject}/#{ERB::Util.url_encode(id)}"
|
450
455
|
end
|
451
|
-
|
456
|
+
|
457
|
+
path = "#{path}?fields=#{select.join(',')}" if select&.any?
|
452
458
|
|
453
459
|
api_get(path).body
|
454
460
|
end
|