restforce 5.2.4 → 6.0.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dfdaff5160d8845afd9c6ed3c384c7edf7d9ecb2593d562bae4d9a4812904137
4
- data.tar.gz: 8510146b906c8f7f0869a467db96d9bd3416446338a9d237d8c2e633c911cc35
3
+ metadata.gz: 06ba6ff782797da12181fade1eb6d25cec0da4395a0880080d684a6c8d0c928e
4
+ data.tar.gz: 17854adb01066aa6d09c784adeecee764b530afff0083297162c0d2633fac38f
5
5
  SHA512:
6
- metadata.gz: 592471696b0c585cb73bc26cca75267cfc7a2f3543f86449edd4e3ffc5401975cd4ded7cc7a20b63b5854434c06e6e63c48e0f49bf51839b8b8bee15e02113e8
7
- data.tar.gz: 71c9272a0799c6e899bbdd56a0380b6ae5b7112d11e56fb3860a10ca0ba0b1dfb9cf58bb25b0bf59ad8f8b38f50360f5f8ccbed4090afa194a67a19a7ae28318
6
+ metadata.gz: 8cb18b0f488c6e793b2c1a64e54e982f08b1739522ae3bb3a5f7dd8837ffe62c05506365731f9040b25dd396b7ad5f3aff143dd9476fd0f2be475bf23623547f
7
+ data.tar.gz: 7445f7337587cc19c61743abe80dad16c3d1b2c10527691d6e33edb65a023dcd4afb32db9a569405c43a7878c3cba32fb2426dd50264d7b56cfc95e5bdb2d203
@@ -4,16 +4,7 @@ updates:
4
4
  directory: "/"
5
5
  schedule:
6
6
  interval: daily
7
- open-pull-requests-limit: 10
8
- ignore:
9
- - dependency-name: rubocop
10
- versions:
11
- - 1.10.0
12
- - 1.11.0
13
- - 1.12.0
14
- - 1.12.1
15
- - 1.9.0
16
- - dependency-name: webmock
17
- versions:
18
- - 3.12.0
19
- - 3.12.1
7
+ - package-ecosystem: 'github-actions'
8
+ directory: '/'
9
+ schedule:
10
+ interval: 'daily'
@@ -0,0 +1,23 @@
1
+ name: 'Build'
2
+ on: push
3
+ jobs:
4
+ lint-and-test:
5
+ runs-on: ubuntu-latest
6
+ strategy:
7
+ fail-fast: false
8
+ matrix:
9
+ ruby_version: ['2.7', '3.0', '3.1', '3.2.0-preview1']
10
+ steps:
11
+ - name: Checkout code
12
+ uses: actions/checkout@v3
13
+ - name: Setup Ruby ${{ matrix.ruby_version }}
14
+ uses: ruby/setup-ruby@v1
15
+ with:
16
+ bundler-cache: true
17
+ ruby-version: ${{ matrix.ruby_version }}
18
+ - name: Install dependencies
19
+ run: bundle install
20
+ - name: Lint Ruby files
21
+ run: bundle exec rubocop
22
+ - name: Run RSpec tests
23
+ run: bundle exec rspec
@@ -0,0 +1,27 @@
1
+ name: 'Test against supported Faraday minor versions'
2
+ on: push
3
+ jobs:
4
+ lint-and-test:
5
+ runs-on: ubuntu-latest
6
+ strategy:
7
+ fail-fast: false
8
+ matrix:
9
+ # Normally, we only test the most recent patch version for any given minor version.
10
+ # For v2.0.x, we test v2.0.0 and v2.0.1 because v2.0.0 has a special behaviour where
11
+ # the Net::HTTP adapter is not included. See
12
+ # https://github.com/lostisland/faraday/blob/main/UPGRADING.md#faraday-20.
13
+ faraday_version: ['1.1.0', '1.2.0', '1.3.1', '1.4.1', '1.5.1', '1.6.0', '1.7.2', '1.8.0', '1.9.3', '1.10.0', '2.0.0', '2.0.1', '2.1.0', '2.2.0', '2.3.0', '2.4.0']
14
+ env:
15
+ FARADAY_VERSION: ~> ${{ matrix.faraday_version }}
16
+ steps:
17
+ - name: Checkout code
18
+ uses: actions/checkout@v3
19
+ - name: Setup Ruby ${{ matrix.ruby_version }}
20
+ uses: ruby/setup-ruby@v1
21
+ with:
22
+ bundler-cache: true
23
+ ruby-version: 3.1
24
+ - name: Install dependencies
25
+ run: bundle install
26
+ - name: Run RSpec tests
27
+ run: bundle exec rspec
data/.rubocop.yml CHANGED
@@ -8,7 +8,7 @@ AllCops:
8
8
  - .*/**/*
