mirror of
https://github.com/lostisland/faraday.git
synced 2025-08-12 00:03:20 -04:00
Compare commits
71 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
d099fafd65 | ||
|
cf32578f25 | ||
|
e76e60d3c0 | ||
|
674fc1583f | ||
|
da86ebae9c | ||
|
ad8fe1e89a | ||
|
1ddd281893 | ||
|
976369857e | ||
|
64e8a2bdb1 | ||
|
bbaa093dbc | ||
|
fa9424b05a | ||
|
4018769a30 | ||
|
b5a02d7300 | ||
|
b63eb9121f | ||
|
77204cc7e8 | ||
|
919dc8fdb3 | ||
|
064a54be6c | ||
|
cd1c44a4aa | ||
|
1551c32371 | ||
|
a9cf00425e | ||
|
529b5b043e | ||
|
b7b2bc19e9 | ||
|
f9f4ce5bc1 | ||
|
93ef9e0ea9 | ||
|
fff02307ac | ||
|
59c5003ceb | ||
|
98d5adf924 | ||
|
9e5c8a113f | ||
|
9fcff671ff | ||
|
3170e7df6f | ||
|
f208ffcc9a | ||
|
9056eccea6 | ||
|
99228e4743 | ||
|
9cdc025759 | ||
|
3835b48d80 | ||
|
3efc0a8982 | ||
|
f27f7ab801 | ||
|
051a635f4b | ||
|
4860f75372 | ||
|
073faf7539 | ||
|
18524c6e89 | ||
|
d51c392c2c | ||
|
d83a2818e7 | ||
|
1958cb1ce2 | ||
|
d7d5a6a36f | ||
|
5996054fd4 | ||
|
d8bfca25fa | ||
|
4abafa5c66 | ||
|
89107f9889 | ||
|
b5245081d9 | ||
|
04515f38b3 | ||
|
6933e9b70f | ||
|
6d82d716c2 | ||
|
7dc694150d | ||
|
c9cc1b30ec | ||
|
c0540b7ba3 | ||
|
87e655f306 | ||
|
cd2cdfd446 | ||
|
f56e9387c8 | ||
|
18154c8332 | ||
|
4b34b509fe | ||
|
d820a58314 | ||
|
cc5d607766 | ||
|
ceb01e42e8 | ||
|
074506e67c | ||
|
898f203584 | ||
|
f0f549d7ef | ||
|
caa4ff42f8 | ||
|
13732f7ff2 | ||
|
8cbfd758c2 | ||
|
9487833b42 |
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@ -24,7 +24,7 @@ These resources can help:
|
|||||||
|
|
||||||
This project attempts to improve in these areas. Join us in doing that important work.
|
This project attempts to improve in these areas. Join us in doing that important work.
|
||||||
|
|
||||||
If you want to privately raise any breach to this policy with the Faraday team, feel free to reach out to [@iMacTia](https://twitter.com/iMacTia) and [@olleolleolle](https://twitter.com/olleolleolle) on Twitter.
|
If you want to privately raise any breach to this policy with the Faraday team, feel free to reach out to [@iMacTia](https://ruby.social/@iMacTia) and [@olleolleolle](https://ruby.social/@olleolleolle) on the Mastodon instance ruby.social.
|
||||||
|
|
||||||
|
|
||||||
### Required Checks
|
### Required Checks
|
||||||
|
8
.github/dependabot.yml
vendored
8
.github/dependabot.yml
vendored
@ -1,6 +1,14 @@
|
|||||||
version: 2
|
version: 2
|
||||||
updates:
|
updates:
|
||||||
|
- package-ecosystem: "bundler"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "weekly"
|
interval: "weekly"
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@ -24,10 +24,10 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Ruby 2.7
|
- name: Setup Ruby 3.x
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: 2.7
|
ruby-version: 3
|
||||||
bundler-cache: true
|
bundler-cache: true
|
||||||
|
|
||||||
- name: Rubocop
|
- name: Rubocop
|
||||||
@ -43,7 +43,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ruby: [ '2.6', '2.7', '3.0', '3.1', '3.2' ]
|
ruby: [ '3.0', '3.1', '3.2', '3.3', '3.4' ]
|
||||||
experimental: [false]
|
experimental: [false]
|
||||||
include:
|
include:
|
||||||
- ruby: head
|
- ruby: head
|
||||||
@ -63,7 +63,6 @@ jobs:
|
|||||||
run: bundle exec rake
|
run: bundle exec rake
|
||||||
|
|
||||||
- name: Test External Adapters
|
- name: Test External Adapters
|
||||||
if: ${{ matrix.ruby != '2.6' }}
|
|
||||||
continue-on-error: ${{ matrix.experimental }}
|
continue-on-error: ${{ matrix.experimental }}
|
||||||
run: bundle exec bake test:external
|
run: bundle exec bake test:external
|
||||||
|
|
||||||
|
15
.github/workflows/publish.yml
vendored
15
.github/workflows/publish.yml
vendored
@ -4,22 +4,23 @@ on:
|
|||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read # to checkout the code (actions/checkout)
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Publish to Rubygems
|
name: Publish to Rubygems
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Ruby 2.7
|
- name: Setup Ruby 3.x
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: 2.7
|
bundler-cache: true
|
||||||
|
ruby-version: 3
|
||||||
|
|
||||||
- name: Publish to RubyGems
|
- name: Publish to RubyGems
|
||||||
uses: dawidd6/action-publish-gem@v1
|
uses: rubygems/release-gem@v1
|
||||||
with:
|
|
||||||
api_key: ${{secrets.RUBYGEMS_AUTH_TOKEN}}
|
|
||||||
|
@ -7,7 +7,7 @@ require:
|
|||||||
AllCops:
|
AllCops:
|
||||||
DisplayCopNames: true
|
DisplayCopNames: true
|
||||||
DisplayStyleGuide: true
|
DisplayStyleGuide: true
|
||||||
TargetRubyVersion: 2.6
|
TargetRubyVersion: 3.0
|
||||||
|
|
||||||
# Custom config
|
# Custom config
|
||||||
Gemspec/RequireMFA: # we don't know if this works with auto-deployments yet
|
Gemspec/RequireMFA: # we don't know if this works with auto-deployments yet
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# This configuration was generated by
|
# This configuration was generated by
|
||||||
# `rubocop --auto-gen-config`
|
# `rubocop --auto-gen-config`
|
||||||
# on 2022-08-08 14:26:32 UTC using RuboCop version 1.33.0.
|
# on 2023-12-27 11:12:52 UTC using RuboCop version 1.59.0.
|
||||||
# The point is for the user to remove these configuration records
|
# The point is for the user to remove these configuration records
|
||||||
# one by one as the offenses are removed from the code base.
|
# one by one as the offenses are removed from the code base.
|
||||||
# Note that changes in the inspected code, or installation of new
|
# Note that changes in the inspected code, or installation of new
|
||||||
@ -23,23 +23,23 @@ Lint/EmptyBlock:
|
|||||||
- 'spec/faraday/rack_builder_spec.rb'
|
- 'spec/faraday/rack_builder_spec.rb'
|
||||||
- 'spec/faraday/response_spec.rb'
|
- 'spec/faraday/response_spec.rb'
|
||||||
|
|
||||||
# Offense count: 12
|
# Offense count: 13
|
||||||
# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods, CountRepeatedAttributes.
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
||||||
Metrics/AbcSize:
|
Metrics/AbcSize:
|
||||||
Max: 42
|
Max: 42
|
||||||
|
|
||||||
# Offense count: 4
|
# Offense count: 3
|
||||||
# Configuration parameters: CountComments, CountAsOne.
|
# Configuration parameters: CountComments, CountAsOne.
|
||||||
Metrics/ClassLength:
|
Metrics/ClassLength:
|
||||||
Max: 230
|
Max: 230
|
||||||
|
|
||||||
# Offense count: 9
|
# Offense count: 9
|
||||||
# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods.
|
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||||
Metrics/CyclomaticComplexity:
|
Metrics/CyclomaticComplexity:
|
||||||
Max: 13
|
Max: 13
|
||||||
|
|
||||||
# Offense count: 26
|
# Offense count: 27
|
||||||
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods.
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
||||||
Metrics/MethodLength:
|
Metrics/MethodLength:
|
||||||
Max: 33
|
Max: 33
|
||||||
|
|
||||||
@ -48,11 +48,22 @@ Metrics/MethodLength:
|
|||||||
Metrics/ParameterLists:
|
Metrics/ParameterLists:
|
||||||
Max: 6
|
Max: 6
|
||||||
|
|
||||||
# Offense count: 6
|
# Offense count: 7
|
||||||
# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods.
|
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||||
Metrics/PerceivedComplexity:
|
Metrics/PerceivedComplexity:
|
||||||
Max: 14
|
Max: 14
|
||||||
|
|
||||||
|
# Offense count: 19
|
||||||
|
# This cop supports safe autocorrection (--autocorrect).
|
||||||
|
# Configuration parameters: AllowOnlyRestArgument, UseAnonymousForwarding, RedundantRestArgumentNames, RedundantKeywordRestArgumentNames, RedundantBlockArgumentNames.
|
||||||
|
# RedundantRestArgumentNames: args, arguments
|
||||||
|
# RedundantKeywordRestArgumentNames: kwargs, options, opts
|
||||||
|
# RedundantBlockArgumentNames: blk, block, proc
|
||||||
|
Style/ArgumentsForwarding:
|
||||||
|
Exclude:
|
||||||
|
- 'lib/faraday.rb'
|
||||||
|
- 'lib/faraday/rack_builder.rb'
|
||||||
|
|
||||||
# Offense count: 3
|
# Offense count: 3
|
||||||
Style/DocumentDynamicEvalDefinition:
|
Style/DocumentDynamicEvalDefinition:
|
||||||
Exclude:
|
Exclude:
|
||||||
|
@ -517,7 +517,7 @@ Breaking changes:
|
|||||||
- Drop support for Ruby 1.8
|
- Drop support for Ruby 1.8
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
- Include wrapped exception/reponse in ClientErrors
|
- Include wrapped exception/response in ClientErrors
|
||||||
- Add `response.reason_phrase`
|
- Add `response.reason_phrase`
|
||||||
- Provide option to selectively skip logging request/response headers
|
- Provide option to selectively skip logging request/response headers
|
||||||
- Add regex support for pattern matching in `test` adapter
|
- Add regex support for pattern matching in `test` adapter
|
||||||
|
2
Gemfile
2
Gemfile
@ -10,7 +10,7 @@ group :development, :test do
|
|||||||
gem 'bake-test-external'
|
gem 'bake-test-external'
|
||||||
gem 'coveralls_reborn', require: false
|
gem 'coveralls_reborn', require: false
|
||||||
gem 'pry'
|
gem 'pry'
|
||||||
gem 'rack', '~> 2.2'
|
gem 'rack', '~> 3.0'
|
||||||
gem 'rake'
|
gem 'rake'
|
||||||
gem 'rspec', '~> 3.7'
|
gem 'rspec', '~> 3.7'
|
||||||
gem 'rspec_junit_formatter', '~> 0.4'
|
gem 'rspec_junit_formatter', '~> 0.4'
|
||||||
|
@ -35,7 +35,7 @@ Need more details? See the [Faraday API Documentation][apidoc] to see how it wor
|
|||||||
This library aims to support and is [tested against][actions] the currently officially supported Ruby
|
This library aims to support and is [tested against][actions] the currently officially supported Ruby
|
||||||
implementations. This means that, even without a major release, we could add or drop support for Ruby versions,
|
implementations. This means that, even without a major release, we could add or drop support for Ruby versions,
|
||||||
following their [EOL](https://endoflife.date/ruby).
|
following their [EOL](https://endoflife.date/ruby).
|
||||||
Currently that means we support Ruby 2.6+
|
Currently that means we support Ruby 3.0+
|
||||||
|
|
||||||
If something doesn't work on one of these Ruby versions, it's a bug.
|
If something doesn't work on one of these Ruby versions, it's a bug.
|
||||||
|
|
||||||
|
3
Rakefile
3
Rakefile
@ -1,6 +1,9 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rspec/core/rake_task'
|
require 'rspec/core/rake_task'
|
||||||
|
require 'bundler'
|
||||||
|
|
||||||
|
Bundler::GemHelper.install_tasks
|
||||||
|
|
||||||
RSpec::Core::RakeTask.new(:spec) do |task|
|
RSpec::Core::RakeTask.new(:spec) do |task|
|
||||||
task.ruby_opts = %w[-W]
|
task.ruby_opts = %w[-W]
|
||||||
|
@ -19,7 +19,7 @@ We've taken this decision for the following technical reasons:
|
|||||||
focused on the Faraday API more quickly, without having to push it on all adapters immediately.
|
focused on the Faraday API more quickly, without having to push it on all adapters immediately.
|
||||||
* With the community creating more and more adapters, we wanted to avoid having first and second-class adapters
|
* With the community creating more and more adapters, we wanted to avoid having first and second-class adapters
|
||||||
by having some of them included with the gem and others available externally.
|
by having some of them included with the gem and others available externally.
|
||||||
* Moving adapters into separate gems allow to solve the dependency issues once and for all.
|
* Moving adapters into separate gems solve the dependency issues once and for all.
|
||||||
Faraday will remain a dependency-free gem, while adapter gems will be able to automatically pull
|
Faraday will remain a dependency-free gem, while adapter gems will be able to automatically pull
|
||||||
any necessary dependency, without having to rely on the developer to do so.
|
any necessary dependency, without having to rely on the developer to do so.
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
* [Quick Start](getting-started/quick-start.md)
|
* [Quick Start](getting-started/quick-start.md)
|
||||||
* [The Env Object](getting-started/env-object.md)
|
* [The Env Object](getting-started/env-object.md)
|
||||||
* [Dealing with Errors](getting-started/errors.md)
|
* [Dealing with Errors](getting-started/errors.md)
|
||||||
|
* [Migrating from rest-client](getting-started/rest-client-migration.md)
|
||||||
* Customization
|
* Customization
|
||||||
* [Configuration](customization/index.md)
|
* [Configuration](customization/index.md)
|
||||||
* [Connection Options](customization/connection-options.md)
|
* [Connection Options](customization/connection-options.md)
|
||||||
|
@ -42,18 +42,34 @@ class FlorpHttp < ::Faraday::Adapter
|
|||||||
def self.setup_parallel_manager(_options = nil)
|
def self.setup_parallel_manager(_options = nil)
|
||||||
FlorpParallelManager.new # NB: we will need to define this
|
FlorpParallelManager.new # NB: we will need to define this
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def call(env)
|
||||||
|
# NB: you can call `in_parallel?` here to check if the current request
|
||||||
|
# is part of a parallel batch. Useful if you need to collect all requests
|
||||||
|
# into the ParallelManager before running them.
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class FlorpParallelManager
|
class FlorpParallelManager
|
||||||
def add(request, method, *args, &block)
|
# The execute method will be passed the same block as `in_parallel`,
|
||||||
# Collect the requests
|
# so you can either collect the requests or just wrap them into a wrapper,
|
||||||
end
|
# depending on how your adapter works.
|
||||||
|
def execute(&block)
|
||||||
def run
|
run_async(&block)
|
||||||
# Process the requests
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
Compare to the finished example [em-synchrony](https://github.com/lostisland/faraday-em_synchrony/blob/main/lib/faraday/adapter/em_synchrony.rb)
|
### A note on the old, deprecated interface
|
||||||
|
|
||||||
|
Prior to the introduction of the `execute` method, the `ParallelManager` was expected to implement a `run` method
|
||||||
|
and the execution of the block was done by the Faraday connection BEFORE calling that method.
|
||||||
|
|
||||||
|
This approach made the `ParallelManager` implementation harder and forced you to keep state around.
|
||||||
|
The new `execute` implementation allows to avoid this shortfall and support different flows.
|
||||||
|
|
||||||
|
As of Faraday 2.0, `run` is still supported in case `execute` is not implemented by the `ParallelManager`,
|
||||||
|
but this method should be considered deprecated.
|
||||||
|
|
||||||
|
For reference, please see an example using `run` from [em-synchrony](https://github.com/lostisland/faraday-em_synchrony/blob/main/lib/faraday/adapter/em_synchrony.rb)
|
||||||
and its [ParallelManager implementation](https://github.com/lostisland/faraday-em_synchrony/blob/main/lib/faraday/adapter/em_synchrony/parallel_manager.rb).
|
and its [ParallelManager implementation](https://github.com/lostisland/faraday-em_synchrony/blob/main/lib/faraday/adapter/em_synchrony/parallel_manager.rb).
|
||||||
|
@ -2,22 +2,24 @@
|
|||||||
|
|
||||||
Faraday supports a number of SSL options, which can be provided while initializing the connection.
|
Faraday supports a number of SSL options, which can be provided while initializing the connection.
|
||||||
|
|
||||||
| Option | Type | Default | Description |
|
| Option | Type | Default | Description |
|
||||||
|--------------------|----------------------------------------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------|
|
|--------------------|----------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `:verify` | Boolean | true | Verify SSL certificate. Defaults to `true`. |
|
| `:verify` | Boolean | true | Verify SSL certificate. Defaults to `true`. |
|
||||||
| `:verify_hostname` | Boolean | true | Verify SSL certificate hostname. Defaults to `true`. |
|
| `:verify_hostname` | Boolean | true | Verify SSL certificate hostname. Defaults to `true`. |
|
||||||
| `:ca_file` | String | nil | Path to a CA file in PEM format. |
|
| `:hostname` | String | nil | Server hostname for SNI (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLSocket.html#method-i-hostname-3D)). |
|
||||||
| `:ca_path` | String | nil | Path to a CA directory. |
|
| `:ca_file` | String | nil | Path to a CA file in PEM format. |
|
||||||
|
| `:ca_path` | String | nil | Path to a CA directory. |
|
||||||
| `:verify_mode` | Integer | nil | Any `OpenSSL::SSL::` constant (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL.html)). |
|
| `:verify_mode` | Integer | nil | Any `OpenSSL::SSL::` constant (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL.html)). |
|
||||||
| `:cert_store` | OpenSSL::X509::Store | nil | OpenSSL certificate store. |
|
| `:cert_store` | OpenSSL::X509::Store | nil | OpenSSL certificate store. |
|
||||||
| `:client_cert` | OpenSSL::X509::Certificate | nil | Client certificate. |
|
| `:client_cert` | OpenSSL::X509::Certificate | nil | Client certificate. |
|
||||||
| `:client_key` | OpenSSL::PKey::RSA, OpenSSL::PKey::DSA | nil | Client private key. |
|
| `:client_key` | OpenSSL::PKey::RSA, OpenSSL::PKey::DSA | nil | Client private key. |
|
||||||
| `:certificate` | OpenSSL::X509::Certificate | nil | Certificate (Excon only). |
|
| `:certificate` | OpenSSL::X509::Certificate | nil | Certificate (Excon only). |
|
||||||
| `:private_key` | OpenSSL::PKey::RSA | nil | Private key (Excon only). |
|
| `:private_key` | OpenSSL::PKey::RSA | nil | Private key (Excon only). |
|
||||||
| `:verify_depth` | Integer | nil | Maximum depth for the certificate chain verification. |
|
| `:verify_depth` | Integer | nil | Maximum depth for the certificate chain verification. |
|
||||||
| `:version` | Integer | nil | SSL version (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-ssl_version-3D)). |
|
| `:version` | Integer | nil | SSL version (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-ssl_version-3D)). |
|
||||||
| `:min_version` | Integer | nil | Minimum SSL version (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-min_version-3D)). |
|
| `:min_version` | Integer | nil | Minimum SSL version (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-min_version-3D)). |
|
||||||
| `:max_version` | Integer | nil | Maximum SSL version (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D)). |
|
| `:max_version` | Integer | nil | Maximum SSL version (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D)). |
|
||||||
|
| `:ciphers` | String | nil | Ciphers supported (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-ciphers-3D)). |
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
|
225
docs/getting-started/rest-client-migration.md
Normal file
225
docs/getting-started/rest-client-migration.md
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
# Migrating from `rest-client` to `Faraday`
|
||||||
|
|
||||||
|
The `rest-client` gem is in maintenance mode, and developers are encouraged to migrate to actively maintained alternatives like [`faraday`](https://github.com/lostisland/faraday). This guide highlights common usage patterns in `rest-client` and how to migrate them to `faraday`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Comparison
|
||||||
|
|
||||||
|
| Task | rest-client example | faraday example |
|
||||||
|
| ----------------- | -------------------------------------------------------- | -------------------------------------------------------------------------- |
|
||||||
|
| Simple GET | `RestClient.get("https://httpbingo.org/get")` | `Faraday.get("https://httpbingo.org/get")` |
|
||||||
|
| GET with params | `RestClient.get(url, params: { id: 1 })` | `Faraday.get(url, { id: 1 })` |
|
||||||
|
| POST form data | `RestClient.post(url, { a: 1 })` | `Faraday.post(url, { a: 1 })` |
|
||||||
|
| POST JSON | `RestClient.post(url, obj.to_json, content_type: :json)` | `Faraday.post(url, obj.to_json, { 'Content-Type' => 'application/json' })` |
|
||||||
|
| Custom headers | `RestClient.get(url, { Authorization: 'Bearer token' })` | `Faraday.get(url, nil, { 'Authorization' => 'Bearer token' })` |
|
||||||
|
| Get response body | `response.body` | `response.body` |
|
||||||
|
| Get status code | `response.code` | `response.status` |
|
||||||
|
| Get headers | `response.headers` (returns `Hash<Symbol, String>`) | `response.headers` (returns `Hash<String, String>`) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
In your `Gemfile`, replace `rest-client` with:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
gem "faraday"
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
bundle install
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Basic HTTP Requests
|
||||||
|
|
||||||
|
### GET request
|
||||||
|
|
||||||
|
**rest-client:**
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
RestClient.get("https://httpbingo.org/get")
|
||||||
|
```
|
||||||
|
|
||||||
|
**faraday:**
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Faraday.get("https://httpbingo.org/get")
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### GET with Params
|
||||||
|
|
||||||
|
**rest-client:**
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
RestClient.get("https://httpbingo.org/get", params: { id: 1, foo: "bar" })
|
||||||
|
```
|
||||||
|
|
||||||
|
**faraday:**
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Faraday.get("https://httpbingo.org/get", { id: 1, foo: "bar" })
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### POST Requests
|
||||||
|
|
||||||
|
**rest-client:**
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
RestClient.post("https://httpbingo.org/post", { foo: "bar" })
|
||||||
|
```
|
||||||
|
|
||||||
|
**faraday:**
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Faraday.post("https://httpbingo.org/post", { foo: "bar" })
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Sending JSON
|
||||||
|
|
||||||
|
**rest-client:**
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
RestClient.post("https://httpbingo.org/post", { foo: "bar" }.to_json, content_type: :json)
|
||||||
|
```
|
||||||
|
|
||||||
|
**faraday (manual):**
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Faraday.post("https://httpbingo.org/post", { foo: "bar" }.to_json, { 'Content-Type' => 'application/json' })
|
||||||
|
```
|
||||||
|
|
||||||
|
**faraday (with middleware):**
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
conn = Faraday.new(url: "https://httpbingo.org") do |f|
|
||||||
|
f.request :json # encode request body as JSON and set Content-Type
|
||||||
|
f.response :json # parse response body as JSON
|
||||||
|
end
|
||||||
|
|
||||||
|
conn.post("/post", { foo: "bar" })
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Handling Responses
|
||||||
|
|
||||||
|
**rest-client:**
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
response = RestClient.get("https://httpbingo.org/headers")
|
||||||
|
response.code # => 200
|
||||||
|
response.body # => "..."
|
||||||
|
response.headers # => { content_type: "application/json", ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
**faraday:**
|
||||||
|
|
||||||
|
> notice headers Hash keys are stringified, not symbolized like in rest-client
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
response = Faraday.get("https://httpbingo.org/headers")
|
||||||
|
response.status # => 200
|
||||||
|
response.body # => "..."
|
||||||
|
response.headers # => { "content-type" => "application/json", ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
**rest-client:**
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
begin
|
||||||
|
RestClient.get("https://httpbingo.org/status/404")
|
||||||
|
rescue RestClient::NotFound => e
|
||||||
|
puts e.response.code # 404
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**faraday:**
|
||||||
|
|
||||||
|
> By default, Faraday does **not** raise exceptions for HTTP errors (like 404 or 500); it simply returns the response. If you want exceptions to be raised on HTTP error responses, include the `:raise_error` middleware.
|
||||||
|
>
|
||||||
|
> With `:raise_error`, Faraday will raise `Faraday::ResourceNotFound` for 404s and other exceptions for other 4xx/5xx responses.
|
||||||
|
>
|
||||||
|
> See also:
|
||||||
|
>
|
||||||
|
> * [Dealing with Errors](getting-started/errors.md)
|
||||||
|
> * [Raising Errors](middleware/included/raising-errors.md)
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
conn = Faraday.new(url: "https://httpbingo.org") do |f|
|
||||||
|
f.response :raise_error
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
conn.get("/status/404")
|
||||||
|
rescue Faraday::ResourceNotFound => e
|
||||||
|
puts e.response[:status] # 404
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Advanced Request Configuration
|
||||||
|
|
||||||
|
**rest-client:**
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
RestClient::Request.execute(method: :get, url: "https://httpbingo.org/get", timeout: 10)
|
||||||
|
```
|
||||||
|
|
||||||
|
**faraday:**
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
conn = Faraday.new(url: "https://httpbingo.org", request: { timeout: 10 })
|
||||||
|
conn.get("/get")
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Headers
|
||||||
|
|
||||||
|
**rest-client:**
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
RestClient.get("https://httpbingo.org/headers", { Authorization: "Bearer token" })
|
||||||
|
```
|
||||||
|
|
||||||
|
**faraday:**
|
||||||
|
|
||||||
|
> Notice headers Hash expects stringified keys.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Faraday.get("https://httpbingo.org/headers", nil, { "Authorization" => "Bearer token" })
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Redirects
|
||||||
|
|
||||||
|
**rest-client:**
|
||||||
|
Automatically follows GET/HEAD redirects by default.
|
||||||
|
|
||||||
|
**faraday:**
|
||||||
|
Use the `follow_redirects` middleware (not included by default):
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require "faraday/follow_redirects"
|
||||||
|
|
||||||
|
conn = Faraday.new(url: "https://httpbingo.org") do |f|
|
||||||
|
f.response :follow_redirects
|
||||||
|
end
|
||||||
|
```
|
@ -39,6 +39,7 @@ by the client. They raise error classes inheriting from `Faraday::ClientError`.
|
|||||||
| [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) | `Faraday::RequestTimeoutError` |
|
| [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) | `Faraday::RequestTimeoutError` |
|
||||||
| [409](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409) | `Faraday::ConflictError` |
|
| [409](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409) | `Faraday::ConflictError` |
|
||||||
| [422](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422) | `Faraday::UnprocessableEntityError` |
|
| [422](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422) | `Faraday::UnprocessableEntityError` |
|
||||||
|
| [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) | `Faraday::TooManyRequestsError` |
|
||||||
| 4xx (any other) | `Faraday::ClientError` |
|
| 4xx (any other) | `Faraday::ClientError` |
|
||||||
|
|
||||||
## 5xx Errors
|
## 5xx Errors
|
||||||
@ -62,23 +63,28 @@ and raised as `Faraday::NilStatusError`, which inherits from `Faraday::ServerErr
|
|||||||
|
|
||||||
The behavior of this middleware can be customized with the following options:
|
The behavior of this middleware can be customized with the following options:
|
||||||
|
|
||||||
| Option | Default | Description |
|
| Option | Default | Description |
|
||||||
|---------------------|---------|-------------|
|
|----------------------|---------|-------------|
|
||||||
| **include_request** | true | When true, exceptions are initialized with request information including `method`, `url`, `url_path`, `params`, `headers`, and `body`. |
|
| **include_request** | true | When true, exceptions are initialized with request information including `method`, `url`, `url_path`, `params`, `headers`, and `body`. |
|
||||||
|
| **allowed_statuses** | [] | An array of status codes that should not raise an error. |
|
||||||
|
|
||||||
### Example Usage
|
### Example Usage
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||||
faraday.response :raise_error, include_request: true
|
faraday.response :raise_error, include_request: true, allowed_statuses: [404]
|
||||||
end
|
end
|
||||||
|
|
||||||
begin
|
begin
|
||||||
conn.get('/wrong-url') # => Assume this raises a 404 response
|
conn.get('/wrong-url') # => Assume this raises a 404 response
|
||||||
rescue Faraday::ResourceNotFound => e
|
conn.get('/protected-url') # => Assume this raises a 401 response
|
||||||
e.response[:status] #=> 404
|
rescue Faraday::UnauthorizedError => e
|
||||||
e.response[:headers] #=> { ... }
|
e.response[:status] # => 401
|
||||||
e.response[:body] #=> "..."
|
e.response[:headers] # => { ... }
|
||||||
e.response[:request][:url_path] #=> "/wrong-url"
|
e.response[:body] # => "..."
|
||||||
|
e.response[:request][:url_path] # => "/protected-url"
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
In this example, a `Faraday::UnauthorizedError` exception is raised for the `/protected-url` request, while the
|
||||||
|
`/wrong-url` request does not raise an error because the status code `404` is in the `allowed_statuses` array.
|
||||||
|
@ -114,6 +114,49 @@ conn = Faraday.new do |f|
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### DEFAULT_OPTIONS
|
||||||
|
|
||||||
|
`DEFAULT_OPTIONS` improve the flexibility and customizability of new and existing middleware. Class-level `DEFAULT_OPTIONS` and the ability to set these defaults at the application level compliment existing functionality in which options can be passed into middleware on a per-instance basis.
|
||||||
|
|
||||||
|
#### Using DEFAULT_OPTIONS
|
||||||
|
|
||||||
|
Using `RaiseError` as an example, you can see that `DEFAULT_OPTIONS` have been defined at the top of the class:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
DEFAULT_OPTIONS = { include_request: true }.freeze
|
||||||
|
```
|
||||||
|
|
||||||
|
These options will be set at the class level upon instantiation and referenced as needed within the class. From our same example:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
def response_values(env)
|
||||||
|
...
|
||||||
|
return response unless options[:include_request]
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
If the default value provides the desired functionality, no further consideration is needed.
|
||||||
|
|
||||||
|
#### Setting Alternative Options per Application
|
||||||
|
|
||||||
|
In the case where it is desirable to change the default option for all instances within an application, it can be done by configuring the options in a `/config/initializers` file. For example:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# config/initializers/faraday_config.rb
|
||||||
|
|
||||||
|
Faraday::Response::RaiseError.default_options = { include_request: false }
|
||||||
|
```
|
||||||
|
|
||||||
|
After app initialization, all instances of the middleware will have the newly configured option(s). They can still be overridden on a per-instance bases (if handled in the middleware), like this:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Faraday.new do |f|
|
||||||
|
...
|
||||||
|
f.response :raise_error, include_request: true
|
||||||
|
...
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
### Available Middleware
|
### Available Middleware
|
||||||
|
|
||||||
The following pages provide detailed configuration for the middleware that ships with Faraday:
|
The following pages provide detailed configuration for the middleware that ships with Faraday:
|
||||||
|
@ -13,17 +13,17 @@ Gem::Specification.new do |spec|
|
|||||||
spec.homepage = 'https://lostisland.github.io/faraday'
|
spec.homepage = 'https://lostisland.github.io/faraday'
|
||||||
spec.licenses = ['MIT']
|
spec.licenses = ['MIT']
|
||||||
|
|
||||||
spec.required_ruby_version = '>= 2.6'
|
spec.required_ruby_version = '>= 3.0'
|
||||||
|
|
||||||
spec.add_dependency 'base64'
|
|
||||||
# faraday-net_http is the "default adapter", but being a Faraday dependency it can't
|
# faraday-net_http is the "default adapter", but being a Faraday dependency it can't
|
||||||
# control which version of faraday it will be pulled from.
|
# control which version of faraday it will be pulled from.
|
||||||
# To avoid releasing a major version every time there's a new Faraday API, we should
|
# To avoid releasing a major version every time there's a new Faraday API, we should
|
||||||
# always fix its required version to the next MINOR version.
|
# always fix its required version to the next MINOR version.
|
||||||
# This way, we can release minor versions of the adapter with "breaking" changes for older versions of Faraday
|
# This way, we can release minor versions of the adapter with "breaking" changes for older versions of Faraday
|
||||||
# and then bump the version requirement on the next compatible version of faraday.
|
# and then bump the version requirement on the next compatible version of faraday.
|
||||||
spec.add_dependency 'faraday-net_http', '>= 2.0', '< 3.1'
|
spec.add_dependency 'faraday-net_http', '>= 2.0', '< 3.5'
|
||||||
spec.add_dependency 'ruby2_keywords', '>= 0.0.4'
|
spec.add_dependency 'json'
|
||||||
|
spec.add_dependency 'logger'
|
||||||
|
|
||||||
# Includes `examples` and `spec` to allow external adapter gems to run Faraday unit and integration tests
|
# Includes `examples` and `spec` to allow external adapter gems to run Faraday unit and integration tests
|
||||||
spec.files = Dir['CHANGELOG.md', '{examples,lib,spec}/**/*', 'LICENSE.md', 'Rakefile', 'README.md']
|
spec.files = Dir['CHANGELOG.md', '{examples,lib,spec}/**/*', 'LICENSE.md', 'Rakefile', 'README.md']
|
||||||
@ -33,6 +33,7 @@ Gem::Specification.new do |spec|
|
|||||||
'changelog_uri' =>
|
'changelog_uri' =>
|
||||||
"https://github.com/lostisland/faraday/releases/tag/v#{spec.version}",
|
"https://github.com/lostisland/faraday/releases/tag/v#{spec.version}",
|
||||||
'source_code_uri' => 'https://github.com/lostisland/faraday',
|
'source_code_uri' => 'https://github.com/lostisland/faraday',
|
||||||
'bug_tracker_uri' => 'https://github.com/lostisland/faraday/issues'
|
'bug_tracker_uri' => 'https://github.com/lostisland/faraday/issues',
|
||||||
|
'rubygems_mfa_required' => 'true'
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'cgi'
|
require 'cgi/escape'
|
||||||
|
require 'cgi/util' if RUBY_VERSION < '3.5'
|
||||||
require 'date'
|
require 'date'
|
||||||
require 'set'
|
require 'set'
|
||||||
require 'forwardable'
|
require 'forwardable'
|
||||||
|
@ -26,7 +26,7 @@ module Faraday
|
|||||||
self.supports_parallel = false
|
self.supports_parallel = false
|
||||||
|
|
||||||
def initialize(_app = nil, opts = {}, &block)
|
def initialize(_app = nil, opts = {}, &block)
|
||||||
@app = ->(env) { env.response }
|
@app = lambda(&:response)
|
||||||
@connection_options = opts
|
@connection_options = opts
|
||||||
@config_block = block
|
@config_block = block
|
||||||
end
|
end
|
||||||
|
@ -15,7 +15,7 @@ module Faraday
|
|||||||
class Connection
|
class Connection
|
||||||
# A Set of allowed HTTP verbs.
|
# A Set of allowed HTTP verbs.
|
||||||
METHODS = Set.new %i[get post put delete head patch options trace]
|
METHODS = Set.new %i[get post put delete head patch options trace]
|
||||||
USER_AGENT = "Faraday v#{VERSION}"
|
USER_AGENT = "Faraday v#{VERSION}".freeze
|
||||||
|
|
||||||
# @return [Hash] URI query unencoded key/value pairs.
|
# @return [Hash] URI query unencoded key/value pairs.
|
||||||
attr_reader :params
|
attr_reader :params
|
||||||
@ -314,15 +314,23 @@ module Faraday
|
|||||||
#
|
#
|
||||||
# @yield a block to execute multiple requests.
|
# @yield a block to execute multiple requests.
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def in_parallel(manager = nil)
|
def in_parallel(manager = nil, &block)
|
||||||
@parallel_manager = manager || default_parallel_manager do
|
@parallel_manager = manager || default_parallel_manager do
|
||||||
warn 'Warning: `in_parallel` called but no parallel-capable adapter ' \
|
warn 'Warning: `in_parallel` called but no parallel-capable adapter ' \
|
||||||
'on Faraday stack'
|
'on Faraday stack'
|
||||||
warn caller[2, 10].join("\n")
|
warn caller[2, 10].join("\n")
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
yield
|
return yield unless @parallel_manager
|
||||||
@parallel_manager&.run
|
|
||||||
|
if @parallel_manager.respond_to?(:execute)
|
||||||
|
# Execute is the new method that is responsible for executing the block.
|
||||||
|
@parallel_manager.execute(&block)
|
||||||
|
else
|
||||||
|
# TODO: Old behaviour, deprecate and remove in 3.0
|
||||||
|
yield
|
||||||
|
@parallel_manager.run
|
||||||
|
end
|
||||||
ensure
|
ensure
|
||||||
@parallel_manager = nil
|
@parallel_manager = nil
|
||||||
end
|
end
|
||||||
@ -423,8 +431,8 @@ module Faraday
|
|||||||
#
|
#
|
||||||
# @param method [Symbol] HTTP method.
|
# @param method [Symbol] HTTP method.
|
||||||
# @param url [String, URI, nil] String or URI to access.
|
# @param url [String, URI, nil] String or URI to access.
|
||||||
# @param body [String, nil] The request body that will eventually be converted to
|
# @param body [String, Hash, Array, nil] The request body that will eventually be converted to
|
||||||
# a string.
|
# a string; middlewares can be used to support more complex types.
|
||||||
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||||
#
|
#
|
||||||
# @return [Faraday::Response]
|
# @return [Faraday::Response]
|
||||||
@ -473,7 +481,8 @@ module Faraday
|
|||||||
if url && !base.path.end_with?('/')
|
if url && !base.path.end_with?('/')
|
||||||
base.path = "#{base.path}/" # ensure trailing slash
|
base.path = "#{base.path}/" # ensure trailing slash
|
||||||
end
|
end
|
||||||
url = url.to_s.gsub(':', '%3A') if URI.parse(url.to_s).opaque
|
# Ensure relative url will be parsed correctly (such as `service:search` )
|
||||||
|
url = "./#{url}" if url.respond_to?(:start_with?) && !url.start_with?('http://', 'https://', '/', './', '../')
|
||||||
uri = url ? base + url : base
|
uri = url ? base + url : base
|
||||||
if params
|
if params
|
||||||
uri.query = params.to_query(params_encoder || options.params_encoder)
|
uri.query = params.to_query(params_encoder || options.params_encoder)
|
||||||
|
@ -102,7 +102,7 @@ module Faraday
|
|||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
SUBKEYS_REGEX = /[^\[\]]+(?:\]?\[\])?/.freeze
|
SUBKEYS_REGEX = /[^\[\]]+(?:\]?\[\])?/
|
||||||
|
|
||||||
def decode_pair(key, value, context)
|
def decode_pair(key, value, context)
|
||||||
subkeys = key.scan(SUBKEYS_REGEX)
|
subkeys = key.scan(SUBKEYS_REGEX)
|
||||||
|
@ -79,12 +79,46 @@ module Faraday
|
|||||||
|
|
||||||
# Pulls out potential parent exception and response hash.
|
# Pulls out potential parent exception and response hash.
|
||||||
def exc_msg_and_response(exc, response = nil)
|
def exc_msg_and_response(exc, response = nil)
|
||||||
return [exc, exc.message, response] if exc.respond_to?(:backtrace)
|
case exc
|
||||||
|
when Exception
|
||||||
|
[exc, exc.message, response]
|
||||||
|
when Hash
|
||||||
|
[nil, build_error_message_from_hash(exc), exc]
|
||||||
|
when Faraday::Env
|
||||||
|
[nil, build_error_message_from_env(exc), exc]
|
||||||
|
else
|
||||||
|
[nil, exc.to_s, response]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return [nil, "the server responded with status #{exc[:status]}", exc] \
|
private
|
||||||
if exc.respond_to?(:each_key)
|
|
||||||
|
|
||||||
[nil, exc.to_s, response]
|
def build_error_message_from_hash(hash)
|
||||||
|
# Be defensive with external Hash objects - they might be missing keys
|
||||||
|
status = hash.fetch(:status, nil)
|
||||||
|
request = hash.fetch(:request, nil)
|
||||||
|
|
||||||
|
return fallback_error_message(status) if request.nil?
|
||||||
|
|
||||||
|
method = request.fetch(:method, nil)
|
||||||
|
url = request.fetch(:url, nil)
|
||||||
|
build_status_error_message(status, method, url)
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_error_message_from_env(env)
|
||||||
|
# Faraday::Env is internal - we can make reasonable assumptions about its structure
|
||||||
|
build_status_error_message(env.status, env.method, env.url)
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_status_error_message(status, method, url)
|
||||||
|
method_str = method ? method.to_s.upcase : ''
|
||||||
|
url_str = url ? url.to_s : ''
|
||||||
|
"the server responded with status #{status} for #{method_str} #{url_str}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def fallback_error_message(status)
|
||||||
|
"the server responded with status #{status} - method and url are not available " \
|
||||||
|
'due to include_request: false on Faraday::Response::RaiseError middleware'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -158,4 +192,8 @@ module Faraday
|
|||||||
# Raised by middlewares that parse the response, like the JSON response middleware.
|
# Raised by middlewares that parse the response, like the JSON response middleware.
|
||||||
class ParsingError < Error
|
class ParsingError < Error
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Raised by Faraday::Middleware and subclasses when invalid default_options are used
|
||||||
|
class InitializationError < Error
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -23,8 +23,8 @@ module Faraday
|
|||||||
def_delegators :@logger, :debug, :info, :warn, :error, :fatal
|
def_delegators :@logger, :debug, :info, :warn, :error, :fatal
|
||||||
|
|
||||||
def request(env)
|
def request(env)
|
||||||
public_send(log_level, 'request') do
|
public_send(log_level) do
|
||||||
"#{env.method.upcase} #{apply_filters(env.url.to_s)}"
|
"request: #{env.method.upcase} #{apply_filters(env.url.to_s)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
log_headers('request', env.request_headers) if log_headers?(:request)
|
log_headers('request', env.request_headers) if log_headers?(:request)
|
||||||
@ -32,7 +32,7 @@ module Faraday
|
|||||||
end
|
end
|
||||||
|
|
||||||
def response(env)
|
def response(env)
|
||||||
public_send(log_level, 'response') { "Status #{env.status}" }
|
public_send(log_level) { "response: Status #{env.status}" }
|
||||||
|
|
||||||
log_headers('response', env.response_headers) if log_headers?(:response)
|
log_headers('response', env.response_headers) if log_headers?(:response)
|
||||||
log_body('response', env[:body]) if env[:body] && log_body?(:response)
|
log_body('response', env[:body]) if env[:body] && log_body?(:response)
|
||||||
@ -41,7 +41,7 @@ module Faraday
|
|||||||
def exception(exc)
|
def exception(exc)
|
||||||
return unless log_errors?
|
return unless log_errors?
|
||||||
|
|
||||||
public_send(log_level, 'error') { exc.full_message }
|
public_send(log_level) { "error: #{exc.full_message}" }
|
||||||
|
|
||||||
log_headers('error', exc.response_headers) if exc.respond_to?(:response_headers) && log_headers?(:error)
|
log_headers('error', exc.response_headers) if exc.respond_to?(:response_headers) && log_headers?(:error)
|
||||||
return unless exc.respond_to?(:response_body) && exc.response_body && log_body?(:error)
|
return unless exc.respond_to?(:response_body) && exc.response_body && log_body?(:error)
|
||||||
@ -107,11 +107,11 @@ module Faraday
|
|||||||
end
|
end
|
||||||
|
|
||||||
def log_headers(type, headers)
|
def log_headers(type, headers)
|
||||||
public_send(log_level, type) { apply_filters(dump_headers(headers)) }
|
public_send(log_level) { "#{type}: #{apply_filters(dump_headers(headers))}" }
|
||||||
end
|
end
|
||||||
|
|
||||||
def log_body(type, body)
|
def log_body(type, body)
|
||||||
public_send(log_level, type) { apply_filters(dump_body(body)) }
|
public_send(log_level) { "#{type}: #{apply_filters(dump_body(body))}" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'monitor'
|
||||||
|
|
||||||
module Faraday
|
module Faraday
|
||||||
# Middleware is the basic base class of any Faraday middleware.
|
# Middleware is the basic base class of any Faraday middleware.
|
||||||
class Middleware
|
class Middleware
|
||||||
@ -7,9 +9,46 @@ module Faraday
|
|||||||
|
|
||||||
attr_reader :app, :options
|
attr_reader :app, :options
|
||||||
|
|
||||||
|
DEFAULT_OPTIONS = {}.freeze
|
||||||
|
LOCK = Mutex.new
|
||||||
|
|
||||||
def initialize(app = nil, options = {})
|
def initialize(app = nil, options = {})
|
||||||
@app = app
|
@app = app
|
||||||
@options = options
|
@options = self.class.default_options.merge(options)
|
||||||
|
end
|
||||||
|
|
||||||
|
class << self
|
||||||
|
# Faraday::Middleware::default_options= allows user to set default options at the Faraday::Middleware
|
||||||
|
# class level.
|
||||||
|
#
|
||||||
|
# @example Set the Faraday::Response::RaiseError option, `include_request` to `false`
|
||||||
|
# my_app/config/initializers/my_faraday_middleware.rb
|
||||||
|
#
|
||||||
|
# Faraday::Response::RaiseError.default_options = { include_request: false }
|
||||||
|
#
|
||||||
|
def default_options=(options = {})
|
||||||
|
validate_default_options(options)
|
||||||
|
LOCK.synchronize do
|
||||||
|
@default_options = default_options.merge(options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# default_options attr_reader that initializes class instance variable
|
||||||
|
# with the values of any Faraday::Middleware defaults, and merges with
|
||||||
|
# subclass defaults
|
||||||
|
def default_options
|
||||||
|
@default_options ||= DEFAULT_OPTIONS.merge(self::DEFAULT_OPTIONS)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def validate_default_options(options)
|
||||||
|
invalid_keys = options.keys.reject { |opt| self::DEFAULT_OPTIONS.key?(opt) }
|
||||||
|
return unless invalid_keys.any?
|
||||||
|
|
||||||
|
raise(Faraday::InitializationError,
|
||||||
|
"Invalid options provided. Keys not found in #{self}::DEFAULT_OPTIONS: #{invalid_keys.join(', ')}")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(env)
|
def call(env)
|
||||||
|
@ -60,7 +60,7 @@ module Faraday
|
|||||||
:reason_phrase, :response_body) do
|
:reason_phrase, :response_body) do
|
||||||
const_set(:ContentLength, 'Content-Length')
|
const_set(:ContentLength, 'Content-Length')
|
||||||
const_set(:StatusesWithoutBody, Set.new([204, 304]))
|
const_set(:StatusesWithoutBody, Set.new([204, 304]))
|
||||||
const_set(:SuccessfulStatuses, (200..299).freeze)
|
const_set(:SuccessfulStatuses, 200..299)
|
||||||
|
|
||||||
# A Set of HTTP verbs that typically send a body. If no body is set for
|
# A Set of HTTP verbs that typically send a body. If no body is set for
|
||||||
# these requests, the Content-Length header is set to 0.
|
# these requests, the Content-Length header is set to 0.
|
||||||
@ -169,7 +169,7 @@ module Faraday
|
|||||||
def stream_response(&block)
|
def stream_response(&block)
|
||||||
size = 0
|
size = 0
|
||||||
yielded = false
|
yielded = false
|
||||||
block_result = block.call do |chunk| # rubocop:disable Performance/RedundantBlockCall
|
block_result = block.call do |chunk|
|
||||||
if chunk.bytesize.positive? || size.positive?
|
if chunk.bytesize.positive? || size.positive?
|
||||||
yielded = true
|
yielded = true
|
||||||
size += chunk.bytesize
|
size += chunk.bytesize
|
||||||
|
@ -22,8 +22,10 @@ module Faraday
|
|||||||
when URI
|
when URI
|
||||||
value = { uri: value }
|
value = { uri: value }
|
||||||
when Hash, Options
|
when Hash, Options
|
||||||
if (uri = value.delete(:uri))
|
if value[:uri]
|
||||||
value[:uri] = Utils.URI(uri)
|
value = value.dup.tap do |duped|
|
||||||
|
duped[:uri] = Utils.URI(duped[:uri])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -11,6 +11,9 @@ module Faraday
|
|||||||
# # @return [Boolean] whether to enable hostname verification on server certificates
|
# # @return [Boolean] whether to enable hostname verification on server certificates
|
||||||
# # during the handshake or not (see https://github.com/ruby/openssl/pull/60)
|
# # during the handshake or not (see https://github.com/ruby/openssl/pull/60)
|
||||||
# #
|
# #
|
||||||
|
# # @!attribute hostname
|
||||||
|
# # @return [String] Server hostname used for SNI (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLSocket.html#method-i-hostname-3D)
|
||||||
|
# #
|
||||||
# # @!attribute ca_file
|
# # @!attribute ca_file
|
||||||
# # @return [String] CA file
|
# # @return [String] CA file
|
||||||
# #
|
# #
|
||||||
@ -46,12 +49,15 @@ module Faraday
|
|||||||
# #
|
# #
|
||||||
# # @!attribute max_version
|
# # @!attribute max_version
|
||||||
# # @return [String, Symbol] maximum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D)
|
# # @return [String, Symbol] maximum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D)
|
||||||
|
# #
|
||||||
|
# # @!attribute ciphers
|
||||||
|
# # @return [String] cipher list in OpenSSL format (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-ciphers-3D)
|
||||||
# class SSLOptions < Options; end
|
# class SSLOptions < Options; end
|
||||||
SSLOptions = Options.new(:verify, :verify_hostname,
|
SSLOptions = Options.new(:verify, :verify_hostname, :hostname,
|
||||||
:ca_file, :ca_path, :verify_mode,
|
:ca_file, :ca_path, :verify_mode,
|
||||||
:cert_store, :client_cert, :client_key,
|
:cert_store, :client_cert, :client_key,
|
||||||
:certificate, :private_key, :verify_depth,
|
:certificate, :private_key, :verify_depth,
|
||||||
:version, :min_version, :max_version) do
|
:version, :min_version, :max_version, :ciphers) do
|
||||||
# @return [Boolean] true if should verify
|
# @return [Boolean] true if should verify
|
||||||
def verify?
|
def verify?
|
||||||
verify != false
|
verify != false
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'ruby2_keywords'
|
|
||||||
require 'faraday/adapter_registry'
|
require 'faraday/adapter_registry'
|
||||||
|
|
||||||
module Faraday
|
module Faraday
|
||||||
@ -28,10 +27,11 @@ module Faraday
|
|||||||
|
|
||||||
attr_reader :name
|
attr_reader :name
|
||||||
|
|
||||||
ruby2_keywords def initialize(klass, *args, &block)
|
def initialize(klass, *args, **kwargs, &block)
|
||||||
@name = klass.to_s
|
@name = klass.to_s
|
||||||
REGISTRY.set(klass) if klass.respond_to?(:name)
|
REGISTRY.set(klass) if klass.respond_to?(:name)
|
||||||
@args = args
|
@args = args
|
||||||
|
@kwargs = kwargs
|
||||||
@block = block
|
@block = block
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ module Faraday
|
|||||||
end
|
end
|
||||||
|
|
||||||
def build(app = nil)
|
def build(app = nil)
|
||||||
klass.new(app, *@args, &@block)
|
klass.new(app, *@args, **@kwargs, &@block)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -89,52 +89,52 @@ module Faraday
|
|||||||
@handlers.frozen?
|
@handlers.frozen?
|
||||||
end
|
end
|
||||||
|
|
||||||
ruby2_keywords def use(klass, *args, &block)
|
def use(klass, ...)
|
||||||
if klass.is_a? Symbol
|
if klass.is_a? Symbol
|
||||||
use_symbol(Faraday::Middleware, klass, *args, &block)
|
use_symbol(Faraday::Middleware, klass, ...)
|
||||||
else
|
else
|
||||||
raise_if_locked
|
raise_if_locked
|
||||||
raise_if_adapter(klass)
|
raise_if_adapter(klass)
|
||||||
@handlers << self.class::Handler.new(klass, *args, &block)
|
@handlers << self.class::Handler.new(klass, ...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
ruby2_keywords def request(key, *args, &block)
|
def request(key, ...)
|
||||||
use_symbol(Faraday::Request, key, *args, &block)
|
use_symbol(Faraday::Request, key, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
ruby2_keywords def response(key, *args, &block)
|
def response(...)
|
||||||
use_symbol(Faraday::Response, key, *args, &block)
|
use_symbol(Faraday::Response, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
ruby2_keywords def adapter(klass = NO_ARGUMENT, *args, &block)
|
def adapter(klass = NO_ARGUMENT, *args, **kwargs, &block)
|
||||||
return @adapter if klass == NO_ARGUMENT || klass.nil?
|
return @adapter if klass == NO_ARGUMENT || klass.nil?
|
||||||
|
|
||||||
klass = Faraday::Adapter.lookup_middleware(klass) if klass.is_a?(Symbol)
|
klass = Faraday::Adapter.lookup_middleware(klass) if klass.is_a?(Symbol)
|
||||||
@adapter = self.class::Handler.new(klass, *args, &block)
|
@adapter = self.class::Handler.new(klass, *args, **kwargs, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
## methods to push onto the various positions in the stack:
|
## methods to push onto the various positions in the stack:
|
||||||
|
|
||||||
ruby2_keywords def insert(index, *args, &block)
|
def insert(index, ...)
|
||||||
raise_if_locked
|
raise_if_locked
|
||||||
index = assert_index(index)
|
index = assert_index(index)
|
||||||
handler = self.class::Handler.new(*args, &block)
|
handler = self.class::Handler.new(...)
|
||||||
@handlers.insert(index, handler)
|
@handlers.insert(index, handler)
|
||||||
end
|
end
|
||||||
|
|
||||||
alias insert_before insert
|
alias insert_before insert
|
||||||
|
|
||||||
ruby2_keywords def insert_after(index, *args, &block)
|
def insert_after(index, ...)
|
||||||
index = assert_index(index)
|
index = assert_index(index)
|
||||||
insert(index + 1, *args, &block)
|
insert(index + 1, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
ruby2_keywords def swap(index, *args, &block)
|
def swap(index, ...)
|
||||||
raise_if_locked
|
raise_if_locked
|
||||||
index = assert_index(index)
|
index = assert_index(index)
|
||||||
@handlers.delete_at(index)
|
@handlers.delete_at(index)
|
||||||
insert(index, *args, &block)
|
insert(index, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(handler)
|
def delete(handler)
|
||||||
@ -221,7 +221,7 @@ module Faraday
|
|||||||
end
|
end
|
||||||
|
|
||||||
def raise_if_adapter(klass)
|
def raise_if_adapter(klass)
|
||||||
return unless is_adapter?(klass)
|
return unless klass <= Faraday::Adapter
|
||||||
|
|
||||||
raise 'Adapter should be set using the `adapter` method, not `use`'
|
raise 'Adapter should be set using the `adapter` method, not `use`'
|
||||||
end
|
end
|
||||||
@ -234,12 +234,8 @@ module Faraday
|
|||||||
!@adapter.nil?
|
!@adapter.nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_adapter?(klass) # rubocop:disable Naming/PredicateName
|
def use_symbol(mod, key, ...)
|
||||||
klass <= Faraday::Adapter
|
use(mod.lookup_middleware(key), ...)
|
||||||
end
|
|
||||||
|
|
||||||
ruby2_keywords def use_symbol(mod, key, *args, &block)
|
|
||||||
use(mod.lookup_middleware(key), *args, &block)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_index(index)
|
def assert_index(index)
|
||||||
|
@ -13,7 +13,7 @@ module Faraday
|
|||||||
# Doesn't try to encode bodies that already are in string form.
|
# Doesn't try to encode bodies that already are in string form.
|
||||||
class Json < Middleware
|
class Json < Middleware
|
||||||
MIME_TYPE = 'application/json'
|
MIME_TYPE = 'application/json'
|
||||||
MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}.freeze
|
MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}
|
||||||
|
|
||||||
def on_request(env)
|
def on_request(env)
|
||||||
match_content_type(env) do |data|
|
match_content_type(env) do |data|
|
||||||
|
@ -60,7 +60,8 @@ module Faraday
|
|||||||
@decoder_options =
|
@decoder_options =
|
||||||
if @decoder_options.is_a?(Array) && @decoder_options.size >= 2
|
if @decoder_options.is_a?(Array) && @decoder_options.size >= 2
|
||||||
@decoder_options.slice(0, 2)
|
@decoder_options.slice(0, 2)
|
||||||
elsif @decoder_options.respond_to?(:load)
|
elsif @decoder_options&.respond_to?(:load) # rubocop:disable Lint/RedundantSafeNavigation
|
||||||
|
# In some versions of Rails, `nil` responds to `load` - hence the safe navigation check above
|
||||||
[@decoder_options, :load]
|
[@decoder_options, :load]
|
||||||
else
|
else
|
||||||
[::JSON, :parse]
|
[::JSON, :parse]
|
||||||
|
@ -10,11 +10,13 @@ module Faraday
|
|||||||
# lifecycle to a given Logger object. By default, this logs to STDOUT. See
|
# lifecycle to a given Logger object. By default, this logs to STDOUT. See
|
||||||
# Faraday::Logging::Formatter to see specifically what is logged.
|
# Faraday::Logging::Formatter to see specifically what is logged.
|
||||||
class Logger < Middleware
|
class Logger < Middleware
|
||||||
|
DEFAULT_OPTIONS = { formatter: Logging::Formatter }.merge(Logging::Formatter::DEFAULT_OPTIONS).freeze
|
||||||
|
|
||||||
def initialize(app, logger = nil, options = {})
|
def initialize(app, logger = nil, options = {})
|
||||||
super(app)
|
super(app, options)
|
||||||
logger ||= ::Logger.new($stdout)
|
logger ||= ::Logger.new($stdout)
|
||||||
formatter_class = options.delete(:formatter) || Logging::Formatter
|
formatter_class = @options.delete(:formatter)
|
||||||
@formatter = formatter_class.new(logger: logger, options: options)
|
@formatter = formatter_class.new(logger: logger, options: @options)
|
||||||
yield @formatter if block_given?
|
yield @formatter if block_given?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -6,32 +6,32 @@ module Faraday
|
|||||||
# client or server error responses.
|
# client or server error responses.
|
||||||
class RaiseError < Middleware
|
class RaiseError < Middleware
|
||||||
# rubocop:disable Naming/ConstantName
|
# rubocop:disable Naming/ConstantName
|
||||||
ClientErrorStatuses = (400...500).freeze
|
ClientErrorStatuses = (400...500)
|
||||||
ServerErrorStatuses = (500...600).freeze
|
ServerErrorStatuses = (500...600)
|
||||||
|
ClientErrorStatusesWithCustomExceptions = {
|
||||||
|
400 => Faraday::BadRequestError,
|
||||||
|
401 => Faraday::UnauthorizedError,
|
||||||
|
403 => Faraday::ForbiddenError,
|
||||||
|
404 => Faraday::ResourceNotFound,
|
||||||
|
408 => Faraday::RequestTimeoutError,
|
||||||
|
409 => Faraday::ConflictError,
|
||||||
|
422 => Faraday::UnprocessableEntityError,
|
||||||
|
429 => Faraday::TooManyRequestsError
|
||||||
|
}.freeze
|
||||||
# rubocop:enable Naming/ConstantName
|
# rubocop:enable Naming/ConstantName
|
||||||
|
|
||||||
|
DEFAULT_OPTIONS = { include_request: true, allowed_statuses: [] }.freeze
|
||||||
|
|
||||||
def on_complete(env)
|
def on_complete(env)
|
||||||
|
return if Array(options[:allowed_statuses]).include?(env[:status])
|
||||||
|
|
||||||
case env[:status]
|
case env[:status]
|
||||||
when 400
|
when *ClientErrorStatusesWithCustomExceptions.keys
|
||||||
raise Faraday::BadRequestError, response_values(env)
|
raise ClientErrorStatusesWithCustomExceptions[env[:status]], response_values(env)
|
||||||
when 401
|
|
||||||
raise Faraday::UnauthorizedError, response_values(env)
|
|
||||||
when 403
|
|
||||||
raise Faraday::ForbiddenError, response_values(env)
|
|
||||||
when 404
|
|
||||||
raise Faraday::ResourceNotFound, response_values(env)
|
|
||||||
when 407
|
when 407
|
||||||
# mimic the behavior that we get with proxy requests with HTTPS
|
# mimic the behavior that we get with proxy requests with HTTPS
|
||||||
msg = %(407 "Proxy Authentication Required")
|
msg = %(407 "Proxy Authentication Required")
|
||||||
raise Faraday::ProxyAuthError.new(msg, response_values(env))
|
raise Faraday::ProxyAuthError.new(msg, response_values(env))
|
||||||
when 408
|
|
||||||
raise Faraday::RequestTimeoutError, response_values(env)
|
|
||||||
when 409
|
|
||||||
raise Faraday::ConflictError, response_values(env)
|
|
||||||
when 422
|
|
||||||
raise Faraday::UnprocessableEntityError, response_values(env)
|
|
||||||
when 429
|
|
||||||
raise Faraday::TooManyRequestsError, response_values(env)
|
|
||||||
when ClientErrorStatuses
|
when ClientErrorStatuses
|
||||||
raise Faraday::ClientError, response_values(env)
|
raise Faraday::ClientError, response_values(env)
|
||||||
when ServerErrorStatuses
|
when ServerErrorStatuses
|
||||||
@ -58,7 +58,7 @@ module Faraday
|
|||||||
|
|
||||||
# Include the request data by default. If the middleware was explicitly
|
# Include the request data by default. If the middleware was explicitly
|
||||||
# configured to _not_ include request data, then omit it.
|
# configured to _not_ include request data, then omit it.
|
||||||
return response unless options.fetch(:include_request, true)
|
return response unless options[:include_request]
|
||||||
|
|
||||||
response.merge(
|
response.merge(
|
||||||
request: {
|
request: {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'base64'
|
|
||||||
require 'uri'
|
require 'uri'
|
||||||
require 'faraday/utils/headers'
|
require 'faraday/utils/headers'
|
||||||
require 'faraday/utils/params_hash'
|
require 'faraday/utils/params_hash'
|
||||||
@ -26,7 +25,7 @@ module Faraday
|
|||||||
attr_writer :default_space_encoding
|
attr_writer :default_space_encoding
|
||||||
end
|
end
|
||||||
|
|
||||||
ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/.freeze
|
ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
|
||||||
|
|
||||||
def escape(str)
|
def escape(str)
|
||||||
str.to_s.gsub(ESCAPE_RE) do |match|
|
str.to_s.gsub(ESCAPE_RE) do |match|
|
||||||
@ -38,7 +37,7 @@ module Faraday
|
|||||||
CGI.unescape str.to_s
|
CGI.unescape str.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
DEFAULT_SEP = /[&;] */n.freeze
|
DEFAULT_SEP = /[&;] */n
|
||||||
|
|
||||||
# Adapted from Rack
|
# Adapted from Rack
|
||||||
def parse_query(query)
|
def parse_query(query)
|
||||||
@ -54,7 +53,7 @@ module Faraday
|
|||||||
end
|
end
|
||||||
|
|
||||||
def basic_header_from(login, pass)
|
def basic_header_from(login, pass)
|
||||||
value = Base64.encode64("#{login}:#{pass}")
|
value = ["#{login}:#{pass}"].pack('m') # Base64 encoding
|
||||||
value.delete!("\n")
|
value.delete!("\n")
|
||||||
"Basic #{value}"
|
"Basic #{value}"
|
||||||
end
|
end
|
||||||
|
@ -62,10 +62,10 @@ module Faraday
|
|||||||
super(key, val)
|
super(key, val)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch(key, *args, &block)
|
def fetch(key, ...)
|
||||||
key = KeyMap[key]
|
key = KeyMap[key]
|
||||||
key = @names.fetch(key.downcase, key)
|
key = @names.fetch(key.downcase, key)
|
||||||
super(key, *args, &block)
|
super(key, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(key)
|
def delete(key)
|
||||||
@ -77,6 +77,12 @@ module Faraday
|
|||||||
super(key)
|
super(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def dig(key, *rest)
|
||||||
|
key = KeyMap[key]
|
||||||
|
key = @names.fetch(key.downcase, key)
|
||||||
|
super(key, *rest)
|
||||||
|
end
|
||||||
|
|
||||||
def include?(key)
|
def include?(key)
|
||||||
@names.include? key.downcase
|
@names.include? key.downcase
|
||||||
end
|
end
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module Faraday
|
module Faraday
|
||||||
VERSION = '2.8.1'
|
VERSION = '2.13.4'
|
||||||
end
|
end
|
||||||
|
32
package-lock.json
generated
32
package-lock.json
generated
@ -169,11 +169,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/braces": {
|
"node_modules/braces": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fill-range": "^7.0.1"
|
"fill-range": "^7.1.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@ -612,9 +613,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fill-range": {
|
"node_modules/fill-range": {
|
||||||
"version": "7.0.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"to-regex-range": "^5.0.1"
|
"to-regex-range": "^5.0.1"
|
||||||
},
|
},
|
||||||
@ -931,6 +933,7 @@
|
|||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
}
|
}
|
||||||
@ -1444,9 +1447,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "6.3.0",
|
"version": "6.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||||
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
}
|
}
|
||||||
@ -1631,6 +1635,7 @@
|
|||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-number": "^7.0.0"
|
"is-number": "^7.0.0"
|
||||||
},
|
},
|
||||||
@ -1895,9 +1900,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "7.5.9",
|
"version": "7.5.10",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||||
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
|
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.3.0"
|
"node": ">=8.3.0"
|
||||||
},
|
},
|
||||||
|
@ -300,14 +300,14 @@ RSpec.describe Faraday::Connection do
|
|||||||
it 'joins url to base when used relative path' do
|
it 'joins url to base when used relative path' do
|
||||||
conn = Faraday.new(url: url)
|
conn = Faraday.new(url: url)
|
||||||
uri = conn.build_exclusive_url('service:search?limit=400')
|
uri = conn.build_exclusive_url('service:search?limit=400')
|
||||||
expect(uri.to_s).to eq('http://service.com/service%3Asearch?limit=400')
|
expect(uri.to_s).to eq('http://service.com/service:search?limit=400')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'joins url to base when used with path prefix' do
|
it 'joins url to base when used with path prefix' do
|
||||||
conn = Faraday.new(url: url)
|
conn = Faraday.new(url: url)
|
||||||
conn.path_prefix = '/api'
|
conn.path_prefix = '/api'
|
||||||
uri = conn.build_exclusive_url('service:search?limit=400')
|
uri = conn.build_exclusive_url('service:search?limit=400')
|
||||||
expect(uri.to_s).to eq('http://service.com/api/service%3Asearch?limit=400')
|
expect(uri.to_s).to eq('http://service.com/api/service:search?limit=400')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -23,8 +23,12 @@ RSpec.describe Faraday::Error do
|
|||||||
|
|
||||||
it { expect(subject.wrapped_exception).to be_nil }
|
it { expect(subject.wrapped_exception).to be_nil }
|
||||||
it { expect(subject.response).to eq(exception) }
|
it { expect(subject.response).to eq(exception) }
|
||||||
it { expect(subject.message).to eq('the server responded with status 400') }
|
it { expect(subject.message).to eq('the server responded with status 400 - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') }
|
||||||
it { expect(subject.inspect).to eq('#<Faraday::Error response={:status=>400}>') }
|
if RUBY_VERSION >= '3.4'
|
||||||
|
it { expect(subject.inspect).to eq('#<Faraday::Error response={status: 400}>') }
|
||||||
|
else
|
||||||
|
it { expect(subject.inspect).to eq('#<Faraday::Error response={:status=>400}>') }
|
||||||
|
end
|
||||||
it { expect(subject.response_status).to eq(400) }
|
it { expect(subject.response_status).to eq(400) }
|
||||||
it { expect(subject.response_headers).to be_nil }
|
it { expect(subject.response_headers).to be_nil }
|
||||||
it { expect(subject.response_body).to be_nil }
|
it { expect(subject.response_body).to be_nil }
|
||||||
@ -61,7 +65,11 @@ RSpec.describe Faraday::Error do
|
|||||||
it { expect(subject.wrapped_exception).to be_nil }
|
it { expect(subject.wrapped_exception).to be_nil }
|
||||||
it { expect(subject.response).to eq(response) }
|
it { expect(subject.response).to eq(response) }
|
||||||
it { expect(subject.message).to eq('custom message') }
|
it { expect(subject.message).to eq('custom message') }
|
||||||
it { expect(subject.inspect).to eq('#<Faraday::Error response={:status=>400}>') }
|
if RUBY_VERSION >= '3.4'
|
||||||
|
it { expect(subject.inspect).to eq('#<Faraday::Error response={status: 400}>') }
|
||||||
|
else
|
||||||
|
it { expect(subject.inspect).to eq('#<Faraday::Error response={:status=>400}>') }
|
||||||
|
end
|
||||||
it { expect(subject.response_status).to eq(400) }
|
it { expect(subject.response_status).to eq(400) }
|
||||||
it { expect(subject.response_headers).to be_nil }
|
it { expect(subject.response_headers).to be_nil }
|
||||||
it { expect(subject.response_body).to be_nil }
|
it { expect(subject.response_body).to be_nil }
|
||||||
@ -81,5 +89,87 @@ RSpec.describe Faraday::Error do
|
|||||||
it { expect(subject.response_headers).to eq(headers) }
|
it { expect(subject.response_headers).to eq(headers) }
|
||||||
it { expect(subject.response_body).to eq(body) }
|
it { expect(subject.response_body).to eq(body) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with hash missing status key' do
|
||||||
|
let(:exception) { { body: 'error body' } }
|
||||||
|
|
||||||
|
it { expect(subject.wrapped_exception).to be_nil }
|
||||||
|
it { expect(subject.response).to eq(exception) }
|
||||||
|
it { expect(subject.message).to eq('the server responded with status - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with hash with status but missing request data' do
|
||||||
|
let(:exception) { { status: 404, body: 'not found' } } # missing request key
|
||||||
|
|
||||||
|
it { expect(subject.wrapped_exception).to be_nil }
|
||||||
|
it { expect(subject.response).to eq(exception) }
|
||||||
|
it { expect(subject.message).to eq('the server responded with status 404 - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with hash with status and request but missing method in request' do
|
||||||
|
let(:exception) { { status: 404, body: 'not found', request: { url: 'http://example.com/test' } } } # missing method
|
||||||
|
|
||||||
|
it { expect(subject.wrapped_exception).to be_nil }
|
||||||
|
it { expect(subject.response).to eq(exception) }
|
||||||
|
it { expect(subject.message).to eq('the server responded with status 404 for http://example.com/test') }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with hash with status and request but missing url in request' do
|
||||||
|
let(:exception) { { status: 404, body: 'not found', request: { method: :get } } } # missing url
|
||||||
|
|
||||||
|
it { expect(subject.wrapped_exception).to be_nil }
|
||||||
|
it { expect(subject.response).to eq(exception) }
|
||||||
|
it { expect(subject.message).to eq('the server responded with status 404 for GET ') }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with properly formed Faraday::Env' do
|
||||||
|
# This represents the normal case - a well-formed Faraday::Env object
|
||||||
|
# with all the standard properties populated as they would be during
|
||||||
|
# a typical HTTP request/response cycle
|
||||||
|
let(:exception) { Faraday::Env.new }
|
||||||
|
|
||||||
|
before do
|
||||||
|
exception.status = 500
|
||||||
|
exception.method = :post
|
||||||
|
exception.url = URI('https://api.example.com/users')
|
||||||
|
exception.request = Faraday::RequestOptions.new
|
||||||
|
exception.response_headers = { 'content-type' => 'application/json' }
|
||||||
|
exception.response_body = '{"error": "Internal server error"}'
|
||||||
|
exception.request_headers = { 'authorization' => 'Bearer token123' }
|
||||||
|
exception.request_body = '{"name": "John"}'
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect(subject.wrapped_exception).to be_nil }
|
||||||
|
it { expect(subject.response).to eq(exception) }
|
||||||
|
it { expect(subject.message).to eq('the server responded with status 500 for POST https://api.example.com/users') }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with Faraday::Env missing status key' do
|
||||||
|
let(:exception) { Faraday::Env.new }
|
||||||
|
|
||||||
|
before do
|
||||||
|
exception[:body] = 'error body'
|
||||||
|
# Intentionally not setting status
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect(subject.wrapped_exception).to be_nil }
|
||||||
|
it { expect(subject.response).to eq(exception) }
|
||||||
|
it { expect(subject.message).to eq('the server responded with status for ') }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with Faraday::Env with direct method and url properties' do
|
||||||
|
let(:exception) { Faraday::Env.new }
|
||||||
|
|
||||||
|
before do
|
||||||
|
exception.status = 404
|
||||||
|
exception.method = :get
|
||||||
|
exception.url = URI('http://example.com/test')
|
||||||
|
exception[:body] = 'not found'
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect(subject.wrapped_exception).to be_nil }
|
||||||
|
it { expect(subject.response).to eq(exception) }
|
||||||
|
it { expect(subject.message).to eq('the server responded with status 404 for GET http://example.com/test') }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -67,4 +67,147 @@ RSpec.describe Faraday::Middleware do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '::default_options' do
|
||||||
|
let(:subclass_no_options) { FaradayMiddlewareSubclasses::SubclassNoOptions }
|
||||||
|
let(:subclass_one_option) { FaradayMiddlewareSubclasses::SubclassOneOption }
|
||||||
|
let(:subclass_two_options) { FaradayMiddlewareSubclasses::SubclassTwoOptions }
|
||||||
|
|
||||||
|
def build_conn(resp_middleware)
|
||||||
|
Faraday.new do |c|
|
||||||
|
c.adapter :test do |stub|
|
||||||
|
stub.get('/success') { [200, {}, 'ok'] }
|
||||||
|
end
|
||||||
|
c.response resp_middleware
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
RSpec.shared_context 'reset @default_options' do
|
||||||
|
before(:each) do
|
||||||
|
FaradayMiddlewareSubclasses::SubclassNoOptions.instance_variable_set(:@default_options, nil)
|
||||||
|
FaradayMiddlewareSubclasses::SubclassOneOption.instance_variable_set(:@default_options, nil)
|
||||||
|
FaradayMiddlewareSubclasses::SubclassTwoOptions.instance_variable_set(:@default_options, nil)
|
||||||
|
Faraday::Middleware.instance_variable_set(:@default_options, nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
after(:all) do
|
||||||
|
FaradayMiddlewareSubclasses::SubclassNoOptions.instance_variable_set(:@default_options, nil)
|
||||||
|
FaradayMiddlewareSubclasses::SubclassOneOption.instance_variable_set(:@default_options, nil)
|
||||||
|
FaradayMiddlewareSubclasses::SubclassTwoOptions.instance_variable_set(:@default_options, nil)
|
||||||
|
Faraday::Middleware.instance_variable_set(:@default_options, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with subclass DEFAULT_OPTIONS defined' do
|
||||||
|
include_context 'reset @default_options'
|
||||||
|
|
||||||
|
context 'and without application options configured' do
|
||||||
|
let(:resp1) { build_conn(:one_option).get('/success') }
|
||||||
|
|
||||||
|
it 'has only subclass defaults' do
|
||||||
|
expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS)
|
||||||
|
expect(subclass_no_options.default_options).to eq(subclass_no_options::DEFAULT_OPTIONS)
|
||||||
|
expect(subclass_one_option.default_options).to eq(subclass_one_option::DEFAULT_OPTIONS)
|
||||||
|
expect(subclass_two_options.default_options).to eq(subclass_two_options::DEFAULT_OPTIONS)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect(resp1.body).to eq('ok') }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "and with one application's options changed" do
|
||||||
|
let(:resp2) { build_conn(:two_options).get('/success') }
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options = { some_option: false }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'only updates default options of target subclass' do
|
||||||
|
expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS)
|
||||||
|
expect(subclass_no_options.default_options).to eq(subclass_no_options::DEFAULT_OPTIONS)
|
||||||
|
expect(subclass_one_option.default_options).to eq(subclass_one_option::DEFAULT_OPTIONS)
|
||||||
|
expect(subclass_two_options.default_options).to eq({ some_option: false, some_other_option: false })
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect(resp2.body).to eq('ok') }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "and with two applications' options changed" do
|
||||||
|
let(:resp1) { build_conn(:one_option).get('/success') }
|
||||||
|
let(:resp2) { build_conn(:two_options).get('/success') }
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
FaradayMiddlewareSubclasses::SubclassOneOption.default_options = { some_other_option: true }
|
||||||
|
FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options = { some_option: false }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates subclasses and parent independent of each other' do
|
||||||
|
expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS)
|
||||||
|
expect(subclass_no_options.default_options).to eq(subclass_no_options::DEFAULT_OPTIONS)
|
||||||
|
expect(subclass_one_option.default_options).to eq({ some_other_option: true })
|
||||||
|
expect(subclass_two_options.default_options).to eq({ some_option: false, some_other_option: false })
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect(resp1.body).to eq('ok') }
|
||||||
|
it { expect(resp2.body).to eq('ok') }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with FARADAY::MIDDLEWARE DEFAULT_OPTIONS and with Subclass DEFAULT_OPTIONS' do
|
||||||
|
before(:each) do
|
||||||
|
stub_const('Faraday::Middleware::DEFAULT_OPTIONS', { its_magic: false })
|
||||||
|
end
|
||||||
|
|
||||||
|
# Must stub Faraday::Middleware::DEFAULT_OPTIONS before resetting default options
|
||||||
|
include_context 'reset @default_options'
|
||||||
|
|
||||||
|
context 'and without application options configured' do
|
||||||
|
let(:resp1) { build_conn(:one_option).get('/success') }
|
||||||
|
|
||||||
|
it 'has only subclass defaults' do
|
||||||
|
expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS)
|
||||||
|
expect(FaradayMiddlewareSubclasses::SubclassNoOptions.default_options).to eq({ its_magic: false })
|
||||||
|
expect(FaradayMiddlewareSubclasses::SubclassOneOption.default_options).to eq({ its_magic: false, some_other_option: false })
|
||||||
|
expect(FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options).to eq({ its_magic: false, some_option: true, some_other_option: false })
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect(resp1.body).to eq('ok') }
|
||||||
|
end
|
||||||
|
|
||||||
|
context "and with two applications' options changed" do
|
||||||
|
let(:resp1) { build_conn(:one_option).get('/success') }
|
||||||
|
let(:resp2) { build_conn(:two_options).get('/success') }
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
FaradayMiddlewareSubclasses::SubclassOneOption.default_options = { some_other_option: true }
|
||||||
|
FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options = { some_option: false }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates subclasses and parent independent of each other' do
|
||||||
|
expect(Faraday::Middleware.default_options).to eq(Faraday::Middleware::DEFAULT_OPTIONS)
|
||||||
|
expect(FaradayMiddlewareSubclasses::SubclassNoOptions.default_options).to eq({ its_magic: false })
|
||||||
|
expect(FaradayMiddlewareSubclasses::SubclassOneOption.default_options).to eq({ its_magic: false, some_other_option: true })
|
||||||
|
expect(FaradayMiddlewareSubclasses::SubclassTwoOptions.default_options).to eq({ its_magic: false, some_option: false, some_other_option: false })
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect(resp1.body).to eq('ok') }
|
||||||
|
it { expect(resp2.body).to eq('ok') }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'default_options input validation' do
|
||||||
|
include_context 'reset @default_options'
|
||||||
|
|
||||||
|
it 'raises error if Faraday::Middleware option does not exist' do
|
||||||
|
expect { Faraday::Middleware.default_options = { something_special: true } }.to raise_error(Faraday::InitializationError) do |e|
|
||||||
|
expect(e.message).to eq('Invalid options provided. Keys not found in Faraday::Middleware::DEFAULT_OPTIONS: something_special')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises error if subclass option does not exist' do
|
||||||
|
expect { subclass_one_option.default_options = { this_is_a_typo: true } }.to raise_error(Faraday::InitializationError) do |e|
|
||||||
|
expect(e.message).to eq('Invalid options provided. Keys not found in FaradayMiddlewareSubclasses::SubclassOneOption::DEFAULT_OPTIONS: this_is_a_typo')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -27,6 +27,33 @@ RSpec.describe Faraday::ProxyOptions do
|
|||||||
expect(options.inspect).to eq('#<Faraday::ProxyOptions (empty)>')
|
expect(options.inspect).to eq('#<Faraday::ProxyOptions (empty)>')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'works with hash' do
|
||||||
|
hash = { user: 'user', password: 'pass', uri: 'http://@example.org' }
|
||||||
|
options = Faraday::ProxyOptions.from(hash)
|
||||||
|
expect(options.user).to eq('user')
|
||||||
|
expect(options.password).to eq('pass')
|
||||||
|
expect(options.uri).to be_a_kind_of(URI)
|
||||||
|
expect(options.path).to eq('')
|
||||||
|
expect(options.port).to eq(80)
|
||||||
|
expect(options.host).to eq('example.org')
|
||||||
|
expect(options.scheme).to eq('http')
|
||||||
|
expect(options.inspect).to match('#<Faraday::ProxyOptions uri=')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'works with option' do
|
||||||
|
opt_arg = { user: 'user', password: 'pass', uri: 'http://@example.org' }
|
||||||
|
option = Faraday::ConnectionOptions.from(proxy: opt_arg)
|
||||||
|
options = Faraday::ProxyOptions.from(option.proxy)
|
||||||
|
expect(options.user).to eq('user')
|
||||||
|
expect(options.password).to eq('pass')
|
||||||
|
expect(options.uri).to be_a_kind_of(URI)
|
||||||
|
expect(options.path).to eq('')
|
||||||
|
expect(options.port).to eq(80)
|
||||||
|
expect(options.host).to eq('example.org')
|
||||||
|
expect(options.scheme).to eq('http')
|
||||||
|
expect(options.inspect).to match('#<Faraday::ProxyOptions uri=')
|
||||||
|
end
|
||||||
|
|
||||||
it 'works with no auth' do
|
it 'works with no auth' do
|
||||||
proxy = Faraday::ProxyOptions.from 'http://example.org'
|
proxy = Faraday::ProxyOptions.from 'http://example.org'
|
||||||
expect(proxy.user).to be_nil
|
expect(proxy.user).to be_nil
|
||||||
|
@ -62,7 +62,8 @@ RSpec.describe Faraday::NestedParamsEncoder do
|
|||||||
it 'encodes rack compat' do
|
it 'encodes rack compat' do
|
||||||
params = { a: [{ one: '1', two: '2' }, '3', ''] }
|
params = { a: [{ one: '1', two: '2' }, '3', ''] }
|
||||||
result = Faraday::Utils.unescape(Faraday::NestedParamsEncoder.encode(params)).split('&')
|
result = Faraday::Utils.unescape(Faraday::NestedParamsEncoder.encode(params)).split('&')
|
||||||
expected = Rack::Utils.build_nested_query(params).split('&')
|
escaped = Rack::Utils.build_nested_query(params)
|
||||||
|
expected = Rack::Utils.unescape(escaped).split('&')
|
||||||
expect(result).to match_array(expected)
|
expect(result).to match_array(expected)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -184,6 +184,23 @@ RSpec.describe Faraday::Response::Json, type: :response do
|
|||||||
response = process(body)
|
response = process(body)
|
||||||
expect(response.body).to eq(result)
|
expect(response.body).to eq(result)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'passes relevant options to JSON parse even when nil responds to :load' do
|
||||||
|
original_allow_message_expectations_on_nil = RSpec::Mocks.configuration.allow_message_expectations_on_nil
|
||||||
|
RSpec::Mocks.configuration.allow_message_expectations_on_nil = true
|
||||||
|
allow(nil).to receive(:respond_to?)
|
||||||
|
.with(:load)
|
||||||
|
.and_return(true)
|
||||||
|
|
||||||
|
expect(JSON).to receive(:parse)
|
||||||
|
.with(body, { symbolize_names: true })
|
||||||
|
.and_return(result)
|
||||||
|
|
||||||
|
response = process(body)
|
||||||
|
expect(response.body).to eq(result)
|
||||||
|
ensure
|
||||||
|
RSpec::Mocks.configuration.allow_message_expectations_on_nil = original_allow_message_expectations_on_nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -55,6 +55,26 @@ RSpec.describe Faraday::Response::Logger do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when logger with program name' do
|
||||||
|
let(:logger) { Logger.new(string_io, progname: 'my_best_program') }
|
||||||
|
|
||||||
|
it 'logs with program name' do
|
||||||
|
conn.get '/hello'
|
||||||
|
|
||||||
|
expect(string_io.string).to match('-- my_best_program: request:')
|
||||||
|
expect(string_io.string).to match('-- my_best_program: response:')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when logger without program name' do
|
||||||
|
it 'logs without program name' do
|
||||||
|
conn.get '/hello'
|
||||||
|
|
||||||
|
expect(string_io.string).to match('-- : request:')
|
||||||
|
expect(string_io.string).to match('-- : response:')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'with default formatter' do
|
context 'with default formatter' do
|
||||||
let(:formatter) { instance_double(Faraday::Logging::Formatter, request: true, response: true, filter: []) }
|
let(:formatter) { instance_double(Faraday::Logging::Formatter, request: true, response: true, filter: []) }
|
||||||
|
|
||||||
@ -169,7 +189,7 @@ RSpec.describe Faraday::Response::Logger do
|
|||||||
context 'when logging request body' do
|
context 'when logging request body' do
|
||||||
let(:logger_options) { { bodies: { request: true } } }
|
let(:logger_options) { { bodies: { request: true } } }
|
||||||
|
|
||||||
it 'log only request body' do
|
it 'logs only request body' do
|
||||||
conn.post '/ohyes', 'name=Tamago', accept: 'text/html'
|
conn.post '/ohyes', 'name=Tamago', accept: 'text/html'
|
||||||
expect(string_io.string).to match(%(name=Tamago))
|
expect(string_io.string).to match(%(name=Tamago))
|
||||||
expect(string_io.string).not_to match(%(pebbles))
|
expect(string_io.string).not_to match(%(pebbles))
|
||||||
@ -179,7 +199,7 @@ RSpec.describe Faraday::Response::Logger do
|
|||||||
context 'when logging response body' do
|
context 'when logging response body' do
|
||||||
let(:logger_options) { { bodies: { response: true } } }
|
let(:logger_options) { { bodies: { response: true } } }
|
||||||
|
|
||||||
it 'log only response body' do
|
it 'logs only response body' do
|
||||||
conn.post '/ohyes', 'name=Hamachi', accept: 'text/html'
|
conn.post '/ohyes', 'name=Hamachi', accept: 'text/html'
|
||||||
expect(string_io.string).to match(%(pebbles))
|
expect(string_io.string).to match(%(pebbles))
|
||||||
expect(string_io.string).not_to match(%(name=Hamachi))
|
expect(string_io.string).not_to match(%(name=Hamachi))
|
||||||
@ -189,13 +209,13 @@ RSpec.describe Faraday::Response::Logger do
|
|||||||
context 'when logging request and response bodies' do
|
context 'when logging request and response bodies' do
|
||||||
let(:logger_options) { { bodies: true } }
|
let(:logger_options) { { bodies: true } }
|
||||||
|
|
||||||
it 'log request and response body' do
|
it 'logs request and response body' do
|
||||||
conn.post '/ohyes', 'name=Ebi', accept: 'text/html'
|
conn.post '/ohyes', 'name=Ebi', accept: 'text/html'
|
||||||
expect(string_io.string).to match(%(name=Ebi))
|
expect(string_io.string).to match(%(name=Ebi))
|
||||||
expect(string_io.string).to match(%(pebbles))
|
expect(string_io.string).to match(%(pebbles))
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'log response body object' do
|
it 'logs response body object' do
|
||||||
conn.get '/rubbles', nil, accept: 'text/html'
|
conn.get '/rubbles', nil, accept: 'text/html'
|
||||||
expect(string_io.string).to match(%([\"Barney\", \"Betty\", \"Bam Bam\"]\n))
|
expect(string_io.string).to match(%([\"Barney\", \"Betty\", \"Bam Bam\"]\n))
|
||||||
end
|
end
|
||||||
@ -208,6 +228,21 @@ RSpec.describe Faraday::Response::Logger do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when bodies are logged by default' do
|
||||||
|
before do
|
||||||
|
described_class.default_options = { bodies: true }
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'logs response body' do
|
||||||
|
conn.post '/ohai'
|
||||||
|
expect(string_io.string).to match(%(fred))
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
described_class.default_options = { bodies: false }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'when logging errors' do
|
context 'when logging errors' do
|
||||||
let(:logger_options) { { errors: true } }
|
let(:logger_options) { { errors: true } }
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|||||||
|
|
||||||
it 'raises Faraday::BadRequestError for 400 responses' do
|
it 'raises Faraday::BadRequestError for 400 responses' do
|
||||||
expect { conn.get('bad-request') }.to raise_error(Faraday::BadRequestError) do |ex|
|
expect { conn.get('bad-request') }.to raise_error(Faraday::BadRequestError) do |ex|
|
||||||
expect(ex.message).to eq('the server responded with status 400')
|
expect(ex.message).to eq('the server responded with status 400 for GET http:/bad-request')
|
||||||
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
||||||
expect(ex.response[:status]).to eq(400)
|
expect(ex.response[:status]).to eq(400)
|
||||||
expect(ex.response_status).to eq(400)
|
expect(ex.response_status).to eq(400)
|
||||||
@ -39,7 +39,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|||||||
|
|
||||||
it 'raises Faraday::UnauthorizedError for 401 responses' do
|
it 'raises Faraday::UnauthorizedError for 401 responses' do
|
||||||
expect { conn.get('unauthorized') }.to raise_error(Faraday::UnauthorizedError) do |ex|
|
expect { conn.get('unauthorized') }.to raise_error(Faraday::UnauthorizedError) do |ex|
|
||||||
expect(ex.message).to eq('the server responded with status 401')
|
expect(ex.message).to eq('the server responded with status 401 for GET http:/unauthorized')
|
||||||
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
||||||
expect(ex.response[:status]).to eq(401)
|
expect(ex.response[:status]).to eq(401)
|
||||||
expect(ex.response_status).to eq(401)
|
expect(ex.response_status).to eq(401)
|
||||||
@ -50,7 +50,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|||||||
|
|
||||||
it 'raises Faraday::ForbiddenError for 403 responses' do
|
it 'raises Faraday::ForbiddenError for 403 responses' do
|
||||||
expect { conn.get('forbidden') }.to raise_error(Faraday::ForbiddenError) do |ex|
|
expect { conn.get('forbidden') }.to raise_error(Faraday::ForbiddenError) do |ex|
|
||||||
expect(ex.message).to eq('the server responded with status 403')
|
expect(ex.message).to eq('the server responded with status 403 for GET http:/forbidden')
|
||||||
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
||||||
expect(ex.response[:status]).to eq(403)
|
expect(ex.response[:status]).to eq(403)
|
||||||
expect(ex.response_status).to eq(403)
|
expect(ex.response_status).to eq(403)
|
||||||
@ -61,7 +61,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|||||||
|
|
||||||
it 'raises Faraday::ResourceNotFound for 404 responses' do
|
it 'raises Faraday::ResourceNotFound for 404 responses' do
|
||||||
expect { conn.get('not-found') }.to raise_error(Faraday::ResourceNotFound) do |ex|
|
expect { conn.get('not-found') }.to raise_error(Faraday::ResourceNotFound) do |ex|
|
||||||
expect(ex.message).to eq('the server responded with status 404')
|
expect(ex.message).to eq('the server responded with status 404 for GET http:/not-found')
|
||||||
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
||||||
expect(ex.response[:status]).to eq(404)
|
expect(ex.response[:status]).to eq(404)
|
||||||
expect(ex.response_status).to eq(404)
|
expect(ex.response_status).to eq(404)
|
||||||
@ -83,7 +83,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|||||||
|
|
||||||
it 'raises Faraday::RequestTimeoutError for 408 responses' do
|
it 'raises Faraday::RequestTimeoutError for 408 responses' do
|
||||||
expect { conn.get('request-timeout') }.to raise_error(Faraday::RequestTimeoutError) do |ex|
|
expect { conn.get('request-timeout') }.to raise_error(Faraday::RequestTimeoutError) do |ex|
|
||||||
expect(ex.message).to eq('the server responded with status 408')
|
expect(ex.message).to eq('the server responded with status 408 for GET http:/request-timeout')
|
||||||
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
||||||
expect(ex.response[:status]).to eq(408)
|
expect(ex.response[:status]).to eq(408)
|
||||||
expect(ex.response_status).to eq(408)
|
expect(ex.response_status).to eq(408)
|
||||||
@ -94,7 +94,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|||||||
|
|
||||||
it 'raises Faraday::ConflictError for 409 responses' do
|
it 'raises Faraday::ConflictError for 409 responses' do
|
||||||
expect { conn.get('conflict') }.to raise_error(Faraday::ConflictError) do |ex|
|
expect { conn.get('conflict') }.to raise_error(Faraday::ConflictError) do |ex|
|
||||||
expect(ex.message).to eq('the server responded with status 409')
|
expect(ex.message).to eq('the server responded with status 409 for GET http:/conflict')
|
||||||
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
||||||
expect(ex.response[:status]).to eq(409)
|
expect(ex.response[:status]).to eq(409)
|
||||||
expect(ex.response_status).to eq(409)
|
expect(ex.response_status).to eq(409)
|
||||||
@ -105,7 +105,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|||||||
|
|
||||||
it 'raises Faraday::UnprocessableEntityError for 422 responses' do
|
it 'raises Faraday::UnprocessableEntityError for 422 responses' do
|
||||||
expect { conn.get('unprocessable-entity') }.to raise_error(Faraday::UnprocessableEntityError) do |ex|
|
expect { conn.get('unprocessable-entity') }.to raise_error(Faraday::UnprocessableEntityError) do |ex|
|
||||||
expect(ex.message).to eq('the server responded with status 422')
|
expect(ex.message).to eq('the server responded with status 422 for GET http:/unprocessable-entity')
|
||||||
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
||||||
expect(ex.response[:status]).to eq(422)
|
expect(ex.response[:status]).to eq(422)
|
||||||
expect(ex.response_status).to eq(422)
|
expect(ex.response_status).to eq(422)
|
||||||
@ -116,7 +116,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|||||||
|
|
||||||
it 'raises Faraday::TooManyRequestsError for 429 responses' do
|
it 'raises Faraday::TooManyRequestsError for 429 responses' do
|
||||||
expect { conn.get('too-many-requests') }.to raise_error(Faraday::TooManyRequestsError) do |ex|
|
expect { conn.get('too-many-requests') }.to raise_error(Faraday::TooManyRequestsError) do |ex|
|
||||||
expect(ex.message).to eq('the server responded with status 429')
|
expect(ex.message).to eq('the server responded with status 429 for GET http:/too-many-requests')
|
||||||
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
||||||
expect(ex.response[:status]).to eq(429)
|
expect(ex.response[:status]).to eq(429)
|
||||||
expect(ex.response_status).to eq(429)
|
expect(ex.response_status).to eq(429)
|
||||||
@ -138,7 +138,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|||||||
|
|
||||||
it 'raises Faraday::ClientError for other 4xx responses' do
|
it 'raises Faraday::ClientError for other 4xx responses' do
|
||||||
expect { conn.get('4xx') }.to raise_error(Faraday::ClientError) do |ex|
|
expect { conn.get('4xx') }.to raise_error(Faraday::ClientError) do |ex|
|
||||||
expect(ex.message).to eq('the server responded with status 499')
|
expect(ex.message).to eq('the server responded with status 499 for GET http:/4xx')
|
||||||
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
||||||
expect(ex.response[:status]).to eq(499)
|
expect(ex.response[:status]).to eq(499)
|
||||||
expect(ex.response_status).to eq(499)
|
expect(ex.response_status).to eq(499)
|
||||||
@ -149,7 +149,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|||||||
|
|
||||||
it 'raises Faraday::ServerError for 500 responses' do
|
it 'raises Faraday::ServerError for 500 responses' do
|
||||||
expect { conn.get('server-error') }.to raise_error(Faraday::ServerError) do |ex|
|
expect { conn.get('server-error') }.to raise_error(Faraday::ServerError) do |ex|
|
||||||
expect(ex.message).to eq('the server responded with status 500')
|
expect(ex.message).to eq('the server responded with status 500 for GET http:/server-error')
|
||||||
expect(ex.response[:headers]['X-Error']).to eq('bailout')
|
expect(ex.response[:headers]['X-Error']).to eq('bailout')
|
||||||
expect(ex.response[:status]).to eq(500)
|
expect(ex.response[:status]).to eq(500)
|
||||||
expect(ex.response_status).to eq(500)
|
expect(ex.response_status).to eq(500)
|
||||||
@ -194,18 +194,82 @@ RSpec.describe Faraday::Response::RaiseError do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the include_request option is set to false' do
|
describe 'DEFAULT_OPTION: include_request' do
|
||||||
let(:middleware_options) { { include_request: false } }
|
before(:each) do
|
||||||
|
Faraday::Response::RaiseError.instance_variable_set(:@default_options, nil)
|
||||||
|
Faraday::Middleware.instance_variable_set(:@default_options, nil)
|
||||||
|
end
|
||||||
|
|
||||||
it 'does not include request info in the exception' do
|
after(:all) do
|
||||||
expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex|
|
Faraday::Response::RaiseError.instance_variable_set(:@default_options, nil)
|
||||||
expect(ex.response.keys).to contain_exactly(
|
Faraday::Middleware.instance_variable_set(:@default_options, nil)
|
||||||
:status,
|
end
|
||||||
:headers,
|
|
||||||
:body
|
context 'when RaiseError DEFAULT_OPTION (include_request: true) is used' do
|
||||||
)
|
it 'includes request info in the exception' do
|
||||||
|
expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex|
|
||||||
|
expect(ex.response.keys).to contain_exactly(
|
||||||
|
:status,
|
||||||
|
:headers,
|
||||||
|
:body,
|
||||||
|
:request
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when application sets default_options `include_request: false`' do
|
||||||
|
before(:each) do
|
||||||
|
Faraday::Response::RaiseError.default_options = { include_request: false }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'and when include_request option is omitted' do
|
||||||
|
it 'does not include request info in the exception' do
|
||||||
|
expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex|
|
||||||
|
expect(ex.response.keys).to contain_exactly(
|
||||||
|
:status,
|
||||||
|
:headers,
|
||||||
|
:body
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'and when include_request option is explicitly set for instance' do
|
||||||
|
let(:middleware_options) { { include_request: true } }
|
||||||
|
|
||||||
|
it 'includes request info in the exception' do
|
||||||
|
expect { perform_request }.to raise_error(Faraday::BadRequestError) do |ex|
|
||||||
|
expect(ex.response.keys).to contain_exactly(
|
||||||
|
:status,
|
||||||
|
:headers,
|
||||||
|
:body,
|
||||||
|
:request
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'allowing certain status codes' do
|
||||||
|
let(:conn) do
|
||||||
|
Faraday.new do |b|
|
||||||
|
b.response :raise_error, allowed_statuses: [404]
|
||||||
|
b.adapter :test do |stub|
|
||||||
|
stub.get('bad-request') { [400, { 'X-Reason' => 'because' }, 'keep looking'] }
|
||||||
|
stub.get('not-found') { [404, { 'X-Reason' => 'because' }, 'keep looking'] }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises an error for status codes that are not explicitly allowed' do
|
||||||
|
expect { conn.get('bad-request') }.to raise_error(Faraday::BadRequestError)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not raise an error for allowed status codes' do
|
||||||
|
expect { conn.get('not-found') }.not_to raise_error
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -56,6 +56,15 @@ RSpec.describe Faraday::Utils::Headers do
|
|||||||
it { expect(subject.delete('content-type')).to be_nil }
|
it { expect(subject.delete('content-type')).to be_nil }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#dig' do
|
||||||
|
before { subject['Content-Type'] = 'application/json' }
|
||||||
|
|
||||||
|
it { expect(subject&.dig('Content-Type')).to eq('application/json') }
|
||||||
|
it { expect(subject&.dig('CONTENT-TYPE')).to eq('application/json') }
|
||||||
|
it { expect(subject&.dig(:content_type)).to eq('application/json') }
|
||||||
|
it { expect(subject&.dig('invalid')).to be_nil }
|
||||||
|
end
|
||||||
|
|
||||||
describe '#parse' do
|
describe '#parse' do
|
||||||
context 'when response headers leave http status line out' do
|
context 'when response headers leave http status line out' do
|
||||||
let(:headers) { "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n" }
|
let(:headers) { "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n" }
|
||||||
|
@ -103,7 +103,9 @@ RSpec.describe Faraday::Utils do
|
|||||||
version: '2',
|
version: '2',
|
||||||
min_version: nil,
|
min_version: nil,
|
||||||
max_version: nil,
|
max_version: nil,
|
||||||
verify_hostname: nil
|
verify_hostname: nil,
|
||||||
|
hostname: nil,
|
||||||
|
ciphers: nil
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -19,7 +19,9 @@ RSpec.describe Faraday do
|
|||||||
|
|
||||||
it 'uses method_missing on Faraday if there is no proxyable method' do
|
it 'uses method_missing on Faraday if there is no proxyable method' do
|
||||||
expected_message =
|
expected_message =
|
||||||
if RUBY_VERSION >= '3.3'
|
if RUBY_VERSION >= '3.4'
|
||||||
|
"undefined method 'this_method_does_not_exist' for module Faraday"
|
||||||
|
elsif RUBY_VERSION >= '3.3'
|
||||||
"undefined method `this_method_does_not_exist' for module Faraday"
|
"undefined method `this_method_does_not_exist' for module Faraday"
|
||||||
else
|
else
|
||||||
"undefined method `this_method_does_not_exist' for Faraday:Module"
|
"undefined method `this_method_does_not_exist' for Faraday:Module"
|
||||||
|
@ -29,14 +29,15 @@ SimpleCov.start do
|
|||||||
minimum_coverage_by_file 26
|
minimum_coverage_by_file 26
|
||||||
end
|
end
|
||||||
|
|
||||||
# Ensure all /lib files are loaded
|
|
||||||
# so they will be included in the test coverage report.
|
|
||||||
Dir['./lib/**/*.rb'].sort.each { |file| require file }
|
|
||||||
|
|
||||||
require 'faraday'
|
require 'faraday'
|
||||||
require 'pry'
|
require 'pry'
|
||||||
|
|
||||||
Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
|
# Ensure all /lib files are loaded
|
||||||
|
# so they will be included in the test coverage report.
|
||||||
|
Dir['./lib/**/*.rb'].each { |file| require file }
|
||||||
|
|
||||||
|
# Load all Rspec support files
|
||||||
|
Dir['./spec/support/**/*.rb'].each { |file| require file }
|
||||||
|
|
||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
# rspec-expectations config goes here. You can use an alternate
|
# rspec-expectations config goes here. You can use an alternate
|
||||||
|
18
spec/support/faraday_middleware_subclasses.rb
Normal file
18
spec/support/faraday_middleware_subclasses.rb
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module FaradayMiddlewareSubclasses
|
||||||
|
class SubclassNoOptions < Faraday::Middleware
|
||||||
|
end
|
||||||
|
|
||||||
|
class SubclassOneOption < Faraday::Middleware
|
||||||
|
DEFAULT_OPTIONS = { some_other_option: false }.freeze
|
||||||
|
end
|
||||||
|
|
||||||
|
class SubclassTwoOptions < Faraday::Middleware
|
||||||
|
DEFAULT_OPTIONS = { some_option: true, some_other_option: false }.freeze
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Faraday::Response.register_middleware(no_options: FaradayMiddlewareSubclasses::SubclassNoOptions)
|
||||||
|
Faraday::Response.register_middleware(one_option: FaradayMiddlewareSubclasses::SubclassOneOption)
|
||||||
|
Faraday::Response.register_middleware(two_options: FaradayMiddlewareSubclasses::SubclassTwoOptions)
|
Loading…
x
Reference in New Issue
Block a user