diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 93a4352f..0098ea7c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,17 +12,20 @@ jobs: strategy: matrix: ruby-version: ['3.2'] + gemfile: ['faraday_1', 'faraday_2'] ES_VERSION: ['8.18.0'] include: - ES_VERSION: '8.18.0' ES_DOWNLOAD_URL: >- https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.18.0-linux-x86_64.tar.gz + env: + BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile steps: + - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true # runs 'bundle install' and caches installed gems automatically - - uses: actions/checkout@v4 - name: Cache Elasticsearch id: cache-elasticsearch uses: actions/cache@v4 diff --git a/.gitignore b/.gitignore index d64dd0aa..54bf8a78 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ /vendor/cache/*.gem /coverage Gemfile.lock +gemfiles/*.lock *.gem tags .byebug_history diff --git a/CHANGELOG.md b/CHANGELOG.md index 636799bf..233cc857 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.3.0 (2026-06-22) +- Add support for Faraday 2 (now compatible with both Faraday 1.x and 2.x) +- Drop the deprecated `faraday_middleware` dependency in favor of `faraday-gzip` + ## 6.2.4 (2025-10-10) - Added support for ES 8.19.3 diff --git a/Gemfile b/Gemfile index d38f1a47..43fff963 100644 --- a/Gemfile +++ b/Gemfile @@ -8,6 +8,9 @@ group :development do gem "activesupport", ">= 7.0" gem "bundler", "~> 2.0" gem "debug", "~> 1.7" + # The default bundle resolves Faraday 2.x, where the :retry middleware lives in the + # faraday-retry gem (it is in core on 1.x). Needed for the retry test; consumers opt in. + gem "faraday-retry", require: "faraday/retry" gem "minitest", "~> 5.17" gem "minitest-focus", "~> 1.3" gem "rake" @@ -17,6 +20,5 @@ group :development do gem "rubocop-performance", "~> 1.21.0" gem "rubocop-rake", "~> 0.6.0" gem "simplecov", require: false - gem "spy", "~> 1.0" gem "webmock", "~> 3.5" end diff --git a/elastomer-client.gemspec b/elastomer-client.gemspec index 3c049fec..d2ce72a5 100644 --- a/elastomer-client.gemspec +++ b/elastomer-client.gemspec @@ -22,8 +22,8 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_dependency "addressable", "~> 2.5" - spec.add_dependency "faraday", ">= 0.17" - spec.add_dependency "faraday_middleware", ">= 0.14" + spec.add_dependency "faraday", ">= 1.0", "< 3" + spec.add_dependency "faraday-gzip", ">= 1.0" spec.add_dependency "multi_json", "~> 1.12" spec.add_dependency "semantic", "~> 1.6" end diff --git a/gemfiles/faraday_1.gemfile b/gemfiles/faraday_1.gemfile new file mode 100644 index 00000000..86edf958 --- /dev/null +++ b/gemfiles/faraday_1.gemfile @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +# Tests elastomer-client against Faraday 1.x. +# +# faraday-gzip works on both Faraday 1.x and 2.x and provides the :gzip request middleware +# used for response decompression (replacing the deprecated faraday_middleware). +source "https://rubygems.org" + +gem "faraday", "~> 1.10" + +gemspec path: ".." + +group :development do + gem "activesupport", ">= 7.0" + gem "bundler", "~> 2.0" + gem "debug", "~> 1.7" + gem "minitest", "~> 5.17" + gem "minitest-focus", "~> 1.3" + gem "rake" + gem "rubocop", "~> 1.63.0" + gem "rubocop-github", "~> 0.20.0" + gem "rubocop-minitest", "~> 0.35.0" + gem "rubocop-performance", "~> 1.21.0" + gem "rubocop-rake", "~> 0.6.0" + gem "simplecov", require: false + gem "webmock", "~> 3.5" +end diff --git a/gemfiles/faraday_2.gemfile b/gemfiles/faraday_2.gemfile new file mode 100644 index 00000000..8d9f1307 --- /dev/null +++ b/gemfiles/faraday_2.gemfile @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# Tests elastomer-client against Faraday 2.x. +# +# This is the configuration the monolith targets after the Faraday 2 upgrade. faraday-gzip +# provides the :gzip request middleware (response decompression) on Faraday 2.x. +source "https://rubygems.org" + +gem "faraday", "~> 2.0" + +gemspec path: ".." + +group :development do + gem "activesupport", ">= 7.0" + gem "bundler", "~> 2.0" + gem "debug", "~> 1.7" + # On Faraday 2.x the :retry middleware lives in the faraday-retry gem (it is in core on + # 1.x). Consumers opt into retries via a connection block; the suite covers that path. + gem "faraday-retry", require: "faraday/retry" + gem "minitest", "~> 5.17" + gem "minitest-focus", "~> 1.3" + gem "rake" + gem "rubocop", "~> 1.63.0" + gem "rubocop-github", "~> 0.20.0" + gem "rubocop-minitest", "~> 0.35.0" + gem "rubocop-performance", "~> 1.21.0" + gem "rubocop-rake", "~> 0.6.0" + gem "simplecov", require: false + gem "webmock", "~> 3.5" +end diff --git a/lib/elastomer_client/client.rb b/lib/elastomer_client/client.rb index 416fc00d..3e5004e7 100644 --- a/lib/elastomer_client/client.rb +++ b/lib/elastomer_client/client.rb @@ -2,7 +2,7 @@ require "addressable/template" require "faraday" -require "faraday_middleware" +require "faraday/gzip" require "multi_json" require "semantic" require "zlib" @@ -132,7 +132,7 @@ def connection @connection ||= Faraday.new(url) do |conn| conn.response(:parse_json) # Request compressed responses from ES and decompress them - conn.use(:gzip) + conn.request(:gzip) conn.request(:encode_json) conn.request(:limit_size, max_request_size:) if max_request_size conn.request(:elastomer_compress, compression:) if compress_body @@ -140,10 +140,14 @@ def connection conn.options[:timeout] = read_timeout conn.options[:open_timeout] = open_timeout + # Faraday 2 removed the Connection#token_auth and #basic_auth helpers, and the + # :authorization middleware emits different header formats across Faraday majors. + # Set the Authorization header directly so the output is identical on 1.x and 2.x. if token_auth? - conn.token_auth(@token_auth) + conn.headers["Authorization"] = %(Token token="#{@token_auth}") elsif basic_auth? - conn.basic_auth(@basic_auth[:username], @basic_auth[:password]) + credentials = ["#{@basic_auth[:username]}:#{@basic_auth[:password]}"].pack("m0") + conn.headers["Authorization"] = "Basic #{credentials}" end @connection_block&.call(conn) diff --git a/lib/elastomer_client/version.rb b/lib/elastomer_client/version.rb index 68acdd42..4a8487df 100644 --- a/lib/elastomer_client/version.rb +++ b/lib/elastomer_client/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module ElastomerClient - VERSION = "6.2.4" + VERSION = "6.3.0" def self.version VERSION diff --git a/test/client_test.rb b/test/client_test.rb index 30c87fcb..66a81ca5 100644 --- a/test/client_test.rb +++ b/test/client_test.rb @@ -13,15 +13,15 @@ end it "allows configuring the Faraday when a block is given" do - assert ElastomerClient::Client.new.connection.builder.handlers.none? { |handler| handler.klass == FaradayMiddleware::Instrumentation } + assert ElastomerClient::Client.new.connection.builder.handlers.none? { |handler| handler.klass == ElastomerClient::Middleware::OpaqueId } c = ElastomerClient::Client.new do |connection| assert_kind_of(Faraday::Connection, connection) - connection.use :instrumentation + connection.request :opaque_id end - assert c.connection.builder.handlers.any? { |handler| handler.klass == FaradayMiddleware::Instrumentation } + assert c.connection.builder.handlers.any? { |handler| handler.klass == ElastomerClient::Middleware::OpaqueId } end it "use Faraday's default adapter if none is specified" do @@ -145,14 +145,9 @@ }) client = ElastomerClient::Client.new(**client_params) - connection = Faraday::Connection.new - basic_auth_spy = Spy.on(connection, :basic_auth).and_return(nil) + expected = "Basic #{["my_user:my_secret_password"].pack("m0")}" - Faraday.stub(:new, $client_params[:url], connection) do - client.ping - end - - assert basic_auth_spy.has_been_called_with?("my_user", "my_secret_password") + assert_equal expected, client.connection.headers["Authorization"] end it "ignores basic authentication if password is missing" do @@ -161,14 +156,7 @@ }) client = ElastomerClient::Client.new(**client_params) - connection = Faraday::Connection.new - basic_auth_spy = Spy.on(connection, :basic_auth).and_return(nil) - - Faraday.stub(:new, $client_params[:url], connection) do - client.ping - end - - refute_predicate basic_auth_spy, :has_been_called? + refute client.connection.headers.key?("Authorization") end it "ignores basic authentication if username is missing" do @@ -177,28 +165,14 @@ }) client = ElastomerClient::Client.new(**client_params) - connection = Faraday::Connection.new - basic_auth_spy = Spy.on(connection, :basic_auth).and_return(nil) - - Faraday.stub(:new, $client_params[:url], connection) do - client.ping - end - - refute_predicate basic_auth_spy, :has_been_called? + refute client.connection.headers.key?("Authorization") end it "can use token authentication" do client_params = $client_params.merge(token_auth: "my_secret_token") client = ElastomerClient::Client.new(**client_params) - connection = Faraday::Connection.new - token_auth_spy = Spy.on(connection, :token_auth).and_return(nil) - - Faraday.stub(:new, $client_params[:url], connection) do - client.ping - end - - assert token_auth_spy.has_been_called_with?("my_secret_token") + assert_equal %(Token token="my_secret_token"), client.connection.headers["Authorization"] end it "prefers token authentication over basic" do @@ -208,16 +182,7 @@ }, token_auth: "my_secret_token") client = ElastomerClient::Client.new(**client_params) - connection = Faraday::Connection.new - basic_auth_spy = Spy.on(connection, :basic_auth).and_return(nil) - token_auth_spy = Spy.on(connection, :token_auth).and_return(nil) - - Faraday.stub(:new, $client_params[:url], connection) do - client.ping - end - - refute_predicate basic_auth_spy, :has_been_called? - assert token_auth_spy.has_been_called_with?("my_secret_token") + assert_equal %(Token token="my_secret_token"), client.connection.headers["Authorization"] end end @@ -372,11 +337,15 @@ it "adding retry logic retries up to 2 times" do retry_count = 0 + # :retry maps to Faraday::Request::Retry on Faraday 1.x and Faraday::Retry::Middleware + # on Faraday 2.x (via faraday-retry). Look up whichever the running Faraday registers. + retry_klass = Faraday::Request.lookup_middleware(:retry) + retry_options = { max: 2, interval: 0.05, methods: [:get], - exceptions: Faraday::Request::Retry::DEFAULT_EXCEPTIONS + [Faraday::ConnectionFailed], + exceptions: retry_klass::DEFAULT_EXCEPTIONS + [Faraday::ConnectionFailed], retry_block: proc { |env, options, retries, exc| retry_count += 1 } } retry_client = ElastomerClient::Client.new(port: 9205) do |connection|