restforce 3.2.0 → 4.2.2
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 +9 -9
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +2 -2
- data/CHANGELOG.md +21 -0
- data/Gemfile +1 -0
- data/README.md +33 -16
- data/lib/restforce.rb +7 -6
- data/lib/restforce/concerns/api.rb +1 -1
- data/lib/restforce/concerns/authentication.rb +10 -0
- data/lib/restforce/concerns/base.rb +2 -0
- data/lib/restforce/concerns/connection.rb +3 -3
- data/lib/restforce/concerns/picklists.rb +1 -1
- data/lib/restforce/concerns/streaming.rb +22 -7
- data/lib/restforce/config.rb +3 -0
- data/lib/restforce/mash.rb +1 -1
- data/lib/restforce/middleware/authentication.rb +7 -3
- data/lib/restforce/middleware/authentication/jwt_bearer.rb +38 -0
- data/lib/restforce/middleware/instance_url.rb +1 -1
- data/lib/restforce/version.rb +1 -1
- data/restforce.gemspec +5 -4
- data/spec/fixtures/test_private.key +27 -0
- data/spec/integration/abstract_client_spec.rb +2 -2
- data/spec/unit/concerns/api_spec.rb +1 -1
- data/spec/unit/concerns/authentication_spec.rb +35 -0
- data/spec/unit/concerns/streaming_spec.rb +58 -30
- data/spec/unit/mash_spec.rb +5 -0
- data/spec/unit/middleware/authentication/jwt_bearer_spec.rb +62 -0
- data/spec/unit/middleware/authentication_spec.rb +21 -0
- data/spec/unit/middleware/raise_error_spec.rb +10 -10
- data/spec/unit/sobject_spec.rb +2 -5
- metadata +28 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 414c3c4cb8ef46b5f943d190aa10d750c58c06d18ab280ba5dd34883cc684a80
|
4
|
+
data.tar.gz: fa27e1d169c51af87ef5a2a10c80ffe9619f01f34efc54778a92361147b37e2a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 227bb7793df6e71fda10d1c3d89b77c066525f0d5dee759a06900cec3bee8e7c2361cc395681a3cd0b0d547b16afb13bd19f707c750372357fd95da1697e4e12
|
7
|
+
data.tar.gz: bfe499dc745c3f32d624cad78dbafc1c5b3777b4381d6ead38eeaae026d489ec22aa2719c0607b8923f0889744b87bf5a35694f201fd22d9418a5aefcf1f0a4d
|
data/.circleci/config.yml
CHANGED
@@ -34,23 +34,23 @@ references:
|
|
34
34
|
destination: test-results
|
35
35
|
|
36
36
|
jobs:
|
37
|
-
build-
|
37
|
+
build-ruby265:
|
38
38
|
docker:
|
39
|
-
- image: circleci/ruby:2.5
|
39
|
+
- image: circleci/ruby:2.6.5
|
40
40
|
steps: *steps
|
41
|
-
build-
|
41
|
+
build-ruby257:
|
42
42
|
docker:
|
43
|
-
- image: circleci/ruby:2.
|
43
|
+
- image: circleci/ruby:2.5.7
|
44
44
|
steps: *steps
|
45
|
-
build-
|
45
|
+
build-ruby249:
|
46
46
|
docker:
|
47
|
-
- image: circleci/ruby:2.
|
47
|
+
- image: circleci/ruby:2.4.9
|
48
48
|
steps: *steps
|
49
49
|
|
50
50
|
workflows:
|
51
51
|
version: 2
|
52
52
|
tests:
|
53
53
|
jobs:
|
54
|
-
- build-
|
55
|
-
- build-
|
56
|
-
- build-
|
54
|
+
- build-ruby265
|
55
|
+
- build-ruby257
|
56
|
+
- build-ruby249
|
data/.rubocop.yml
CHANGED
data/.rubocop_todo.yml
CHANGED
@@ -12,7 +12,7 @@
|
|
12
12
|
# SupportedHashRocketStyles: key, separator, table
|
13
13
|
# SupportedColonStyles: key, separator, table
|
14
14
|
# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
|
15
|
-
Layout/
|
15
|
+
Layout/HashAlignment:
|
16
16
|
Exclude:
|
17
17
|
- 'lib/restforce/middleware/logger.rb'
|
18
18
|
- 'restforce.gemspec'
|
@@ -23,7 +23,7 @@ Layout/AlignHash:
|
|
23
23
|
# Cop supports --auto-correct.
|
24
24
|
# Configuration parameters: EnforcedStyle, IndentationWidth.
|
25
25
|
# SupportedStyles: special_inside_parentheses, consistent, align_braces
|
26
|
-
Layout/
|
26
|
+
Layout/FirstHashElementIndentation:
|
27
27
|
Exclude:
|
28
28
|
- 'lib/restforce/concerns/connection.rb'
|
29
29
|
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,24 @@
|
|
1
|
+
## 4.2.2 (Jan 23, 2020)
|
2
|
+
|
3
|
+
* Fix `NoMethodError: undefined method '[]' for nil:NilClass` error when generating objects to return (@apurkiss)
|
4
|
+
|
5
|
+
## 4.2.1 (Dec 4, 2019)
|
6
|
+
|
7
|
+
* Handle empty response bodies returned with authentication errors (@sylvandor)
|
8
|
+
* Fix Faraday deprecation warning in newer Faraday versions (`v0.17.1` onwards) caused by inheriting from a deprecated class
|
9
|
+
|
10
|
+
## 4.2.0 (Oct 23, 2019)
|
11
|
+
|
12
|
+
* Add support for platform events, CDC, generic events, etc. in the Streaming API (@nathanKramer)
|
13
|
+
|
14
|
+
## 4.1.0 (Oct 20, 2019)
|
15
|
+
|
16
|
+
* Add support for JWT authentication (@nathanKramer, @tagCincy)
|
17
|
+
|
18
|
+
## 4.0.0 (Oct 9, 2019)
|
19
|
+
|
20
|
+
* __Deprecate support for Ruby 2.3__, since [Ruby 2.3 reached its end-of-life](https://www.ruby-lang.org/en/news/2019/03/31/support-of-ruby-2-3-has-ended/) in March 2019. (This is the only breaking change included in this version.)
|
21
|
+
|
1
22
|
## 3.2.0 (Oct 9, 2019)
|
2
23
|
|
3
24
|
* Add support for the Batch API (@gaiottino, @teoulas)
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -25,7 +25,7 @@ Features include:
|
|
25
25
|
|
26
26
|
Add this line to your application's Gemfile:
|
27
27
|
|
28
|
-
gem 'restforce', '~>
|
28
|
+
gem 'restforce', '~> 4.2.2'
|
29
29
|
|
30
30
|
And then execute:
|
31
31
|
|
@@ -35,7 +35,7 @@ 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
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
|
|
@@ -111,6 +111,21 @@ client = Restforce.new(username: 'foo',
|
|
111
111
|
api_version: '41.0')
|
112
112
|
```
|
113
113
|
|
114
|
+
#### JWT Bearer Token
|
115
|
+
|
116
|
+
If you prefer to use a [JWT Bearer Token](https://developer.salesforce.com/page/Digging_Deeper_into_OAuth_2.0_on_Force.com#Obtaining_an_Access_Token_using_a_JWT_Bearer_Token) to authenticate:
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
client = Restforce.new(username: 'foo',
|
120
|
+
client_id: 'client_id',
|
121
|
+
instance_url: 'instance_url',
|
122
|
+
jwt_key: 'certificate_private_key',
|
123
|
+
api_version: '38.0')
|
124
|
+
```
|
125
|
+
|
126
|
+
The `jwt_key` option is the private key of the certificate uploaded to your Connected App in Salesforce.
|
127
|
+
Choose "use digital signatures" in the Connected App configuration screen to upload your certificate.
|
128
|
+
|
114
129
|
You can also set the username, password, security token, client ID, client
|
115
130
|
secret and API version in environment variables:
|
116
131
|
|
@@ -473,11 +488,11 @@ document = client.query('select Id, Name, Body from Document').first
|
|
473
488
|
File.open(document.Name, 'wb') { |f| f.write(document.Body) }
|
474
489
|
```
|
475
490
|
|
476
|
-
**Note:** The example above is only applicable if your SOQL query returns a single Document record. If more than one record is returned,
|
491
|
+
**Note:** The example above is only applicable if your SOQL query returns a single Document record. If more than one record is returned,
|
477
492
|
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)
|
493
|
+
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.
|
494
|
+
**Executive Summary:** Don't retrieve the Body field in a SOQL query; instead, use the BLOB retrieval URL documented
|
495
|
+
in [SObject BLOB Retrieve](https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_blob_retrieve.htm)
|
481
496
|
|
482
497
|
* * *
|
483
498
|
|
@@ -513,8 +528,10 @@ client.get('/services/apexrest/FieldCase', company: 'GenePoint')
|
|
513
528
|
|
514
529
|
### Streaming
|
515
530
|
|
516
|
-
Restforce supports the [Streaming API](
|
517
|
-
pub/sub with Salesforce a trivial task
|
531
|
+
Restforce supports the [Streaming API](https://trailhead.salesforce.com/en/content/learn/modules/api_basics/api_basics_streaming), and makes implementing
|
532
|
+
pub/sub with Salesforce a trivial task.
|
533
|
+
|
534
|
+
Here is an example of creating and subscribing to a `PushTopic`:
|
518
535
|
|
519
536
|
```ruby
|
520
537
|
# Restforce uses faye as the underlying implementation for CometD.
|
@@ -538,7 +555,7 @@ client.create!('PushTopic',
|
|
538
555
|
|
539
556
|
EM.run do
|
540
557
|
# Subscribe to the PushTopic.
|
541
|
-
client.
|
558
|
+
client.subscription '/topic/AllAccounts' do |message|
|
542
559
|
puts message.inspect
|
543
560
|
end
|
544
561
|
end
|
@@ -559,7 +576,7 @@ that event ID:
|
|
559
576
|
```ruby
|
560
577
|
EM.run {
|
561
578
|
# Subscribe to the PushTopic.
|
562
|
-
client.
|
579
|
+
client.subscription '/topic/AllAccounts', replay: 10 do |message|
|
563
580
|
puts message.inspect
|
564
581
|
end
|
565
582
|
}
|
@@ -574,14 +591,14 @@ There are two magic values for the replay ID accepted by Salesforce:
|
|
574
591
|
|
575
592
|
**Warning**: Only use a replay ID of a event from the last 24 hours otherwise
|
576
593
|
Salesforce will not send anything, including newer events. If in doubt, use one
|
577
|
-
of the two magic replay IDs mentioned above.
|
594
|
+
of the two magic replay IDs mentioned above.
|
578
595
|
|
579
|
-
You might want to store the replay ID in some sort of datastore so you can
|
596
|
+
You might want to store the replay ID in some sort of datastore so you can
|
580
597
|
access it, for example between application restarts. In that case, there is the
|
581
598
|
option of passing a custom replay handler which responds to `[]` and `[]=`.
|
582
599
|
|
583
600
|
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
|
601
|
+
memory using a Hash, stores a timestamp and has some rudimentary logic that
|
585
602
|
will use one of the magic IDs depending on the value of the timestamp:
|
586
603
|
|
587
604
|
```ruby
|
@@ -633,13 +650,13 @@ of the subscription:
|
|
633
650
|
EM.run {
|
634
651
|
# Subscribe to the PushTopic and use the custom replay handler to store any
|
635
652
|
# received replay ID.
|
636
|
-
client.
|
653
|
+
client.subscription '/topic/AllAccounts', replay: SimpleReplayHandler.new do |message|
|
637
654
|
puts message.inspect
|
638
655
|
end
|
639
656
|
}
|
640
|
-
```
|
657
|
+
```
|
641
658
|
|
642
|
-
_See also_:
|
659
|
+
_See also_:
|
643
660
|
|
644
661
|
* [Force.com Streaming API docs](http://www.salesforce.com/us/developer/docs/api_streaming/index.htm)
|
645
662
|
* [Message Durability docs](https://developer.salesforce.com/docs/atlas.en-us.api_streaming.meta/api_streaming/using_streaming_api_durability.htm)
|
data/lib/restforce.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'faraday'
|
4
4
|
require 'faraday_middleware'
|
5
5
|
require 'json'
|
6
|
+
require 'jwt'
|
6
7
|
|
7
8
|
require 'restforce/version'
|
8
9
|
require 'restforce/config'
|
@@ -47,15 +48,15 @@ module Restforce
|
|
47
48
|
APIVersionError = Class.new(Error)
|
48
49
|
BatchAPIError = Class.new(Error)
|
49
50
|
|
50
|
-
# Inherit from Faraday::
|
51
|
-
# Consumers of this library that rescue and handle Faraday::
|
51
|
+
# Inherit from Faraday::ResourceNotFound for backwards-compatibility
|
52
|
+
# Consumers of this library that rescue and handle Faraday::ResourceNotFound
|
52
53
|
# can continue to do so.
|
53
|
-
NotFoundError = Class.new(Faraday::
|
54
|
+
NotFoundError = Class.new(Faraday::ResourceNotFound)
|
54
55
|
|
55
|
-
# Inherit from Faraday::
|
56
|
-
# Consumers of this library that rescue and handle Faraday::
|
56
|
+
# Inherit from Faraday::ClientError for backwards-compatibility
|
57
|
+
# Consumers of this library that rescue and handle Faraday::ClientError
|
57
58
|
# can continue to do so.
|
58
|
-
ResponseError = Class.new(Faraday::
|
59
|
+
ResponseError = Class.new(Faraday::ClientError)
|
59
60
|
MatchesMultipleError= Class.new(ResponseError)
|
60
61
|
EntityTooLargeError = Class.new(ResponseError)
|
61
62
|
|
@@ -19,6 +19,8 @@ module Restforce
|
|
19
19
|
Restforce::Middleware::Authentication::Password
|
20
20
|
elsif oauth_refresh?
|
21
21
|
Restforce::Middleware::Authentication::Token
|
22
|
+
elsif jwt?
|
23
|
+
Restforce::Middleware::Authentication::JWTBearer
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
@@ -38,6 +40,14 @@ module Restforce
|
|
38
40
|
options[:client_id] &&
|
39
41
|
options[:client_secret]
|
40
42
|
end
|
43
|
+
|
44
|
+
# Internal: Returns true if jwt bearer token flow should be used for
|
45
|
+
# authentication.
|
46
|
+
def jwt?
|
47
|
+
options[:jwt_key] &&
|
48
|
+
options[:username] &&
|
49
|
+
options[:client_id]
|
50
|
+
end
|
41
51
|
end
|
42
52
|
end
|
43
53
|
end
|
@@ -28,6 +28,8 @@ module Restforce
|
|
28
28
|
# password and oauth authentication
|
29
29
|
# :client_secret - The oauth client secret to use.
|
30
30
|
#
|
31
|
+
# :jwt_key - The private key for JWT authentication
|
32
|
+
#
|
31
33
|
# :host - The String hostname to use during
|
32
34
|
# authentication requests
|
33
35
|
# (default: 'login.salesforce.com').
|
@@ -72,9 +72,9 @@ module Restforce
|
|
72
72
|
# Internal: Faraday Connection options
|
73
73
|
def connection_options
|
74
74
|
{ request: {
|
75
|
-
|
76
|
-
|
77
|
-
},
|
75
|
+
timeout: options[:timeout],
|
76
|
+
open_timeout: options[:timeout]
|
77
|
+
},
|
78
78
|
proxy: options[:proxy_uri],
|
79
79
|
ssl: options[:ssl] }
|
80
80
|
end
|
@@ -83,7 +83,7 @@ module Restforce
|
|
83
83
|
# See http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_calls_des
|
84
84
|
# cribesobjects_describesobjectresult.htm
|
85
85
|
def valid?(picklist_entry)
|
86
|
-
valid_for = picklist_entry['validFor'].ljust(16, 'A').
|
86
|
+
valid_for = picklist_entry['validFor'].ljust(16, 'A').unpack1('m').
|
87
87
|
unpack('C*')
|
88
88
|
(valid_for[index >> 3] & (0x80 >> index % 8)).positive?
|
89
89
|
end
|
@@ -5,13 +5,28 @@ module Restforce
|
|
5
5
|
module Streaming
|
6
6
|
# Public: Subscribe to a PushTopic
|
7
7
|
#
|
8
|
-
#
|
8
|
+
# topics - The name of the PushTopic channel(s) to subscribe to.
|
9
9
|
# block - A block to run when a new message is received.
|
10
10
|
#
|
11
11
|
# Returns a Faye::Subscription
|
12
|
-
def
|
13
|
-
Array(
|
14
|
-
|
12
|
+
def legacy_subscribe(topics, options = {}, &block)
|
13
|
+
topics = Array(topics).map { |channel| "/topic/#{channel}" }
|
14
|
+
subscription(topics, options, &block)
|
15
|
+
end
|
16
|
+
alias subscribe legacy_subscribe
|
17
|
+
|
18
|
+
# Public: Subscribe to one or more Streaming API channels
|
19
|
+
#
|
20
|
+
# channels - The name of the Streaming API (cometD) channel(s) to subscribe to.
|
21
|
+
# block - A block to run when a new message is received.
|
22
|
+
#
|
23
|
+
# Returns a Faye::Subscription
|
24
|
+
def subscription(channels, options = {}, &block)
|
25
|
+
one_or_more_channels = Array(channels)
|
26
|
+
one_or_more_channels.each do |channel|
|
27
|
+
replay_handlers[channel] = options[:replay]
|
28
|
+
end
|
29
|
+
faye.subscribe(one_or_more_channels, &block)
|
15
30
|
end
|
16
31
|
|
17
32
|
# Public: Faye client to use for subscribing to PushTopics
|
@@ -49,7 +64,7 @@ module Restforce
|
|
49
64
|
|
50
65
|
def incoming(message, callback)
|
51
66
|
callback.call(message).tap do
|
52
|
-
channel = message.fetch('channel')
|
67
|
+
channel = message.fetch('channel')
|
53
68
|
replay_id = message.fetch('data', {}).fetch('event', {})['replayId']
|
54
69
|
|
55
70
|
handler = @replay_handlers[channel]
|
@@ -64,12 +79,12 @@ module Restforce
|
|
64
79
|
# Leave non-subscribe messages alone
|
65
80
|
return callback.call(message) unless message['channel'] == '/meta/subscribe'
|
66
81
|
|
67
|
-
channel = message['subscription']
|
82
|
+
channel = message['subscription']
|
68
83
|
|
69
84
|
# Set the replay value for the channel
|
70
85
|
message['ext'] ||= {}
|
71
86
|
message['ext']['replay'] = {
|
72
|
-
|
87
|
+
channel => replay_id(channel)
|
73
88
|
}
|
74
89
|
|
75
90
|
# Carry on and send the message to the server
|
data/lib/restforce/config.rb
CHANGED
@@ -108,6 +108,9 @@ module Restforce
|
|
108
108
|
# The OAuth client secret
|
109
109
|
option :client_secret, default: lambda { ENV['SALESFORCE_CLIENT_SECRET'] }
|
110
110
|
|
111
|
+
# The private key for JWT authentication
|
112
|
+
option :jwt_key
|
113
|
+
|
111
114
|
# Set this to true if you're authenticating with a Sandbox instance.
|
112
115
|
# Defaults to false.
|
113
116
|
option :host, default: lambda { ENV['SALESFORCE_HOST'] || 'login.salesforce.com' }
|
data/lib/restforce/mash.rb
CHANGED
@@ -6,8 +6,9 @@ module Restforce
|
|
6
6
|
# will attempt to either reauthenticate (username and password) or refresh
|
7
7
|
# the oauth access token (if a refresh token is present).
|
8
8
|
class Middleware::Authentication < Restforce::Middleware
|
9
|
-
autoload :Password,
|
10
|
-
autoload :Token,
|
9
|
+
autoload :Password, 'restforce/middleware/authentication/password'
|
10
|
+
autoload :Token, 'restforce/middleware/authentication/token'
|
11
|
+
autoload :JWTBearer, 'restforce/middleware/authentication/jwt_bearer'
|
11
12
|
|
12
13
|
# Rescue from 401's, authenticate then raise the error again so the client
|
13
14
|
# can reissue the request.
|
@@ -62,7 +63,10 @@ module Restforce
|
|
62
63
|
|
63
64
|
# Internal: The parsed error response.
|
64
65
|
def error_message(response)
|
65
|
-
|
66
|
+
return response.status.to_s unless response.body
|
67
|
+
|
68
|
+
"#{response.body['error']}: #{response.body['error_description']} " \
|
69
|
+
"(#{response.status})"
|
66
70
|
end
|
67
71
|
|
68
72
|
# Featured detect form encoding.
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'jwt'
|
4
|
+
|
5
|
+
module Restforce
|
6
|
+
class Middleware
|
7
|
+
class Authentication
|
8
|
+
class JWTBearer < Restforce::Middleware::Authentication
|
9
|
+
def params
|
10
|
+
{
|
11
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
12
|
+
assertion: jwt_bearer_token
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def jwt_bearer_token
|
19
|
+
JWT.encode claim_set, private_key, 'RS256'
|
20
|
+
end
|
21
|
+
|
22
|
+
def claim_set
|
23
|
+
{
|
24
|
+
iss: @options[:client_id],
|
25
|
+
sub: @options[:username],
|
26
|
+
aud: @options[:host],
|
27
|
+
iat: Time.now.utc.to_i,
|
28
|
+
exp: Time.now.utc.to_i + 180
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def private_key
|
33
|
+
OpenSSL::PKey::RSA.new(@options[:jwt_key])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/restforce/version.rb
CHANGED
data/restforce.gemspec
CHANGED
@@ -22,19 +22,20 @@ Gem::Specification.new do |gem|
|
|
22
22
|
'changelog_uri' => 'https://github.com/restforce/restforce/blob/master/CHANGELOG.md'
|
23
23
|
}
|
24
24
|
|
25
|
-
gem.required_ruby_version = '>= 2.
|
25
|
+
gem.required_ruby_version = '>= 2.4'
|
26
26
|
|
27
27
|
gem.add_dependency 'faraday', '<= 1.0', '>= 0.9.0'
|
28
28
|
gem.add_dependency 'faraday_middleware', ['>= 0.8.8', '<= 1.0']
|
29
29
|
|
30
30
|
gem.add_dependency 'json', '>= 1.7.5'
|
31
|
+
gem.add_dependency 'jwt', ['>= 1.5.6']
|
31
32
|
|
32
|
-
gem.add_dependency 'hashie',
|
33
|
+
gem.add_dependency 'hashie', '>= 1.2.0', '< 5.0'
|
33
34
|
|
34
35
|
gem.add_development_dependency 'faye' unless RUBY_PLATFORM == 'java'
|
35
36
|
gem.add_development_dependency 'rspec', '~> 2.14.0'
|
36
|
-
gem.add_development_dependency 'rspec_junit_formatter', '~> 0.
|
37
|
-
gem.add_development_dependency 'rubocop', '~> 0.
|
37
|
+
gem.add_development_dependency 'rspec_junit_formatter', '~> 0.4.1'
|
38
|
+
gem.add_development_dependency 'rubocop', '~> 0.77.0'
|
38
39
|
gem.add_development_dependency 'simplecov', '~> 0.17.1'
|
39
40
|
gem.add_development_dependency 'webmock', '~> 3.7.6'
|
40
41
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEpAIBAAKCAQEAy3KYqxZIgVDgFwdA+OQcKMJQu3iUTlyCSk9b3RLBOudnvk8u
|
3
|
+
n0ShtKkOKB4b4RZeedcrlKESoak/6NS+M7CDemRT0EagqUiz/ZsZxB2KUp7au+d8
|
4
|
+
0KWX99/loBjDttuon8ITDw2WFC9X0+TZqfsXcQ0iV1/9Sf8WHShd8ZqShjJBlEvf
|
5
|
+
7u7VdNW8dXrl+4cvpPzspVxg6jVotEpmp875jmGRvshgx0iz0jtfAyxaaKStITC6
|
6
|
+
MxufVNDgIYQDl6queh8b9noDLtt17Eq6YnropYN1hOjaLtoLBP7AN2gsXG7N3vqC
|
7
|
+
JG619W9X4zCmKztv4oGjymInrS2msC2J02dNGQIDAQABAoIBAAurTARsJ8Z7DA9m
|
8
|
+
FBzygIb59kV6eg8wkSyP9rXscHbfdPzeb88k0Z2aILy+VV0IumyEofRJdNce7RJ+
|
9
|
+
uVYfprrrbD9C/c4X5HMEZWrxQtDQWb1zXp5dESVfiz0ujnM7kCVxrUQsxFHuETyP
|
10
|
+
IMj2JPcQCMs4L0ACSJNtkE3eTs8xko5kwDHZGiLTi5jD1bLgaHl1A+9CTU8LosTy
|
11
|
+
hEIrNSZfNidDPU4QSbwoElYZxpDMSbtyHaIk1WHz7zLzWoogK3x5AIQh64wWAQVd
|
12
|
+
zzlp2j2jSM7oQ9j+k1aNiUBdDoRX53jmaIwE/1WDW/LT33qAoqRw+5qHeLRoRcfu
|
13
|
+
3uj/WI0CgYEA6wnpIUhqqWT+febhXtCr1mAJlAJpzUUQncN6Zk0Kj/kE1V52OqgL
|
14
|
+
gtOactII7J3+0zK7KGptqseTank0ghmGNdRBQ7+1JTQhpjLrCm/huKDhl+sBk95u
|
15
|
+
opxw/ZTwMFYPwsmZlFcy4uWRjtI+QzaV+2Xk5JF57H8vUiX/+XqseQcCgYEA3Zdw
|
16
|
+
zVHpcVPlyiXCbSvwb9IYXiJaQl/Rg96Klxah3MZNyRRKe5IoKUTJwEDuQ1MAHrve
|
17
|
+
cWrNLcXhX6r/PzIXSSLe71wgwpn7UcaqWzZJqqN7OIGEeTzYWbB6tGhse7Dw7tWB
|
18
|
+
hRkQSE0LPzZqboHz5msRM02sa61qiI5+ASJvIN8CgYEAvT+IoEzv3R89ruBVPQPm
|
19
|
+
KMHBVJSw3iArJex8xJxp0c0fMDJUHhyq0BdTd/pYRzVcNm/VtNAlJ2p07zlSpyKo
|
20
|
+
JvWV61gUIjWclnbPO+MkK4YWvzzxUz+5c2NlszjWQQU6wYuUBpZDmeBg2E++5F2y
|
21
|
+
W+8KY2QjeOJbltiUCCvXbccCgYEAqARYB5aARumyZqBS16xlVqQazeWGQqWcmzx2
|
22
|
+
ITGL8XZ7LGgyQZgE06XQw/F3t5yLjsIsXBr7ECXmST/C4gv9E/tYxm04edV/dfYI
|
23
|
+
3bhACx6CI8owxCyabwcdQwWam/8B8FX7KwxiCDBCwt9ju/7VDHVKSXgvsEWBbaF9
|
24
|
+
cSbG1EkCgYBZFztTUnD/cLMcvLUegN0K+6Qa3x3nRSrlrJ+v51mU1X8G8qNyFO67
|
25
|
+
gUq9h4xbCl4Z5ZTuFKXwPM4XaMzfYdrWNS2zl5IG14FXS077GhDKe062b9mFoxtm
|
26
|
+
aViCit4Hm8xpLTS8x9KB7yYAiF9sR/GklW1SUCIqnpL9JShkhzjfZw==
|
27
|
+
-----END RSA PRIVATE KEY-----
|
@@ -125,7 +125,7 @@ shared_examples_for Restforce::AbstractClient do
|
|
125
125
|
|
126
126
|
it {
|
127
127
|
should raise_error(
|
128
|
-
Faraday::
|
128
|
+
Faraday::ResourceNotFound,
|
129
129
|
"#{error.first['errorCode']}: #{error.first['message']}"
|
130
130
|
)
|
131
131
|
}
|
@@ -239,7 +239,7 @@ shared_examples_for Restforce::AbstractClient do
|
|
239
239
|
status: 404
|
240
240
|
|
241
241
|
subject { lambda { destroy! } }
|
242
|
-
it { should raise_error Faraday::
|
242
|
+
it { should raise_error Faraday::ResourceNotFound }
|
243
243
|
end
|
244
244
|
|
245
245
|
context 'with success' do
|
@@ -274,7 +274,7 @@ describe Restforce::Concerns::API do
|
|
274
274
|
end
|
275
275
|
|
276
276
|
it 'rescues exceptions' do
|
277
|
-
[Faraday::
|
277
|
+
[Faraday::ClientError].each do |exception_klass|
|
278
278
|
client.should_receive(:"#{method}!").
|
279
279
|
with(*args).
|
280
280
|
and_raise(exception_klass.new(nil))
|
@@ -52,6 +52,16 @@ describe Restforce::Concerns::Authentication do
|
|
52
52
|
|
53
53
|
it { should eq Restforce::Middleware::Authentication::Token }
|
54
54
|
end
|
55
|
+
|
56
|
+
context 'when jwt option is provided' do
|
57
|
+
before do
|
58
|
+
client.stub username_password?: false
|
59
|
+
client.stub oauth_refresh?: false
|
60
|
+
client.stub jwt?: true
|
61
|
+
end
|
62
|
+
|
63
|
+
it { should eq Restforce::Middleware::Authentication::JWTBearer }
|
64
|
+
end
|
55
65
|
end
|
56
66
|
|
57
67
|
describe '.username_password?' do
|
@@ -100,4 +110,29 @@ describe Restforce::Concerns::Authentication do
|
|
100
110
|
it { should_not be_true }
|
101
111
|
end
|
102
112
|
end
|
113
|
+
|
114
|
+
describe '.jwt?' do
|
115
|
+
subject { client.jwt? }
|
116
|
+
let(:options) do
|
117
|
+
{}
|
118
|
+
end
|
119
|
+
|
120
|
+
before do
|
121
|
+
client.stub options: options
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'when jwt options are provided' do
|
125
|
+
let(:options) do
|
126
|
+
{ jwt_key: 'JWT_PRIVATE_KEY',
|
127
|
+
username: 'foo',
|
128
|
+
client_id: 'client' }
|
129
|
+
end
|
130
|
+
|
131
|
+
it { should be_true }
|
132
|
+
end
|
133
|
+
|
134
|
+
context 'when jwt options are not provided' do
|
135
|
+
it { should_not be_true }
|
136
|
+
end
|
137
|
+
end
|
103
138
|
end
|
@@ -4,18 +4,19 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
describe Restforce::Concerns::Streaming, event_machine: true do
|
6
6
|
describe '.subscribe' do
|
7
|
-
let(:channels)
|
8
|
-
|
7
|
+
let(:channels) do
|
8
|
+
['/topic/topic1', '/event/MyCustomEvent__e', '/data/ChangeEvents']
|
9
|
+
end
|
9
10
|
let(:subscribe_block) { lambda { 'subscribe' } }
|
10
11
|
let(:faye_double) { double('Faye') }
|
11
12
|
|
12
13
|
it 'subscribes to the topics with faye' do
|
13
14
|
faye_double.
|
14
15
|
should_receive(:subscribe).
|
15
|
-
with(
|
16
|
+
with(channels, &subscribe_block)
|
16
17
|
client.stub faye: faye_double
|
17
18
|
|
18
|
-
client.
|
19
|
+
client.subscription(channels, &subscribe_block)
|
19
20
|
end
|
20
21
|
|
21
22
|
context "replay_handlers" do
|
@@ -25,24 +26,51 @@ describe Restforce::Concerns::Streaming, event_machine: true do
|
|
25
26
|
}
|
26
27
|
|
27
28
|
it 'registers nil handlers when no replay option is given' do
|
28
|
-
client.
|
29
|
-
client.replay_handlers.should eq(
|
29
|
+
client.subscription(channels, &subscribe_block)
|
30
|
+
client.replay_handlers.should eq(
|
31
|
+
'/topic/topic1' => nil,
|
32
|
+
'/event/MyCustomEvent__e' => nil,
|
33
|
+
'/data/ChangeEvents' => nil
|
34
|
+
)
|
30
35
|
end
|
31
36
|
|
32
37
|
it 'registers a replay_handler for each channel given' do
|
33
|
-
client.
|
34
|
-
client.replay_handlers.should eq(
|
38
|
+
client.subscription(channels, replay: -2, &subscribe_block)
|
39
|
+
client.replay_handlers.should eq(
|
40
|
+
'/topic/topic1' => -2,
|
41
|
+
'/event/MyCustomEvent__e' => -2,
|
42
|
+
'/data/ChangeEvents' => -2
|
43
|
+
)
|
35
44
|
end
|
36
45
|
|
37
46
|
it 'replaces earlier handlers in subsequent calls' do
|
38
|
-
client.
|
39
|
-
|
47
|
+
client.subscription(
|
48
|
+
['/topic/channel1', '/topic/channel2'],
|
49
|
+
replay: 2,
|
50
|
+
&subscribe_block
|
51
|
+
)
|
52
|
+
client.subscription(
|
53
|
+
['/topic/channel2', '/topic/channel3'],
|
54
|
+
replay: 3,
|
55
|
+
&subscribe_block
|
56
|
+
)
|
57
|
+
|
40
58
|
client.replay_handlers.should eq(
|
41
|
-
'channel1' => 2,
|
42
|
-
'channel2' => 3,
|
43
|
-
'channel3' => 3
|
59
|
+
'/topic/channel1' => 2,
|
60
|
+
'/topic/channel2' => 3,
|
61
|
+
'/topic/channel3' => 3
|
44
62
|
)
|
45
63
|
end
|
64
|
+
|
65
|
+
context 'backwards compatibility' do
|
66
|
+
it 'it assumes channels are push topics' do
|
67
|
+
client.subscribe(%w[channel1 channel2], replay: -2, &subscribe_block)
|
68
|
+
client.replay_handlers.should eq(
|
69
|
+
'/topic/channel1' => -2,
|
70
|
+
'/topic/channel2' => -2
|
71
|
+
)
|
72
|
+
end
|
73
|
+
end
|
46
74
|
end
|
47
75
|
end
|
48
76
|
|
@@ -87,41 +115,41 @@ describe Restforce::Concerns::Streaming, event_machine: true do
|
|
87
115
|
let(:extension) { Restforce::Concerns::Streaming::ReplayExtension.new(handlers) }
|
88
116
|
|
89
117
|
it 'sends nil without a specified handler' do
|
90
|
-
output = subscribe(extension, to: "channel1")
|
118
|
+
output = subscribe(extension, to: "/topic/channel1")
|
91
119
|
read_replay(output).should eq('/topic/channel1' => nil)
|
92
120
|
end
|
93
121
|
|
94
122
|
it 'with a scalar replay id' do
|
95
|
-
handlers['channel1'] = -2
|
96
|
-
output = subscribe(extension, to: "channel1")
|
123
|
+
handlers['/topic/channel1'] = -2
|
124
|
+
output = subscribe(extension, to: "/topic/channel1")
|
97
125
|
read_replay(output).should eq('/topic/channel1' => -2)
|
98
126
|
end
|
99
127
|
|
100
128
|
it 'with a hash' do
|
101
|
-
hash_handler = { 'channel1' => -1, 'channel2' => -2 }
|
129
|
+
hash_handler = { '/topic/channel1' => -1, '/topic/channel2' => -2 }
|
102
130
|
|
103
|
-
handlers['channel1'] = hash_handler
|
104
|
-
handlers['channel2'] = hash_handler
|
131
|
+
handlers['/topic/channel1'] = hash_handler
|
132
|
+
handlers['/topic/channel2'] = hash_handler
|
105
133
|
|
106
|
-
output = subscribe(extension, to: "channel1")
|
134
|
+
output = subscribe(extension, to: "/topic/channel1")
|
107
135
|
read_replay(output).should eq('/topic/channel1' => -1)
|
108
136
|
|
109
|
-
output = subscribe(extension, to: "channel2")
|
137
|
+
output = subscribe(extension, to: "/topic/channel2")
|
110
138
|
read_replay(output).should eq('/topic/channel2' => -2)
|
111
139
|
end
|
112
140
|
|
113
141
|
it 'with an object' do
|
114
142
|
custom_handler = double('custom_handler')
|
115
143
|
custom_handler.should_receive(:[]).and_return(123)
|
116
|
-
handlers['channel1'] = custom_handler
|
144
|
+
handlers['/topic/channel1'] = custom_handler
|
117
145
|
|
118
|
-
output = subscribe(extension, to: "channel1")
|
146
|
+
output = subscribe(extension, to: "/topic/channel1")
|
119
147
|
read_replay(output).should eq('/topic/channel1' => 123)
|
120
148
|
end
|
121
149
|
|
122
150
|
it 'remembers the last replayId' do
|
123
|
-
handler = { 'channel1' => 41 }
|
124
|
-
handlers['channel1'] = handler
|
151
|
+
handler = { '/topic/channel1' => 41 }
|
152
|
+
handlers['/topic/channel1'] = handler
|
125
153
|
message = {
|
126
154
|
'channel' => '/topic/channel1',
|
127
155
|
'data' => {
|
@@ -130,12 +158,12 @@ describe Restforce::Concerns::Streaming, event_machine: true do
|
|
130
158
|
}
|
131
159
|
|
132
160
|
extension.incoming(message, ->(m) {})
|
133
|
-
handler.should eq('channel1' => 42)
|
161
|
+
handler.should eq('/topic/channel1' => 42)
|
134
162
|
end
|
135
163
|
|
136
164
|
it 'when an incoming message has no replayId' do
|
137
|
-
handler = { 'channel1' => 41 }
|
138
|
-
handlers['channel1'] = handler
|
165
|
+
handler = { '/topic/channel1' => 41 }
|
166
|
+
handlers['/topic/channel1'] = handler
|
139
167
|
|
140
168
|
message = {
|
141
169
|
'channel' => '/topic/channel1',
|
@@ -143,7 +171,7 @@ describe Restforce::Concerns::Streaming, event_machine: true do
|
|
143
171
|
}
|
144
172
|
|
145
173
|
extension.incoming(message, ->(m) {})
|
146
|
-
handler.should eq('channel1' => 41)
|
174
|
+
handler.should eq('/topic/channel1' => 41)
|
147
175
|
end
|
148
176
|
|
149
177
|
private
|
@@ -152,7 +180,7 @@ describe Restforce::Concerns::Streaming, event_machine: true do
|
|
152
180
|
output = nil
|
153
181
|
message = {
|
154
182
|
'channel' => '/meta/subscribe',
|
155
|
-
'subscription' =>
|
183
|
+
'subscription' => options[:to]
|
156
184
|
}
|
157
185
|
extension.outgoing(message, ->(m) {
|
158
186
|
output = m
|
data/spec/unit/mash_spec.rb
CHANGED
@@ -33,6 +33,11 @@ describe Restforce::Mash do
|
|
33
33
|
let(:input) { { 'attributes' => { 'type' => 'Document' } } }
|
34
34
|
it { should eq Restforce::Document }
|
35
35
|
end
|
36
|
+
|
37
|
+
context 'when the attributes value is nil' do
|
38
|
+
let(:input) { { 'attributes' => nil } }
|
39
|
+
it { should eq Restforce::SObject }
|
40
|
+
end
|
36
41
|
end
|
37
42
|
|
38
43
|
context 'else' do
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
describe Restforce::Middleware::Authentication::JWTBearer do
|
5
|
+
let(:jwt_key) { File.read('spec/fixtures/test_private.key') }
|
6
|
+
|
7
|
+
let(:options) do
|
8
|
+
{ host: 'login.salesforce.com',
|
9
|
+
client_id: 'client_id',
|
10
|
+
username: 'foo',
|
11
|
+
jwt_key: jwt_key,
|
12
|
+
instance_url: 'https://na1.salesforce.com',
|
13
|
+
adapter: :net_http }
|
14
|
+
end
|
15
|
+
|
16
|
+
it_behaves_like 'authentication middleware' do
|
17
|
+
let(:success_request) do
|
18
|
+
stub_login_request(
|
19
|
+
body: "grant_type=grant_type—urn:ietf:params:oauth:grant-type:jwt-bearer&" \
|
20
|
+
"assertion=abc1234567890"
|
21
|
+
).to_return(status: 200, body: fixture(:auth_success_response))
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:fail_request) do
|
25
|
+
stub_login_request(
|
26
|
+
body: "grant_type=grant_type—urn:ietf:params:oauth:grant-type:jwt-bearer&" \
|
27
|
+
"assertion=abc1234567890"
|
28
|
+
).to_return(status: 400, body: fixture(:refresh_error_response))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'allows jwt_key as string' do
|
33
|
+
let(:jwt_key) do
|
34
|
+
File.read('spec/fixtures/test_private.key')
|
35
|
+
end
|
36
|
+
|
37
|
+
let(:options) do
|
38
|
+
{ host: 'login.salesforce.com',
|
39
|
+
client_id: 'client_id',
|
40
|
+
username: 'foo',
|
41
|
+
jwt_key: jwt_key,
|
42
|
+
instance_url: 'https://na1.salesforce.com',
|
43
|
+
adapter: :net_http }
|
44
|
+
end
|
45
|
+
|
46
|
+
it_behaves_like 'authentication middleware' do
|
47
|
+
let(:success_request) do
|
48
|
+
stub_login_request(
|
49
|
+
body: "grant_type=grant_type—urn:ietf:params:oauth:grant-type:jwt-bearer&" \
|
50
|
+
"assertion=abc1234567890"
|
51
|
+
).to_return(status: 200, body: fixture(:auth_success_response))
|
52
|
+
end
|
53
|
+
|
54
|
+
let(:fail_request) do
|
55
|
+
stub_login_request(
|
56
|
+
body: "grant_type=grant_type—urn:ietf:params:oauth:grant-type:jwt-bearer&" \
|
57
|
+
"assertion=abc1234567890"
|
58
|
+
).to_return(status: 400, body: fixture(:refresh_error_response))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -89,4 +89,25 @@ describe Restforce::Middleware::Authentication do
|
|
89
89
|
connection.ssl[:version].should eq(:TLSv1_2)
|
90
90
|
end
|
91
91
|
end
|
92
|
+
|
93
|
+
describe '.error_message' do
|
94
|
+
context 'when response.body is present' do
|
95
|
+
let(:response) {
|
96
|
+
Faraday::Response.new(
|
97
|
+
body: { 'error' => 'error', 'error_description' => 'description' },
|
98
|
+
status: 401
|
99
|
+
)
|
100
|
+
}
|
101
|
+
|
102
|
+
subject { middleware.error_message(response) }
|
103
|
+
it { should eq "error: description (401)" }
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'when response.body is nil' do
|
107
|
+
let(:response) { Faraday::Response.new(status: 401) }
|
108
|
+
|
109
|
+
subject { middleware.error_message(response) }
|
110
|
+
it { should eq "401" }
|
111
|
+
end
|
112
|
+
end
|
92
113
|
end
|
@@ -18,8 +18,8 @@ describe Restforce::Middleware::RaiseError do
|
|
18
18
|
'INVALID_FIELD: error_message'
|
19
19
|
end
|
20
20
|
|
21
|
-
it 'raises an error that inherits from Faraday::
|
22
|
-
expect { on_complete }.to raise_error Faraday::
|
21
|
+
it 'raises an error that inherits from Faraday::ResourceNotFound' do
|
22
|
+
expect { on_complete }.to raise_error Faraday::ResourceNotFound
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
@@ -31,8 +31,8 @@ describe Restforce::Middleware::RaiseError do
|
|
31
31
|
/300: The external ID provided/
|
32
32
|
end
|
33
33
|
|
34
|
-
it 'raises an error that inherits from Faraday::
|
35
|
-
expect { on_complete }.to raise_error Faraday::
|
34
|
+
it 'raises an error that inherits from Faraday::ClientError' do
|
35
|
+
expect { on_complete }.to raise_error Faraday::ClientError
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -44,8 +44,8 @@ describe Restforce::Middleware::RaiseError do
|
|
44
44
|
'INVALID_FIELD: error_message'
|
45
45
|
end
|
46
46
|
|
47
|
-
it 'raises an error that inherits from Faraday::
|
48
|
-
expect { on_complete }.to raise_error Faraday::
|
47
|
+
it 'raises an error that inherits from Faraday::ClientError' do
|
48
|
+
expect { on_complete }.to raise_error Faraday::ClientError
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -66,8 +66,8 @@ describe Restforce::Middleware::RaiseError do
|
|
66
66
|
'413: Request Entity Too Large'
|
67
67
|
end
|
68
68
|
|
69
|
-
it 'raises an error that inherits from Faraday::
|
70
|
-
expect { on_complete }.to raise_error Faraday::
|
69
|
+
it 'raises an error that inherits from Faraday::ClientError' do
|
70
|
+
expect { on_complete }.to raise_error Faraday::ClientError
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
@@ -80,8 +80,8 @@ describe Restforce::Middleware::RaiseError do
|
|
80
80
|
"(error code missing): #{body}"
|
81
81
|
end
|
82
82
|
|
83
|
-
it 'raises an error that inherits from Faraday::
|
84
|
-
expect { on_complete }.to raise_error Faraday::
|
83
|
+
it 'raises an error that inherits from Faraday::ClientError' do
|
84
|
+
expect { on_complete }.to raise_error Faraday::ClientError,
|
85
85
|
"(error code missing): #{body}"
|
86
86
|
end
|
87
87
|
end
|
data/spec/unit/sobject_spec.rb
CHANGED
@@ -45,13 +45,10 @@ describe Restforce::SObject do
|
|
45
45
|
destroy: :destroy,
|
46
46
|
destroy!: :destroy! }.each do |method, receiver|
|
47
47
|
describe ".#{method}" do
|
48
|
-
subject(:send_method) { sobject.send(method) }
|
48
|
+
subject(:send_method) { lambda { sobject.send(method) } }
|
49
49
|
|
50
50
|
context 'when an Id was not queried' do
|
51
|
-
it
|
52
|
-
expect { send_method }.to raise_error ArgumentError,
|
53
|
-
/need to query the Id for the record/
|
54
|
-
end
|
51
|
+
it { should raise_error ArgumentError, /need to query the Id for the record/ }
|
55
52
|
end
|
56
53
|
|
57
54
|
context 'when an Id is present' do
|
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:
|
4
|
+
version: 4.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric J. Holmes
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2020-01-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: faraday
|
@@ -65,6 +65,20 @@ dependencies:
|
|
65
65
|
- - ">="
|
66
66
|
- !ruby/object:Gem::Version
|
67
67
|
version: 1.7.5
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: jwt
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 1.5.6
|
75
|
+
type: :runtime
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 1.5.6
|
68
82
|
- !ruby/object:Gem::Dependency
|
69
83
|
name: hashie
|
70
84
|
requirement: !ruby/object:Gem::Requirement
|
@@ -74,7 +88,7 @@ dependencies:
|
|
74
88
|
version: 1.2.0
|
75
89
|
- - "<"
|
76
90
|
- !ruby/object:Gem::Version
|
77
|
-
version: '
|
91
|
+
version: '5.0'
|
78
92
|
type: :runtime
|
79
93
|
prerelease: false
|
80
94
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -84,7 +98,7 @@ dependencies:
|
|
84
98
|
version: 1.2.0
|
85
99
|
- - "<"
|
86
100
|
- !ruby/object:Gem::Version
|
87
|
-
version: '
|
101
|
+
version: '5.0'
|
88
102
|
- !ruby/object:Gem::Dependency
|
89
103
|
name: faye
|
90
104
|
requirement: !ruby/object:Gem::Requirement
|
@@ -119,28 +133,28 @@ dependencies:
|
|
119
133
|
requirements:
|
120
134
|
- - "~>"
|
121
135
|
- !ruby/object:Gem::Version
|
122
|
-
version: 0.
|
136
|
+
version: 0.4.1
|
123
137
|
type: :development
|
124
138
|
prerelease: false
|
125
139
|
version_requirements: !ruby/object:Gem::Requirement
|
126
140
|
requirements:
|
127
141
|
- - "~>"
|
128
142
|
- !ruby/object:Gem::Version
|
129
|
-
version: 0.
|
143
|
+
version: 0.4.1
|
130
144
|
- !ruby/object:Gem::Dependency
|
131
145
|
name: rubocop
|
132
146
|
requirement: !ruby/object:Gem::Requirement
|
133
147
|
requirements:
|
134
148
|
- - "~>"
|
135
149
|
- !ruby/object:Gem::Version
|
136
|
-
version: 0.
|
150
|
+
version: 0.77.0
|
137
151
|
type: :development
|
138
152
|
prerelease: false
|
139
153
|
version_requirements: !ruby/object:Gem::Requirement
|
140
154
|
requirements:
|
141
155
|
- - "~>"
|
142
156
|
- !ruby/object:Gem::Version
|
143
|
-
version: 0.
|
157
|
+
version: 0.77.0
|
144
158
|
- !ruby/object:Gem::Dependency
|
145
159
|
name: simplecov
|
146
160
|
requirement: !ruby/object:Gem::Requirement
|
@@ -210,6 +224,7 @@ files:
|
|
210
224
|
- lib/restforce/mash.rb
|
211
225
|
- lib/restforce/middleware.rb
|
212
226
|
- lib/restforce/middleware/authentication.rb
|
227
|
+
- lib/restforce/middleware/authentication/jwt_bearer.rb
|
213
228
|
- lib/restforce/middleware/authentication/password.rb
|
214
229
|
- lib/restforce/middleware/authentication/token.rb
|
215
230
|
- lib/restforce/middleware/authorization.rb
|
@@ -266,6 +281,7 @@ files:
|
|
266
281
|
- spec/fixtures/sobject/upsert_multiple_error_response.json
|
267
282
|
- spec/fixtures/sobject/upsert_updated_success_response.json
|
268
283
|
- spec/fixtures/sobject/write_error_response.json
|
284
|
+
- spec/fixtures/test_private.key
|
269
285
|
- spec/integration/abstract_client_spec.rb
|
270
286
|
- spec/integration/data/client_spec.rb
|
271
287
|
- spec/spec_helper.rb
|
@@ -291,6 +307,7 @@ files:
|
|
291
307
|
- spec/unit/data/client_spec.rb
|
292
308
|
- spec/unit/document_spec.rb
|
293
309
|
- spec/unit/mash_spec.rb
|
310
|
+
- spec/unit/middleware/authentication/jwt_bearer_spec.rb
|
294
311
|
- spec/unit/middleware/authentication/password_spec.rb
|
295
312
|
- spec/unit/middleware/authentication/token_spec.rb
|
296
313
|
- spec/unit/middleware/authentication_spec.rb
|
@@ -318,7 +335,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
318
335
|
requirements:
|
319
336
|
- - ">="
|
320
337
|
- !ruby/object:Gem::Version
|
321
|
-
version: '2.
|
338
|
+
version: '2.4'
|
322
339
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
323
340
|
requirements:
|
324
341
|
- - ">="
|
@@ -364,6 +381,7 @@ test_files:
|
|
364
381
|
- spec/fixtures/sobject/upsert_multiple_error_response.json
|
365
382
|
- spec/fixtures/sobject/upsert_updated_success_response.json
|
366
383
|
- spec/fixtures/sobject/write_error_response.json
|
384
|
+
- spec/fixtures/test_private.key
|
367
385
|
- spec/integration/abstract_client_spec.rb
|
368
386
|
- spec/integration/data/client_spec.rb
|
369
387
|
- spec/spec_helper.rb
|
@@ -389,6 +407,7 @@ test_files:
|
|
389
407
|
- spec/unit/data/client_spec.rb
|
390
408
|
- spec/unit/document_spec.rb
|
391
409
|
- spec/unit/mash_spec.rb
|
410
|
+
- spec/unit/middleware/authentication/jwt_bearer_spec.rb
|
392
411
|
- spec/unit/middleware/authentication/password_spec.rb
|
393
412
|
- spec/unit/middleware/authentication/token_spec.rb
|
394
413
|
- spec/unit/middleware/authentication_spec.rb
|