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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +3 -3
  3. data/.github/workflows/faraday.yml +31 -3
  4. data/.gitignore +4 -0
  5. data/.rubocop.yml +1 -1
  6. data/CHANGELOG.md +74 -0
  7. data/Gemfile +8 -8
  8. data/README.md +41 -23
  9. data/UPGRADING.md +24 -0
  10. data/lib/restforce/collection.rb +2 -2
  11. data/lib/restforce/concerns/authentication.rb +8 -0
  12. data/lib/restforce/concerns/base.rb +5 -0
  13. data/lib/restforce/concerns/batch_api.rb +2 -2
  14. data/lib/restforce/concerns/composite_api.rb +9 -1
  15. data/lib/restforce/concerns/connection.rb +10 -3
  16. data/lib/restforce/concerns/picklists.rb +1 -1
  17. data/lib/restforce/concerns/streaming.rb +4 -4
  18. data/lib/restforce/config.rb +6 -2
  19. data/lib/restforce/error_code.rb +23 -1
  20. data/lib/restforce/file_part.rb +3 -1
  21. data/lib/restforce/mash.rb +2 -2
  22. data/lib/restforce/middleware/authentication/client_credential.rb +15 -0
  23. data/lib/restforce/middleware/authentication.rb +2 -1
  24. data/lib/restforce/middleware/gzip.rb +5 -0
  25. data/lib/restforce/middleware/json_request.rb +2 -2
  26. data/lib/restforce/middleware/json_response.rb +1 -1
  27. data/lib/restforce/middleware/raise_error.rb +1 -1
  28. data/lib/restforce/patches/parts.rb +2 -0
  29. data/lib/restforce/version.rb +1 -1
  30. data/lib/restforce.rb +1 -1
  31. data/restforce.gemspec +4 -4
  32. data/spec/integration/abstract_client_spec.rb +6 -3
  33. data/spec/unit/concerns/authentication_spec.rb +33 -0
  34. data/spec/unit/concerns/base_spec.rb +1 -1
  35. data/spec/unit/concerns/composite_api_spec.rb +43 -0
  36. data/spec/unit/concerns/connection_spec.rb +3 -1
  37. data/spec/unit/config_spec.rb +28 -1
  38. data/spec/unit/middleware/authentication/client_credential_spec.rb +36 -0
  39. data/spec/unit/middleware/authentication_spec.rb +3 -3
  40. data/spec/unit/middleware/authorization_spec.rb +5 -1
  41. data/spec/unit/middleware/custom_headers_spec.rb +6 -2
  42. data/spec/unit/middleware/gzip_spec.rb +60 -14
  43. data/spec/unit/middleware/instance_url_spec.rb +2 -2
  44. data/spec/unit/middleware/logger_spec.rb +1 -1
  45. data/spec/unit/sobject_spec.rb +9 -5
  46. metadata +12 -19
@@ -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(constant_name)
669
+ super
648
670
  end
649
671
  end
650
672
  end
@@ -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
- require 'restforce/patches/parts' unless Parts::Part.method(:new).arity.abs == 4
32
+ if defined?(Parts) && Parts::Part.method(:new).arity.abs != 4
33
+ require 'restforce/patches/parts'
34
+ end
@@ -46,10 +46,10 @@ module Restforce
46
46
  end
47
47
  end
48
48
 
49
- def initialize(source_hash = nil, client = nil, default = nil, &blk)
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(&blk)
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 :json
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$}.freeze
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(json: Restforce::Middleware::JsonRequest)
90
+ Faraday::Request.register_middleware(restforce_json: Restforce::Middleware::JsonRequest)
@@ -82,4 +82,4 @@ module Restforce
82
82
  end
83
83
  end
84
84
 
85
- Faraday::Response.register_middleware(json: Restforce::Middleware::JsonResponse)
85
+ Faraday::Response.register_middleware(restforce_json: Restforce::Middleware::JsonResponse)
@@ -60,7 +60,7 @@ module Restforce
60
60
  }
61
61
  end
62
62
 
63
- ERROR_CODE_MATCHER = /\A[A-Z_]+\z/.freeze
63
+ ERROR_CODE_MATCHER = /\A[A-Z_]+\z/
64
64
 
65
65
  def exception_class_for_error_code(error_code)
66
66
  return Restforce::ResponseError unless ERROR_CODE_MATCHER.match?(error_code)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'stringio'
4
+
3
5
  module Faraday
4
6
  module Parts
5
7
  module Part
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Restforce
4
- VERSION = '6.0.0'
4
+ VERSION = '8.0.1'
5
5
  end
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 = '>= 2.7'
25
+ gem.required_ruby_version = '>= 3.1'
26
26
 
27
- gem.add_dependency 'faraday', '< 2.5.0', '>= 1.1.0'
28
- gem.add_dependency 'faraday-follow_redirects', '<= 0.3.0', '< 1.0.0'
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', '< 3.0.0'
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 { should raise_error ArgumentError, 'ID field missing from provided attributes' }
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 { should raise_error Faraday::ResourceNotFound }
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 { should raise_error Restforce::UnauthorizedError }
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
@@ -28,7 +28,7 @@ describe Restforce::Concerns::Base do
28
28
 
29
29
  describe '.options' do
30
30
  subject { lambda { client.options } }
31
- it { should_not raise_error }
31
+ it { expect { subject.call }.not_to raise_error }
32
32
  end
33
33
 
34
34
  describe '.instance_url' do
@@ -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
@@ -85,6 +85,8 @@ describe Restforce::Concerns::Connection do
85
85
  client.stub options: { adapter: :typhoeus }
86
86
  end
87
87
 
88
- its(:adapter) { should eq(:typhoeus) }
88
+ it "uses typheous adapter" do
89
+ expect(client.send(:adapter)).to be(:typhoeus)
90
+ end
89
91
  end
90
92
  end
@@ -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 { should raise_error NotImplementedError }
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 { should_not raise_error }
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 { should raise_error Restforce::UnauthorizedError }
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 { should change { env[:request_headers]['Authorization'] }.to eq 'OAuth token' }
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 { should change { env[:request_headers]['x-test-header'] }.to eq 'Test Value' }
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 { should_not(change { env[:request_headers] }) }
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
- before do
21
- app.should_receive(:on_complete) { middleware.on_complete(env) }
22
- app.should_receive(:call) do
23
- env[:body] = gzip fixture('sobject/query_success_response')
24
- env[:response_headers]['Content-Encoding'] = 'gzip'
25
- app
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
- it 'decompresses the body' do
30
- should change { env[:body] }.to(fixture('sobject/query_success_response'))
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 :compress is false' do
34
- it { should_not(change { env[:request_headers]['Accept-Encoding'] }) }
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 :compress is true' do
75
+ context 'when the response does not even claim to be gzipped' do
38
76
  before do
39
- options[:compress] = true
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 { should(change { env[:request_headers]['Accept-Encoding'] }.to('gzip')) }
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