restforce 6.0.0 → 8.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +3 -3
- data/.github/workflows/faraday.yml +31 -3
- data/.gitignore +4 -0
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +74 -0
- data/Gemfile +8 -8
- data/README.md +41 -23
- data/UPGRADING.md +24 -0
- data/lib/restforce/collection.rb +2 -2
- data/lib/restforce/concerns/authentication.rb +8 -0
- data/lib/restforce/concerns/base.rb +5 -0
- data/lib/restforce/concerns/batch_api.rb +2 -2
- data/lib/restforce/concerns/composite_api.rb +9 -1
- data/lib/restforce/concerns/connection.rb +10 -3
- data/lib/restforce/concerns/picklists.rb +1 -1
- data/lib/restforce/concerns/streaming.rb +4 -4
- data/lib/restforce/config.rb +6 -2
- data/lib/restforce/error_code.rb +23 -1
- data/lib/restforce/file_part.rb +3 -1
- data/lib/restforce/mash.rb +2 -2
- data/lib/restforce/middleware/authentication/client_credential.rb +15 -0
- data/lib/restforce/middleware/authentication.rb +2 -1
- data/lib/restforce/middleware/gzip.rb +5 -0
- data/lib/restforce/middleware/json_request.rb +2 -2
- data/lib/restforce/middleware/json_response.rb +1 -1
- data/lib/restforce/middleware/raise_error.rb +1 -1
- data/lib/restforce/patches/parts.rb +2 -0
- data/lib/restforce/version.rb +1 -1
- data/lib/restforce.rb +1 -1
- data/restforce.gemspec +4 -4
- data/spec/integration/abstract_client_spec.rb +6 -3
- data/spec/unit/concerns/authentication_spec.rb +33 -0
- data/spec/unit/concerns/base_spec.rb +1 -1
- data/spec/unit/concerns/composite_api_spec.rb +43 -0
- data/spec/unit/concerns/connection_spec.rb +3 -1
- data/spec/unit/config_spec.rb +28 -1
- data/spec/unit/middleware/authentication/client_credential_spec.rb +36 -0
- data/spec/unit/middleware/authentication_spec.rb +3 -3
- 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 -14
- data/spec/unit/middleware/instance_url_spec.rb +2 -2
- data/spec/unit/middleware/logger_spec.rb +1 -1
- data/spec/unit/sobject_spec.rb +9 -5
- metadata +12 -19
data/lib/restforce/error_code.rb
CHANGED
|
@@ -19,6 +19,8 @@ module Restforce
|
|
|
19
19
|
|
|
20
20
|
class ApexError < ResponseError; end
|
|
21
21
|
|
|
22
|
+
class ApexRestServicesDisabled < ResponseError; end
|
|
23
|
+
|
|
22
24
|
class ApiCurrentlyDisabled < ResponseError; end
|
|
23
25
|
|
|
24
26
|
class ApiDisabledForOrg < ResponseError; end
|
|
@@ -43,6 +45,8 @@ module Restforce
|
|
|
43
45
|
|
|
44
46
|
class CannotDeleteLastDatedConversionRate < ResponseError; end
|
|
45
47
|
|
|
48
|
+
class CannotDeleteOwner < ResponseError; end
|
|
49
|
+
|
|
46
50
|
class CannotDeleteManagedObject < ResponseError; end
|
|
47
51
|
|
|
48
52
|
class CannotDisableLastAdmin < ResponseError; end
|
|
@@ -55,6 +59,8 @@ module Restforce
|
|
|
55
59
|
|
|
56
60
|
class CannotModifyManagedObject < ResponseError; end
|
|
57
61
|
|
|
62
|
+
class CannotPostToArchivedGroup < ResponseError; end
|
|
63
|
+
|
|
58
64
|
class CannotRenameApexReferencedField < ResponseError; end
|
|
59
65
|
|
|
60
66
|
class CannotRenameApexReferencedObject < ResponseError; end
|
|
@@ -171,6 +177,8 @@ module Restforce
|
|
|
171
177
|
|
|
172
178
|
class InsertUpdateDeleteNotAllowedDuringMaintenance < ResponseError; end
|
|
173
179
|
|
|
180
|
+
class InsufficientAccess < ResponseError; end
|
|
181
|
+
|
|
174
182
|
class InsufficientAccessOnCrossReferenceEntity < ResponseError; end
|
|
175
183
|
|
|
176
184
|
class InsufficientAccessOrReadonly < ResponseError; end
|
|
@@ -217,8 +225,12 @@ module Restforce
|
|
|
217
225
|
|
|
218
226
|
class InvalidInetAddress < ResponseError; end
|
|
219
227
|
|
|
228
|
+
class InvalidInput < ResponseError; end
|
|
229
|
+
|
|
220
230
|
class InvalidLineitemCloneState < ResponseError; end
|
|
221
231
|
|
|
232
|
+
class InvalidMarkup < ResponseError; end
|
|
233
|
+
|
|
222
234
|
class InvalidMasterOrTranslatedSolution < ResponseError; end
|
|
223
235
|
|
|
224
236
|
class InvalidMessageIdReference < ResponseError; end
|
|
@@ -245,6 +257,8 @@ module Restforce
|
|
|
245
257
|
|
|
246
258
|
class InvalidSaveAsActivityFlag < ResponseError; end
|
|
247
259
|
|
|
260
|
+
class InvalidSearch < ResponseError; end
|
|
261
|
+
|
|
248
262
|
class InvalidSessionId < ResponseError; end
|
|
249
263
|
|
|
250
264
|
class InvalidSignupCountry < ResponseError; end
|
|
@@ -417,6 +431,7 @@ module Restforce
|
|
|
417
431
|
"ALL_OR_NONE_OPERATION_ROLLED_BACK" => AllOrNoneOperationRolledBack,
|
|
418
432
|
"ALREADY_IN_PROCESS" => AlreadyInProcess,
|
|
419
433
|
"APEX_ERROR" => ApexError,
|
|
434
|
+
"APEX_REST_SERVICES_DISABLED" => ApexRestServicesDisabled,
|
|
420
435
|
"API_CURRENTLY_DISABLED" => ApiCurrentlyDisabled,
|
|
421
436
|
"API_DISABLED_FOR_ORG" => ApiDisabledForOrg,
|
|
422
437
|
"ASSIGNEE_TYPE_REQUIRED" => AssigneeTypeRequired,
|
|
@@ -434,11 +449,13 @@ module Restforce
|
|
|
434
449
|
"CANNOT_DELETE_LAST_DATED_CONVERSION_RATE" =>
|
|
435
450
|
CannotDeleteLastDatedConversionRate,
|
|
436
451
|
"CANNOT_DELETE_MANAGED_OBJECT" => CannotDeleteManagedObject,
|
|
452
|
+
"CANNOT_DELETE_OWNER" => CannotDeleteOwner,
|
|
437
453
|
"CANNOT_DISABLE_LAST_ADMIN" => CannotDisableLastAdmin,
|
|
438
454
|
"CANNOT_ENABLE_IP_RESTRICT_REQUESTS" => CannotEnableIpRestrictRequests,
|
|
439
455
|
"CANNOT_EXECUTE_FLOW_TRIGGER" => CannotExecuteFlowTrigger,
|
|
440
456
|
"CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY" => CannotInsertUpdateActivateEntity,
|
|
441
457
|
"CANNOT_MODIFY_MANAGED_OBJECT" => CannotModifyManagedObject,
|
|
458
|
+
"CANNOT_POST_TO_ARCHIVED_GROUP" => CannotPostToArchivedGroup,
|
|
442
459
|
"CANNOT_RENAME_APEX_REFERENCED_FIELD" => CannotRenameApexReferencedField,
|
|
443
460
|
"CANNOT_RENAME_APEX_REFERENCED_OBJECT" => CannotRenameApexReferencedObject,
|
|
444
461
|
"CANNOT_REPARENT_RECORD" => CannotReparentRecord,
|
|
@@ -500,6 +517,8 @@ module Restforce
|
|
|
500
517
|
"INACTIVE_OWNER_OR_USER" => InactiveOwnerOrUser,
|
|
501
518
|
"INSERT_UPDATE_DELETE_NOT_ALLOWED_DURING_MAINTENANCE" =>
|
|
502
519
|
InsertUpdateDeleteNotAllowedDuringMaintenance,
|
|
520
|
+
"INSUFFICIENT_ACCESS" =>
|
|
521
|
+
InsufficientAccess,
|
|
503
522
|
"INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY" =>
|
|
504
523
|
InsufficientAccessOnCrossReferenceEntity,
|
|
505
524
|
"INSUFFICIENT_ACCESS_OR_READONLY" => InsufficientAccessOrReadonly,
|
|
@@ -524,7 +543,9 @@ module Restforce
|
|
|
524
543
|
"INVALID_FILTER_ACTION" => InvalidFilterAction,
|
|
525
544
|
"INVALID_ID_FIELD" => InvalidIdField,
|
|
526
545
|
"INVALID_INET_ADDRESS" => InvalidInetAddress,
|
|
546
|
+
"INVALID_INPUT" => InvalidInput,
|
|
527
547
|
"INVALID_LINEITEM_CLONE_STATE" => InvalidLineitemCloneState,
|
|
548
|
+
"INVALID_MARKUP" => InvalidMarkup,
|
|
528
549
|
"INVALID_MASTER_OR_TRANSLATED_SOLUTION" => InvalidMasterOrTranslatedSolution,
|
|
529
550
|
"INVALID_MESSAGE_ID_REFERENCE" => InvalidMessageIdReference,
|
|
530
551
|
"INVALID_OPERATION" => InvalidOperation,
|
|
@@ -539,6 +560,7 @@ module Restforce
|
|
|
539
560
|
"INVALID_READ_ONLY_USER_DML" => InvalidReadOnlyUserDml,
|
|
540
561
|
"INVALID_REPLICATION_DATE" => InvalidReplicationDate,
|
|
541
562
|
"INVALID_SAVE_AS_ACTIVITY_FLAG" => InvalidSaveAsActivityFlag,
|
|
563
|
+
"INVALID_SEARCH" => InvalidSearch,
|
|
542
564
|
"INVALID_SESSION_ID" => InvalidSessionId,
|
|
543
565
|
"INVALID_SIGNUP_COUNTRY" => InvalidSignupCountry,
|
|
544
566
|
"INVALID_STATUS" => InvalidStatus,
|
|
@@ -644,7 +666,7 @@ module Restforce
|
|
|
644
666
|
"defined. If you're sure that this is a valid Salesforce error, then " \
|
|
645
667
|
"please create an issue on GitHub at <#{GITHUB_ISSUE_URL}>."
|
|
646
668
|
|
|
647
|
-
super
|
|
669
|
+
super
|
|
648
670
|
end
|
|
649
671
|
end
|
|
650
672
|
end
|
data/lib/restforce/file_part.rb
CHANGED
|
@@ -29,4 +29,6 @@ end
|
|
|
29
29
|
|
|
30
30
|
# This patch is only needed with multipart-post < 2.0.0
|
|
31
31
|
# 2.0.0 was released in 2013.
|
|
32
|
-
|
|
32
|
+
if defined?(Parts) && Parts::Part.method(:new).arity.abs != 4
|
|
33
|
+
require 'restforce/patches/parts'
|
|
34
|
+
end
|
data/lib/restforce/mash.rb
CHANGED
|
@@ -46,10 +46,10 @@ module Restforce
|
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
def initialize(source_hash = nil, client = nil, default = nil, &
|
|
49
|
+
def initialize(source_hash = nil, client = nil, default = nil, &)
|
|
50
50
|
@client = client
|
|
51
51
|
deep_update(source_hash) if source_hash
|
|
52
|
-
default ? super(default) : super(&
|
|
52
|
+
default ? super(default) : super(&)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def dup
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Restforce
|
|
4
|
+
class Middleware
|
|
5
|
+
class Authentication
|
|
6
|
+
class ClientCredential < Restforce::Middleware::Authentication
|
|
7
|
+
def params
|
|
8
|
+
{ grant_type: 'client_credentials',
|
|
9
|
+
client_id: @options[:client_id],
|
|
10
|
+
client_secret: @options[:client_secret] }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -9,6 +9,7 @@ module Restforce
|
|
|
9
9
|
autoload :Password, 'restforce/middleware/authentication/password'
|
|
10
10
|
autoload :Token, 'restforce/middleware/authentication/token'
|
|
11
11
|
autoload :JWTBearer, 'restforce/middleware/authentication/jwt_bearer'
|
|
12
|
+
autoload :ClientCredential, 'restforce/middleware/authentication/client_credential'
|
|
12
13
|
|
|
13
14
|
# Rescue from 401's, authenticate then raise the error again so the client
|
|
14
15
|
# can reissue the request.
|
|
@@ -50,7 +51,7 @@ module Restforce
|
|
|
50
51
|
builder.use Faraday::Request::UrlEncoded
|
|
51
52
|
builder.use Restforce::Middleware::Mashify, nil, @options
|
|
52
53
|
builder.use Faraday::FollowRedirects::Middleware
|
|
53
|
-
builder.response :
|
|
54
|
+
builder.response :restforce_json
|
|
54
55
|
|
|
55
56
|
if Restforce.log?
|
|
56
57
|
builder.use Restforce::Middleware::Logger,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'stringio'
|
|
3
4
|
require 'zlib'
|
|
4
5
|
|
|
5
6
|
module Restforce
|
|
@@ -28,6 +29,10 @@ module Restforce
|
|
|
28
29
|
# Internal: Decompresses a gzipped string.
|
|
29
30
|
def decompress(body)
|
|
30
31
|
Zlib::GzipReader.new(StringIO.new(body)).read
|
|
32
|
+
rescue Zlib::GzipFile::Error
|
|
33
|
+
# We thought the response was gzipped, but it wasn't. Return the original
|
|
34
|
+
# body back to the caller. See https://github.com/restforce/restforce/issues/761.
|
|
35
|
+
body
|
|
31
36
|
end
|
|
32
37
|
end
|
|
33
38
|
end
|
|
@@ -31,7 +31,7 @@ module Restforce
|
|
|
31
31
|
CONTENT_TYPE = 'Content-Type'
|
|
32
32
|
|
|
33
33
|
MIME_TYPE = 'application/json'
|
|
34
|
-
MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}
|
|
34
|
+
MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}
|
|
35
35
|
|
|
36
36
|
#
|
|
37
37
|
# Taken from `lib/faraday/middleware.rb` in the `faraday`
|
|
@@ -87,4 +87,4 @@ module Restforce
|
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
Faraday::Request.register_middleware(
|
|
90
|
+
Faraday::Request.register_middleware(restforce_json: Restforce::Middleware::JsonRequest)
|
data/lib/restforce/version.rb
CHANGED
data/lib/restforce.rb
CHANGED
|
@@ -49,7 +49,6 @@ module Restforce
|
|
|
49
49
|
UnauthorizedError = Class.new(Faraday::ClientError)
|
|
50
50
|
APIVersionError = Class.new(Error)
|
|
51
51
|
BatchAPIError = Class.new(Error)
|
|
52
|
-
CompositeAPIError = Class.new(Error)
|
|
53
52
|
|
|
54
53
|
# Inherit from Faraday::ResourceNotFound for backwards-compatibility
|
|
55
54
|
# Consumers of this library that rescue and handle Faraday::ResourceNotFound
|
|
@@ -60,6 +59,7 @@ module Restforce
|
|
|
60
59
|
# Consumers of this library that rescue and handle Faraday::ClientError
|
|
61
60
|
# can continue to do so.
|
|
62
61
|
ResponseError = Class.new(Faraday::ClientError)
|
|
62
|
+
CompositeAPIError = Class.new(ResponseError)
|
|
63
63
|
MatchesMultipleError= Class.new(ResponseError)
|
|
64
64
|
EntityTooLargeError = Class.new(ResponseError)
|
|
65
65
|
|
data/restforce.gemspec
CHANGED
|
@@ -22,12 +22,12 @@ Gem::Specification.new do |gem|
|
|
|
22
22
|
'rubygems_mfa_required' => 'true'
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
gem.required_ruby_version = '>=
|
|
25
|
+
gem.required_ruby_version = '>= 3.1'
|
|
26
26
|
|
|
27
|
-
gem.add_dependency 'faraday', '<
|
|
28
|
-
gem.add_dependency 'faraday-follow_redirects', '
|
|
27
|
+
gem.add_dependency 'faraday', '< 3.0.0', '>= 1.1.0'
|
|
28
|
+
gem.add_dependency 'faraday-follow_redirects', '< 0.6.0'
|
|
29
29
|
gem.add_dependency 'faraday-multipart', '>= 1.0.0', '< 2.0.0'
|
|
30
|
-
gem.add_dependency 'faraday-net_http', '<
|
|
30
|
+
gem.add_dependency 'faraday-net_http', '< 4.0.0'
|
|
31
31
|
gem.add_dependency 'hashie', '>= 1.2.0', '< 6.0'
|
|
32
32
|
gem.add_dependency 'jwt', ['>= 1.5.6']
|
|
33
33
|
end
|
|
@@ -144,7 +144,10 @@ shared_examples_for Restforce::AbstractClient do
|
|
|
144
144
|
describe '.update' do
|
|
145
145
|
context 'with missing Id' do
|
|
146
146
|
subject { lambda { client.update('Account', Name: 'Foobar') } }
|
|
147
|
-
it {
|
|
147
|
+
it {
|
|
148
|
+
expect { subject.call }.
|
|
149
|
+
to raise_error ArgumentError, 'ID field missing from provided attributes'
|
|
150
|
+
}
|
|
148
151
|
end
|
|
149
152
|
|
|
150
153
|
context 'with invalid Id' do
|
|
@@ -248,7 +251,7 @@ shared_examples_for Restforce::AbstractClient do
|
|
|
248
251
|
status: 404
|
|
249
252
|
|
|
250
253
|
subject { lambda { destroy! } }
|
|
251
|
-
it {
|
|
254
|
+
it { expect { subject.call }.to raise_error Faraday::ResourceNotFound }
|
|
252
255
|
end
|
|
253
256
|
|
|
254
257
|
context 'with success' do
|
|
@@ -431,7 +434,7 @@ shared_examples_for Restforce::AbstractClient do
|
|
|
431
434
|
end
|
|
432
435
|
|
|
433
436
|
subject { lambda { client.query('SELECT some, fields FROM object') } }
|
|
434
|
-
it {
|
|
437
|
+
it { expect { subject.call }.to raise_error Restforce::UnauthorizedError }
|
|
435
438
|
end
|
|
436
439
|
end
|
|
437
440
|
|
|
@@ -62,6 +62,17 @@ describe Restforce::Concerns::Authentication do
|
|
|
62
62
|
|
|
63
63
|
it { should eq Restforce::Middleware::Authentication::JWTBearer }
|
|
64
64
|
end
|
|
65
|
+
|
|
66
|
+
context 'when client_id and client_secret options are provided' do
|
|
67
|
+
before do
|
|
68
|
+
client.stub username_password?: false
|
|
69
|
+
client.stub oauth_refresh?: false
|
|
70
|
+
client.stub jwt?: false
|
|
71
|
+
client.stub client_credential?: true
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it { should eq Restforce::Middleware::Authentication::ClientCredential }
|
|
75
|
+
end
|
|
65
76
|
end
|
|
66
77
|
|
|
67
78
|
describe '.username_password?' do
|
|
@@ -135,4 +146,26 @@ describe Restforce::Concerns::Authentication do
|
|
|
135
146
|
it { should_not be true }
|
|
136
147
|
end
|
|
137
148
|
end
|
|
149
|
+
|
|
150
|
+
describe '.client_credential?' do
|
|
151
|
+
subject { client.client_credential? }
|
|
152
|
+
let(:options) { {} }
|
|
153
|
+
|
|
154
|
+
before do
|
|
155
|
+
client.stub options: options
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
context 'when oauth client credential options are provided' do
|
|
159
|
+
let(:options) do
|
|
160
|
+
{ client_id: 'client',
|
|
161
|
+
client_secret: 'secret' }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
it { should be_truthy }
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
context 'when oauth client credential options are not provided' do
|
|
168
|
+
it { should_not be true }
|
|
169
|
+
end
|
|
170
|
+
end
|
|
138
171
|
end
|
|
@@ -82,6 +82,23 @@ describe Restforce::Concerns::CompositeAPI do
|
|
|
82
82
|
end
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
+
it '#find' do
|
|
86
|
+
client.
|
|
87
|
+
should_receive(:api_post).
|
|
88
|
+
with(endpoint, { compositeRequest: [
|
|
89
|
+
{
|
|
90
|
+
method: 'GET',
|
|
91
|
+
url: "/services/data/v38.0/sobjects/Object/00312345",
|
|
92
|
+
referenceId: 'find_ref'
|
|
93
|
+
}
|
|
94
|
+
], allOrNone: all_or_none, collateSubrequests: false }.to_json).
|
|
95
|
+
and_return(response)
|
|
96
|
+
|
|
97
|
+
client.send(method) do |subrequests|
|
|
98
|
+
subrequests.find('Object', 'find_ref', '00312345')
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
85
102
|
it 'multiple subrequests' do
|
|
86
103
|
client.
|
|
87
104
|
should_receive(:api_post).
|
|
@@ -123,6 +140,32 @@ describe Restforce::Concerns::CompositeAPI do
|
|
|
123
140
|
end
|
|
124
141
|
end.to raise_error(ArgumentError)
|
|
125
142
|
end
|
|
143
|
+
|
|
144
|
+
it 'has response in CompositeAPIError' do
|
|
145
|
+
response = double('Faraday::Response',
|
|
146
|
+
body: { 'compositeResponse' =>
|
|
147
|
+
[{ 'httpStatusCode' => 400,
|
|
148
|
+
'body' => [{ 'errorCode' =>
|
|
149
|
+
'DUPLICATE_VALUE' }] }] })
|
|
150
|
+
client.
|
|
151
|
+
should_receive(:api_post).
|
|
152
|
+
with(endpoint, { compositeRequest: [
|
|
153
|
+
{
|
|
154
|
+
method: 'POST',
|
|
155
|
+
url: '/services/data/v38.0/sobjects/Object',
|
|
156
|
+
body: { name: 'test' },
|
|
157
|
+
referenceId: 'create_ref'
|
|
158
|
+
}
|
|
159
|
+
], allOrNone: true, collateSubrequests: false }.to_json).
|
|
160
|
+
and_return(response)
|
|
161
|
+
arg = method == :composite ? { all_or_none: true } : {}
|
|
162
|
+
expect do
|
|
163
|
+
client.send(method, **arg) do |subrequests|
|
|
164
|
+
subrequests.create('Object', 'create_ref', name: 'test')
|
|
165
|
+
end
|
|
166
|
+
end.to raise_error(an_instance_of(Restforce::CompositeAPIError).
|
|
167
|
+
and(having_attributes(response: response)))
|
|
168
|
+
end
|
|
126
169
|
end
|
|
127
170
|
|
|
128
171
|
describe '#composite' do
|
data/spec/unit/config_spec.rb
CHANGED
|
@@ -62,7 +62,7 @@ describe Restforce do
|
|
|
62
62
|
%i[username password security_token client_id client_secret compress
|
|
63
63
|
timeout oauth_token refresh_token instance_url api_version host mashify
|
|
64
64
|
authentication_retries proxy_uri authentication_callback ssl
|
|
65
|
-
request_headers log_level logger].each do |attr|
|
|
65
|
+
request_headers log_level logger clear_authorization_header].each do |attr|
|
|
66
66
|
it "allows #{attr} to be set" do
|
|
67
67
|
Restforce.configure do |config|
|
|
68
68
|
config.send("#{attr}=", 'foobar')
|
|
@@ -70,6 +70,21 @@ describe Restforce do
|
|
|
70
70
|
expect(Restforce.configuration.send(attr)).to eq 'foobar'
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
|
+
|
|
74
|
+
it 'takes precedence over environment variables' do
|
|
75
|
+
{ 'SALESFORCE_USERNAME' => 'env_foo',
|
|
76
|
+
'SALESFORCE_CLIENT_ID' => 'env_client id' }.
|
|
77
|
+
each { |var, value| ENV.stub(:fetch).with(var, anything).and_return(value) }
|
|
78
|
+
|
|
79
|
+
{ username: 'config_foo', client_id: nil }.each do |attr, value|
|
|
80
|
+
Restforce.configure do |config|
|
|
81
|
+
config.send("#{attr}=", value)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
expect(Restforce.configuration.send(:username)).to eq 'config_foo'
|
|
86
|
+
expect(Restforce.configuration.send(:client_id)).to eq 'env_client id'
|
|
87
|
+
end
|
|
73
88
|
end
|
|
74
89
|
|
|
75
90
|
describe '#log?' do
|
|
@@ -138,5 +153,17 @@ describe Restforce do
|
|
|
138
153
|
checker.check!
|
|
139
154
|
end
|
|
140
155
|
end
|
|
156
|
+
|
|
157
|
+
it 'shows the precedence over config' do
|
|
158
|
+
ENV['SALESFORCE_USERNAME'] = 'env_foo'
|
|
159
|
+
Restforce.configure do |config|
|
|
160
|
+
config.username = 'config_foo'
|
|
161
|
+
end
|
|
162
|
+
client = Restforce.new(username: 'foo')
|
|
163
|
+
|
|
164
|
+
expect(client.options[:username]).to eq 'foo'
|
|
165
|
+
|
|
166
|
+
Restforce.instance_variable_set :@configuration, nil
|
|
167
|
+
end
|
|
141
168
|
end
|
|
142
169
|
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Restforce::Middleware::Authentication::ClientCredential do
|
|
6
|
+
let(:options) do
|
|
7
|
+
{ host: 'login.salesforce.com',
|
|
8
|
+
client_id: 'client_id',
|
|
9
|
+
client_secret: 'client_secret',
|
|
10
|
+
adapter: :net_http }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it_behaves_like 'authentication middleware' do
|
|
14
|
+
let(:success_request) do
|
|
15
|
+
stub_login_request(
|
|
16
|
+
body: "grant_type=client_credentials&" \
|
|
17
|
+
"client_id=client_id&client_secret=client_secret"
|
|
18
|
+
).to_return(
|
|
19
|
+
status: 200,
|
|
20
|
+
body: fixture(:auth_success_response),
|
|
21
|
+
headers: { "Content-Type" => "application/json" }
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
let(:fail_request) do
|
|
26
|
+
stub_login_request(
|
|
27
|
+
body: "grant_type=client_credentials&" \
|
|
28
|
+
"client_id=client_id&client_secret=client_secret"
|
|
29
|
+
).to_return(
|
|
30
|
+
status: 400,
|
|
31
|
+
body: fixture(:refresh_error_response),
|
|
32
|
+
headers: { "Content-Type" => "application/json" }
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -15,7 +15,7 @@ describe Restforce::Middleware::Authentication do
|
|
|
15
15
|
|
|
16
16
|
describe '.authenticate!' do
|
|
17
17
|
subject { lambda { middleware.authenticate! } }
|
|
18
|
-
it {
|
|
18
|
+
it { expect { subject.call }.to raise_error NotImplementedError }
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
describe '.call' do
|
|
@@ -26,7 +26,7 @@ describe Restforce::Middleware::Authentication do
|
|
|
26
26
|
app.should_receive(:call).once
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
it {
|
|
29
|
+
it { expect { subject.call }.not_to raise_error }
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
context 'when an exception is thrown' do
|
|
@@ -37,7 +37,7 @@ describe Restforce::Middleware::Authentication do
|
|
|
37
37
|
and_raise(Restforce::UnauthorizedError.new('something bad'))
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
it {
|
|
40
|
+
it { expect { subject.call }.to raise_error Restforce::UnauthorizedError }
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
|
|
@@ -8,6 +8,10 @@ describe Restforce::Middleware::Authorization do
|
|
|
8
8
|
describe '.call' do
|
|
9
9
|
subject { lambda { middleware.call(env) } }
|
|
10
10
|
|
|
11
|
-
it {
|
|
11
|
+
it {
|
|
12
|
+
expect { subject.call }.to change {
|
|
13
|
+
env[:request_headers]['Authorization']
|
|
14
|
+
}.to eq 'OAuth token'
|
|
15
|
+
}
|
|
12
16
|
end
|
|
13
17
|
end
|
|
@@ -9,13 +9,17 @@ describe Restforce::Middleware::CustomHeaders do
|
|
|
9
9
|
context 'when :request_headers are a Hash' do
|
|
10
10
|
let(:options) { { request_headers: { 'x-test-header' => 'Test Value' } } }
|
|
11
11
|
|
|
12
|
-
it {
|
|
12
|
+
it {
|
|
13
|
+
expect { subject.call }.to change {
|
|
14
|
+
env[:request_headers]['x-test-header']
|
|
15
|
+
}.to eq 'Test Value'
|
|
16
|
+
}
|
|
13
17
|
end
|
|
14
18
|
|
|
15
19
|
context 'when :request_headers are not a Hash' do
|
|
16
20
|
let(:options) { { request_headers: 'bad header' } }
|
|
17
21
|
|
|
18
|
-
it {
|
|
22
|
+
it { expect { subject.call }.not_to(change { env[:request_headers] }) }
|
|
19
23
|
end
|
|
20
24
|
end
|
|
21
25
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'stringio'
|
|
4
|
+
|
|
3
5
|
require 'spec_helper'
|
|
4
6
|
|
|
5
7
|
describe Restforce::Middleware::Gzip do
|
|
@@ -17,29 +19,73 @@ describe Restforce::Middleware::Gzip do
|
|
|
17
19
|
describe '.call' do
|
|
18
20
|
subject { lambda { middleware.call(env) } }
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
context 'when the response is gzipped' do
|
|
23
|
+
before do
|
|
24
|
+
app.should_receive(:on_complete) { middleware.on_complete(env) }
|
|
25
|
+
app.should_receive(:call) do
|
|
26
|
+
env[:body] = gzip fixture('sobject/query_success_response')
|
|
27
|
+
env[:response_headers]['Content-Encoding'] = 'gzip'
|
|
28
|
+
app
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'decompresses the body' do
|
|
33
|
+
expect { subject.call }.to change {
|
|
34
|
+
env[:body]
|
|
35
|
+
}.to(fixture('sobject/query_success_response'))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
context 'when :compress is false' do
|
|
39
|
+
it 'does not set request headers to ask the response to be compressed' do
|
|
40
|
+
expect { subject.call }.
|
|
41
|
+
not_to(change { env[:request_headers]['Accept-Encoding'] })
|
|
42
|
+
end
|
|
26
43
|
end
|
|
27
|
-
end
|
|
28
44
|
|
|
29
|
-
|
|
30
|
-
|
|
45
|
+
context 'when :compress is true' do
|
|
46
|
+
before do
|
|
47
|
+
options[:compress] = true
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'sets request headers to ask the response to be compressed' do
|
|
51
|
+
expect { subject.call }.to change {
|
|
52
|
+
env[:request_headers]['Accept-Encoding']
|
|
53
|
+
}.to('gzip')
|
|
54
|
+
end
|
|
55
|
+
end
|
|
31
56
|
end
|
|
32
57
|
|
|
33
|
-
context 'when
|
|
34
|
-
|
|
58
|
+
context 'when the response claims to be gzipped, but is not' do
|
|
59
|
+
before do
|
|
60
|
+
app.should_receive(:on_complete) { middleware.on_complete(env) }
|
|
61
|
+
app.should_receive(:call) do
|
|
62
|
+
env[:body] = fixture('sobject/query_success_response')
|
|
63
|
+
env[:response_headers]['Content-Encoding'] = 'gzip'
|
|
64
|
+
app
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'does not decompress the body' do
|
|
69
|
+
expect { subject.call }.to change {
|
|
70
|
+
env[:body]
|
|
71
|
+
}.to(fixture('sobject/query_success_response'))
|
|
72
|
+
end
|
|
35
73
|
end
|
|
36
74
|
|
|
37
|
-
context 'when
|
|
75
|
+
context 'when the response does not even claim to be gzipped' do
|
|
38
76
|
before do
|
|
39
|
-
|
|
77
|
+
app.should_receive(:on_complete) { middleware.on_complete(env) }
|
|
78
|
+
app.should_receive(:call) do
|
|
79
|
+
env[:body] = fixture('sobject/query_success_response')
|
|
80
|
+
app
|
|
81
|
+
end
|
|
40
82
|
end
|
|
41
83
|
|
|
42
|
-
it
|
|
84
|
+
it 'does not decompress the body' do
|
|
85
|
+
expect { subject.call }.to change {
|
|
86
|
+
env[:body]
|
|
87
|
+
}.to(fixture('sobject/query_success_response'))
|
|
88
|
+
end
|
|
43
89
|
end
|
|
44
90
|
end
|
|
45
91
|
|