9
9
  - vendor/**/*
10
10
  NewCops: enable
11
- TargetRubyVersion: 2.6
11
+ TargetRubyVersion: 2.7
12
12
 
13
13
  # Limit lines to 90 characters.
14
14
  Layout/LineLength:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ # 6.0.0.rc.1 (Aug 4 2022)
2
+
3
+ *This version is a release candidate. Once we have gathered feedback from users and are confident that it works as expected, we will release a final `v6.0.0` version.*
4
+
5
+ __This version contains breaking changes. For help with upgrading, see [`UPGRADING.md`](https://github.com/restforce/restforce/blob/v6.0.0.rc.1/UPGRADING.md).__
6
+
7
+ * __⚠️ Drop support for Ruby 2.6__, since [Ruby 2.6 has reached its end-of-life](https://www.ruby-lang.org/en/downloads/) (@timrogers)
8
+ * __⚠️ Drop compatability with `faraday` versions before `v1.1.0`__ (@timrogers)
9
+ * Add support for `faraday` versions `v2.0.0` onwards (@timrogers)
10
+
11
+ # 5.3.1 (Jul 19 2022)
12
+
13
+ * Handle the `EXCEEDED_ID_LIMIT` error returned by the Salesforce API (@timrogers, @yashshah1)
14
+
15
+ # 5.3.0 (May 30, 2022)
16
+
17
+ * Add support for Faraday v1.9.x and v1.10.0 (@magni-, @timrogers)
18
+ * Follow redirects during authentication to support Lightning URLs (e.g. `*.lightning.force.com` instead of `*.my.salesforce.com`) (@nhocki)
19
+
1
20
  # 5.2.4 (Mar 16, 2022)
2
21
 
3
22
  * Fix `Restforce::Collection#size` for Salesforce APIs that use the `size` property to return the total number of results, instead of `totalSize` (@kwong-yw)
data/Gemfile CHANGED
@@ -3,6 +3,10 @@
3
3
  source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
+ faraday_version = ENV.fetch('FARADAY_VERSION', '~> 1.8.0')
7
+
8
+ # Enable us to explicitly pick a Faraday version when running tests
9
+ gem 'faraday', faraday_version
6
10
  gem 'faye' unless RUBY_PLATFORM == 'java'
7
11
  gem 'guard-rspec'
8
12
  gem 'guard-rubocop'
@@ -12,6 +16,8 @@ gem 'rspec', '~> 3.11.0'
12
16
  gem 'rspec-collection_matchers', '~> 1.2.0'
13
17
  gem 'rspec-its', '~> 1.3.0'
14
18
  gem 'rspec_junit_formatter', '~> 0.5.1'
15
- gem 'rubocop', '~> 1.26.0'
19
+ gem 'rubocop', '~> 1.32.0'
16
20
  gem 'simplecov', '~> 0.21.2'
17
- gem 'webmock', '~> 3.14.0'
21
+ gem 'webmock', '~> 3.16.0'
22
+
23
+ gem 'faraday-typhoeus', '~> 0.2.1' unless faraday_version.start_with?("~> 1")
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Restforce
2
2
 
3
- [![CircleCI](https://circleci.com/gh/restforce/restforce.svg?style=svg)](https://circleci.com/gh/restforce/restforce) [![Code Climate](https://codeclimate.com/github/restforce/restforce.png)](https://codeclimate.com/github/restforce/restforce) [![Dependency Status](https://gemnasium.com/restforce/restforce.png)](https://gemnasium.com/restforce/restforce)
4
- ![](https://img.shields.io/gem/dt/restforce.svg)
3
+ [![CircleCI](https://circleci.com/gh/restforce/restforce.svg?style=svg)](https://circleci.com/gh/restforce/restforce)
4
+ ![Downloads](https://img.shields.io/gem/dt/restforce.svg)
5
5
 
6
6
  Restforce is a ruby gem for the [Salesforce REST api](http://www.salesforce.com/us/developer/docs/api_rest/index.htm).
7
7
 
@@ -27,7 +27,7 @@ Features include:
27
27
 
28
28
  Add this line to your application's Gemfile:
29
29
 
30
- gem 'restforce', '~> 5.2.4'
30
+ gem 'restforce', '~> 5.3.1'
31
31
 
32
32
  And then execute:
33
33
 
@@ -293,6 +293,8 @@ client.find('Account', '1234', 'Some_External_Id_Field__c')
293
293
  # => #<Restforce::SObject Id="001D000000INjVe" Name="Test" LastModifiedBy="005G0000002f8FHIAY" ... >
294
294
  ```
295
295
 
296
+ `find` raises an error if nothing is found.
297
+
296
298
  ### select
297
299
 
298
300
  `select` allows the fetching of a specific list of fields from a single object. It requires an `external_id` lookup, but is often much faster than an arbitrary query.
data/UPGRADING.md CHANGED
@@ -1,3 +1,32 @@
1
+ # Upgrading from Restforce 5.x to 6.x
2
+
3
+ __There are two breaking changes introduced in Restforce 6.x__. In this guide, you'll learn about these changes and what you should check in your code to make sure that it will work with the latest version of the library.
4
+
5
+ ## Versions of `faraday` before `v1.1.0` are no longer supported
6
+
7
+ __Likelyhood of impact__: Moderate
8
+
9
+ Restforce uses a gem called [`faraday`](https://github.com/lostisland/faraday) to make HTTP requests to Salesforce.
10
+
11
+ Up until now, Restforce has supported Faraday versions between v0.9.0 and v1.10.0.
12
+
13
+ In Restforce 6.x, we drop support for Faraday versions before v1.1.0, and add support for Faraday v2.x.
14
+
15
+ This will allow you to use the latest versions of Faraday and benefit from security patches, new features, etc., but you may need to adapt your code. The impact of this change will depend on your project:
16
+
17
+ * If Restforce is the only part of your project using Faraday - that is, your own code doesn't use Faraday and none of your other gems use Faraday - then you shouldn't need to do anything special. Just upgrade Restforce, and everything should be handled automatically.
18
+ * If your own code uses Faraday or another gem you use depends on Faraday, and you're currently using a Faraday version before v1.1.0, you will need to upgrade your Faraday version. If possible, you should upgrade to the latest version (v2.4.0 at the time of writing). This may require you to adapt your code (see [here](https://github.com/lostisland/faraday/blob/main/UPGRADING.md) for Faraday's instructions) or upgrade other gems you depend on.
19
+
20
+ ## Ruby 2.6 is no longer supported
21
+
22
+ __Likelyhood of impact__: Moderate
23
+
24
+ Ruby 2.6 is no longer officially supported as an active version of the Ruby language. That means that it will not receive patches and security fixes.
25
+
26
+ Accordingly, we've dropped support for Ruby 2.6 and earlier in the Restforce library. The gemspec now specifies that only 2.7 onwards is supported, and this will be enforced by RubyGems.
27
+
28
+ Before you update to Restforce 6.x, you'll need to switch to Ruby 2.7 or later. The current version of Ruby at the time of wriing is 3.1.
29
+
1
30
  # Upgrading from Restforce 4.x to 5.x
2
31
 
3
32
  __There are three breaking changes introduced in Restforce 5.x__. In this guide, you'll learn about these changes and what you should check in your code to make sure that it will work with the latest version of the library.
@@ -429,6 +429,7 @@ module Restforce
429
429
  # field - External ID field to use (default: nil).
430
430
  #
431
431
  # Returns the Restforce::SObject sobject record.
432
+ # Raises NotFoundError if nothing is found.
432
433
  def find(sobject, id, field = nil)
433
434
  url = if field
434
435
  "sobjects/#{sobject}/#{field}/#{ERB::Util.url_encode(id)}"
@@ -43,7 +43,7 @@ module Restforce
43
43
  # Caches GET requests.
44
44
  builder.use Restforce::Middleware::Caching, cache, options if cache
45
45
  # Follows 30x redirects.
46
- builder.use FaradayMiddleware::FollowRedirects
46
+ builder.use Faraday::FollowRedirects::Middleware
47
47
  # Raises errors for 40x responses.
48
48
  builder.use Restforce::Middleware::RaiseError
49
49
  # Parses returned JSON response into a hash.
@@ -91,29 +91,31 @@ module Restforce
91
91
  end
92
92
  end
93
93
 
94
- option :api_version, default: lambda { ENV['SALESFORCE_API_VERSION'] || '26.0' }
94
+ option :api_version, default: lambda { ENV.fetch('SALESFORCE_API_VERSION', '26.0') }
95
95
 
96
96
  # The username to use during login.
97
- option :username, default: lambda { ENV['SALESFORCE_USERNAME'] }
97
+ option :username, default: lambda { ENV.fetch('SALESFORCE_USERNAME', nil) }
98
98
 
99
99
  # The password to use during login.
100
- option :password, default: lambda { ENV['SALESFORCE_PASSWORD'] }
100
+ option :password, default: lambda { ENV.fetch('SALESFORCE_PASSWORD', nil) }
101
101
 
102
102
  # The security token to use during login.
103
- option :security_token, default: lambda { ENV['SALESFORCE_SECURITY_TOKEN'] }
103
+ option :security_token, default: lambda {
104
+ ENV.fetch('SALESFORCE_SECURITY_TOKEN', nil)
105
+ }
104
106
 
105
107
  # The OAuth client id
106
- option :client_id, default: lambda { ENV['SALESFORCE_CLIENT_ID'] }
108
+ option :client_id, default: lambda { ENV.fetch('SALESFORCE_CLIENT_ID', nil) }
107
109
 
108
110
  # The OAuth client secret
109
- option :client_secret, default: lambda { ENV['SALESFORCE_CLIENT_SECRET'] }
111
+ option :client_secret, default: lambda { ENV.fetch('SALESFORCE_CLIENT_SECRET', nil) }
110
112
 
111
113
  # The private key for JWT authentication
112
114
  option :jwt_key
113
115
 
114
116
  # Set this to true if you're authenticating with a Sandbox instance.
115
117
  # Defaults to false.
116
- option :host, default: lambda { ENV['SALESFORCE_HOST'] || 'login.salesforce.com' }
118
+ option :host, default: lambda { ENV.fetch('SALESFORCE_HOST', 'login.salesforce.com') }
117
119
 
118
120
  option :oauth_token
119
121
  option :refresh_token
@@ -139,7 +141,7 @@ module Restforce
139
141
  # Faraday adapter to use. Defaults to Faraday.default_adapter.
140
142
  option :adapter, default: lambda { Faraday.default_adapter }
141
143
 
142
- option :proxy_uri, default: lambda { ENV['SALESFORCE_PROXY_URI'] }
144
+ option :proxy_uri, default: lambda { ENV.fetch('SALESFORCE_PROXY_URI', nil) }
143
145
 
144
146
  # A Proc that is called with the response body after a successful authentication.
145
147
  option :authentication_callback
@@ -145,6 +145,8 @@ module Restforce
145
145
 
146
146
  class ErrorInMailer < ResponseError; end
147
147
 
148
+ class ExceededIdLimit < ResponseError; end
149
+
148
150
  class ExceededMaxSemijoinSubselects < ResponseError; end
149
151
 
150
152
  class FailedActivation < ResponseError; end
@@ -484,6 +486,7 @@ module Restforce
484
486
  "ENTITY_IS_LOCKED" => EntityIsLocked,
485
487
  "ENVIRONMENT_HUB_MEMBERSHIP_CONFLICT" => EnvironmentHubMembershipConflict,
486
488
  "ERROR_IN_MAILER" => ErrorInMailer,
489
+ "EXCEEDED_ID_LIMIT" => ExceededIdLimit,
487
490
  "EXCEEDED_MAX_SEMIJOIN_SUBSELECTS" => ExceededMaxSemijoinSubselects,
488
491
  "FAILED_ACTIVATION" => FailedActivation,
489
492
  "FIELD_CUSTOM_VALIDATION_EXCEPTION" => FieldCustomValidationException,
@@ -1,11 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- begin
3
+ case Faraday::VERSION
4
+ when /\A1\.[0-8]\./
5
+ # Faraday 1.x versions before 1.9 - not matched by
6
+ # the previous clause - use `FilePart` (which must be explicitly
7
+ # required)
4
8
  require 'faraday/file_part'
5
- rescue LoadError
6
- require 'faraday/upload_io'
9
+ when /\A[12]\./
10
+ # Later 1.x versions from 1.9 onwards automatically include the
11
+ # `faraday-multipart` gem, which includes `Faraday::FilePart`
12
+ require 'faraday/multipart'
13
+ else
14
+ raise "Unexpected Faraday version #{Faraday::VERSION} - not sure how to set up " \
15
+ "multipart support"
7
16
  end
8
-
9
17
  module Restforce
10
18
  if defined?(::Faraday::FilePart)
11
19
  FilePart = Faraday::FilePart
@@ -49,6 +49,7 @@ module Restforce
49
49
  @connection ||= Faraday.new(faraday_options) do |builder|
50
50
  builder.use Faraday::Request::UrlEncoded
51
51
  builder.use Restforce::Middleware::Mashify, nil, @options
52
+ builder.use Faraday::FollowRedirects::Middleware
52
53
  builder.response :json
53
54
 
54
55
  if Restforce.log?
@@ -1,30 +1,155 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ #
4
+ # Taken from `lib/faraday_middleware/response/caching.rb` in the `faraday_middleware`
5
+ # gem (<https://github.com/lostisland/faraday_middleware/blob/784b4f8/lib/faraday_middleware/response/caching.rb>).
6
+ #
7
+ # Includes some small modifications to allow the cache to be cleared in
8
+ # `#without_caching` mode, replicating traditional Restforce behaviour.
9
+ #
10
+ # Copyright (c) 2011 Erik Michaels-Ober, Wynn Netherland, et al.
11
+ # Licensed under the MIT License.
12
+ #
13
+ require 'faraday'
14
+ require 'forwardable'
15
+ require 'digest/sha1'
16
+
3
17
  module Restforce
4
- class Middleware::Caching < FaradayMiddleware::Caching
5
- def call(env)
6
- expire(cache_key(env)) unless use_cache?
7
- super
18
+ # Public: Caches GET responses and pulls subsequent ones from the cache.
19
+ class Middleware::Caching < Faraday::Middleware
20
+ attr_reader :cache
21
+
22
+ # Internal: List of status codes that can be cached:
23
+ # * 200 - 'OK'
24
+ # * 203 - 'Non-Authoritative Information'
25
+ # * 300 - 'Multiple Choices'
26
+ # * 301 - 'Moved Permanently'
27
+ # * 302 - 'Found'
28
+ # * 404 - 'Not Found'
29
+ # * 410 - 'Gone'
30
+ CACHEABLE_STATUS_CODES = [200, 203, 300, 301, 302, 404, 410].freeze
31
+
32
+ extend Forwardable
33
+ def_delegators :'Faraday::Utils', :parse_query, :build_query
34
+
35
+ # Public: initialize the middleware.
36
+ #
37
+ # cache - An object that responds to read and write (default: nil).
38
+ # options - An options Hash (default: {}):
39
+ # :ignore_params - String name or Array names of query
40
+ # params that should be ignored when forming
41
+ # the cache key (default: []).
42
+ # :write_options - Hash of settings that should be passed as the
43
+ # third options parameter to the cache's #write
44
+ # method. If not specified, no options parameter
45
+ # will be passed.
46
+ # :full_key - Boolean - use full URL as cache key:
47
+ # (url.host + url.request_uri)
48
+ # :status_codes - Array of http status code to be cache
49
+ # (default: CACHEABLE_STATUS_CODE)
50
+ #
51
+ # Yields if no cache is given. The block should return a cache object.
52
+ def initialize(app, cache = nil, options = {})
53
+ super(app)
54
+ if cache.is_a?(Hash) && block_given?
55
+ options = cache
56
+ cache = nil
57
+ end
58
+ @cache = cache || yield
59
+ @options = options
8
60
  end
9
61
 
10
- def expire(key)
11
- cache&.delete(key)
62
+ def call(env)
63
+ # Taken from `Restforce::Middleware::Caching` implementation
64
+ # before we switched away from the `faraday_middleware` gem.
65
+ # See https://github.com/restforce/restforce/blob/a08b9d6c5e277bd7111ffa7ed50465dd49c05fab/lib/restforce/middleware/caching.rb.
66
+ cache&.delete(cache_key(env)) unless use_cache?
67
+
68
+ if env[:method] == :get
69
+ if env[:parallel_manager]
70
+ # callback mode
71
+ cache_on_complete(env)
72
+ else
73
+ # synchronous mode
74
+ key = cache_key(env)
75
+ unless (response = cache.read(key)) && response
76
+ response = @app.call(env)
77
+ store_response_in_cache(key, response)
78
+ end
79
+ finalize_response(response, env)
80
+ end
81
+ else
82
+ @app.call(env)
83
+ end
12
84
  end
13
85
 
14
- # We don't want to cache requests for different clients, so append the
15
- # oauth token to the cache key.
16
86
  def cache_key(env)
17
- super(env) + hashed_auth_header(env)
87
+ url = env[:url].dup
88
+ if url.query && params_to_ignore.any?
89
+ params = parse_query url.query
90
+ params.reject! { |k,| params_to_ignore.include? k }
91
+ url.query = params.any? ? build_query(params) : nil
92
+ end
93
+ url.normalize!
94
+ digest = full_key? ? url.host + url.request_uri : url.request_uri
95
+ Digest::SHA1.hexdigest(digest)
18
96
  end
19
97
 
20
- def use_cache?
21
- @options[:use_cache]
98
+ def params_to_ignore
99
+ @params_to_ignore ||= Array(@options[:ignore_params]).map(&:to_s)
100
+ end
101
+
102
+ def full_key?
103
+ @full_key ||= @options[:full_key]
22
104
  end
23
105
 
24
- def hashed_auth_header(env)
25
- Digest::SHA1.hexdigest(
26
- env[:request_headers][Restforce::Middleware::Authorization::AUTH_HEADER]
27
- )
106
+ def custom_status_codes
107
+ @custom_status_codes ||= begin
108
+ codes = CACHEABLE_STATUS_CODES & Array(@options[:status_codes]).map(&:to_i)
109
+ codes.any? ? codes : CACHEABLE_STATUS_CODES
110
+ end
111
+ end
112
+
113
+ def cache_on_complete(env)
114
+ key = cache_key(env)
115
+ if (cached_response = cache.read(key))
116
+ finalize_response(cached_response, env)
117
+ else
118
+ # response.status is nil at this point
119
+ # any checks need to be done inside on_complete block
120
+ @app.call(env).on_complete do |response_env|
121
+ store_response_in_cache(key, response_env.response)
122
+ response_env
123
+ end
124
+ end
125
+ end
126
+
127
+ def store_response_in_cache(key, response)
128
+ return unless custom_status_codes.include?(response.status)
129
+
130
+ if @options[:write_options]
131
+ cache.write(key, response, @options[:write_options])
132
+ else
133
+ cache.write(key, response)
134
+ end
135
+ end
136
+
137
+ def finalize_response(response, env)
138
+ response = response.dup if response.frozen?
139
+ env[:response] = response
140
+ unless env[:response_headers]
141
+ env.update response.env
142
+ # FIXME: omg hax
143
+ response.instance_variable_set('@env', env)
144
+ end
145
+ response
146
+ end
147
+
148
+ # Taken from `Restforce::Middleware::Caching` implementation
149
+ # before we switched away from the `faraday_middleware` gem.
150
+ # See https://github.com/restforce/restforce/blob/a08b9d6c5e277bd7111ffa7ed50465dd49c05fab/lib/restforce/middleware/caching.rb.
151
+ def use_cache?
152
+ @options[:use_cache]
28
153
  end
29
154
  end
30
155
  end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Adapted from `lib/faraday/request/json.rb` in the `faraday`
5
+ # gem (<https://github.com/lostisland/faraday/blob/5366029282968d59980a182258f8c2b0212721c8/lib/faraday/request/json.rb>).
6
+ #
7
+ # We use this because we want to support Faraday 1.x and Faraday 2.x.
8
+ # Faraday 2.x has the JSON middlewares included, but Faraday 1.x doesn't,
9
+ # forcing you to use the `faraday_middleware` gem. This approach allows us
10
+ # to support both.
11
+ #
12
+ # Copyright (c) 2009-2022 Rick Olson, Zack Hobson
13
+ # Licensed under the MIT License.
14
+ #
15
+ require 'json'
16
+
17
+ module Restforce
18
+ # Request middleware that encodes the body as JSON.
19
+ #
20
+ # Processes only requests with matching Content-type or those without a type.
21
+ # If a request doesn't have a type but has a body, it sets the Content-type
22
+ # to JSON MIME-type.
23
+ #
24
+ # Doesn't try to encode bodies that already are in string form.
25
+ # rubocop:disable Style/ClassAndModuleChildren
26
+ class Middleware::JsonRequest < Faraday::Middleware
27
+ # rubocop:enable Style/ClassAndModuleChildren
28
+
29
+ # This is the only line that differs substantively from the version in
30
+ # Faraday. In Faraday, this refers to a Faraday constant.
31
+ CONTENT_TYPE = 'Content-Type'
32
+
33
+ MIME_TYPE = 'application/json'
34
+ MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}.freeze
35
+
36
+ #
37
+ # Taken from `lib/faraday/middleware.rb` in the `faraday`
38
+ # gem (<https://github.com/lostisland/faraday/blob/08b7d4d/lib/faraday/middleware.rb>),
39
+ # with a tiny adaptation to refer to the `@app` instance
40
+ # variable rather than expecting an `attr_reader` to exist.
41
+ #
42
+ # In Faraday versions before v1.2.0, `#call` is missing.
43
+ #
44
+ # Copyright (c) 2009-2022 Rick Olson, Zack Hobson
45
+ # Licensed under the MIT License.
46
+ #
47
+ def call(env)
48
+ on_request(env) if respond_to?(:on_request)
49
+ @app.call(env).on_complete do |environment|
50
+ on_complete(environment) if respond_to?(:on_complete)
51
+ end
52
+ end
53
+
54
+ def on_request(env)
55
+ match_content_type(env) do |data|
56
+ env[:body] = encode(data)
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def encode(data)
63
+ ::JSON.generate(data)
64
+ end
65
+
66
+ def match_content_type(env)
67
+ return unless process_request?(env)
68
+
69
+ env[:request_headers][CONTENT_TYPE] ||= MIME_TYPE
70
+ yield env[:body] unless env[:body].respond_to?(:to_str)
71
+ end
72
+
73
+ def process_request?(env)
74
+ type = request_type(env)
75
+ body?(env) && (type.empty? || type.match?(MIME_TYPE_REGEX))
76
+ end
77
+
78
+ def body?(env)
79
+ (body = env[:body]) && !(body.respond_to?(:to_str) && body.empty?)
80
+ end
81
+
82
+ def request_type(env)
83
+ type = env[:request_headers][CONTENT_TYPE].to_s
84
+ type = type.split(';', 2).first if type.index(';')
85
+ type
86
+ end
87
+ end
88
+ end
89
+
90
+ Faraday::Request.register_middleware(json: Restforce::Middleware::JsonRequest)
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Adapted from `lib/faraday/response/json.rb` in the `faraday`
5
+ # gem (<https://github.com/lostisland/faraday/blob/5366029/lib/faraday/response/json.rb>).
6
+ #
7
+ # Copyright (c) 2009-2022 Rick Olson, Zack Hobson
8
+ # Licensed under the MIT License.
9
+ #
10
+
11
+ require 'json'
12
+
13
+ module Restforce
14
+ # rubocop:disable Style/ClassAndModuleChildren
15
+ class Middleware::JsonResponse < Faraday::Middleware
16
+ # rubocop:enable Style/ClassAndModuleChildren
17
+
18
+ # This is the only line that differs substantively from the version in
19
+ # Faraday. In Faraday, this refers to a Faraday constant.
20
+ CONTENT_TYPE = 'Content-Type'
21
+
22
+ def initialize(app = nil, parser_options: nil, content_type: /\bjson$/,
23
+ preserve_raw: false)
24
+ super(app)
25
+ @parser_options = parser_options
26
+ @content_types = Array(content_type)
27
+ @preserve_raw = preserve_raw
28
+ end
29
+
30
+ #
31
+ # Taken from `lib/faraday/middleware.rb` in the `faraday`
32
+ # gem (<https://github.com/lostisland/faraday/blob/08b7d4d/lib/faraday/middleware.rb>),
33
+ # with a tiny adaptation to refer to the `@app` instance
34
+ # variable rather than expecting an `attr_reader` to exist.
35
+ #
36
+ # In Faraday versions before v1.2.0, `#call` is missing.
37
+ #
38
+ # Copyright (c) 2009-2022 Rick Olson, Zack Hobson
39
+ # Licensed under the MIT License.
40
+ #
41
+ def call(env)
42
+ on_request(env) if respond_to?(:on_request)
43
+ @app.call(env).on_complete do |environment|
44
+ on_complete(environment) if respond_to?(:on_complete)
45
+ end
46
+ end
47
+
48
+ def on_complete(env)
49
+ process_response(env) if parse_response?(env)
50
+ end
51
+
52
+ private
53
+
54
+ def process_response(env)
55
+ env[:raw_body] = env[:body] if @preserve_raw
56
+ env[:body] = parse(env[:body])
57
+ rescue StandardError, SyntaxError => e
58
+ raise Faraday::ParsingError.new(e, env[:response])
59
+ end
60
+
61
+ def parse(body)
62
+ ::JSON.parse(body, @parser_options || {}) unless body.strip.empty?
63
+ end
64
+
65
+ def parse_response?(env)
66
+ process_response_type?(env) &&
67
+ env[:body].respond_to?(:to_str)
68
+ end
69
+
70
+ def process_response_type?(env)
71
+ type = response_type(env)
72
+ @content_types.empty? || @content_types.any? do |pattern|
73
+ pattern.is_a?(Regexp) ? type.match?(pattern) : type == pattern
74
+ end
75
+ end
76
+
77
+ def response_type(env)
78
+ type = env[:response_headers][CONTENT_TYPE].to_s
79
+ type = type.split(';', 2).first if type.index(';')
80
+ type
81
+ end
82
+ end
83
+ end
84
+
85
+ Faraday::Response.register_middleware(json: Restforce::Middleware::JsonResponse)