elastic-transport 8.0.0 → 8.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/license.yml +2 -2
  3. data/.github/workflows/otel.yml +48 -0
  4. data/.github/workflows/tests.yml +45 -5
  5. data/.gitignore +1 -1
  6. data/CHANGELOG.md +131 -8
  7. data/CONTRIBUTING.md +64 -0
  8. data/Gemfile +10 -9
  9. data/Gemfile-faraday1.gemfile +40 -0
  10. data/README.md +7 -528
  11. data/Rakefile +48 -1
  12. data/elastic-transport.gemspec +6 -9
  13. data/lib/elastic/transport/client.rb +66 -45
  14. data/lib/elastic/transport/meta_header.rb +21 -12
  15. data/lib/elastic/transport/opentelemetry.rb +166 -0
  16. data/lib/elastic/transport/transport/base.rb +74 -54
  17. data/lib/elastic/transport/transport/errors.rb +2 -4
  18. data/lib/elastic/transport/transport/http/curb.rb +30 -26
  19. data/lib/elastic/transport/transport/http/faraday.rb +30 -27
  20. data/lib/elastic/transport/transport/http/manticore.rb +10 -4
  21. data/lib/elastic/transport/transport/response.rb +3 -3
  22. data/lib/elastic/transport/transport/serializer/multi_json.rb +3 -3
  23. data/lib/elastic/transport/transport/sniffer.rb +3 -1
  24. data/lib/elastic/transport/version.rb +1 -1
  25. data/lib/elastic/transport.rb +1 -0
  26. data/spec/elastic/transport/base_spec.rb +26 -25
  27. data/spec/elastic/transport/client_spec.rb +91 -18
  28. data/spec/elastic/transport/http/manticore_spec.rb +20 -2
  29. data/spec/elastic/transport/meta_header_spec.rb +26 -11
  30. data/spec/elastic/transport/opentelemetry_spec.rb +325 -0
  31. data/spec/elastic/transport/sniffer_spec.rb +18 -0
  32. data/spec/spec_helper.rb +16 -1
  33. data/test/integration/jruby_test.rb +1 -1
  34. data/test/integration/transport_test.rb +86 -40
  35. data/test/test_helper.rb +9 -6
  36. data/test/unit/adapters_test.rb +104 -0
  37. data/test/unit/connection_test.rb +35 -37
  38. data/test/unit/transport_base_test.rb +7 -8
  39. data/test/unit/transport_curb_test.rb +2 -3
  40. data/test/unit/transport_manticore_test.rb +1 -1
  41. metadata +23 -76
@@ -30,16 +30,16 @@ describe Elastic::Transport::Transport::Base do
30
30
 
31
31
  it 'does not include the password in the logged string' do
32
32
  expect(logger).not_to receive(:error).with(/secret_password/)
33
- expect {
33
+ expect do
34
34
  client.perform_request('GET', '/_cluster/stats')
35
- }.to raise_exception(Faraday::ConnectionFailed)
35
+ end.to raise_exception(Elastic::Transport::Transport::Error)
36
36
  end
37
37
 
38
38
  it 'replaces the password with the string \'REDACTED\'' do
39
39
  expect(logger).to receive(:error).with(/REDACTED/)
40
- expect {
40
+ expect do
41
41
  client.perform_request('GET', '/_cluster/stats')
42
- }.to raise_exception(Faraday::ConnectionFailed)
42
+ end.to raise_exception(Elastic::Transport::Transport::Error)
43
43
  end
44
44
  end
45
45
 
@@ -93,7 +93,7 @@ describe Elastic::Transport::Transport::Base do
93
93
  end
94
94
 
95
95
  it 'raises an exception' do
96
- expect { client.perform_request('GET', '/info') }.to raise_exception(Faraday::ConnectionFailed)
96
+ expect { client.perform_request('GET', '/info') }.to raise_exception(Elastic::Transport::Transport::Error)
97
97
  end
98
98
  end
99
99
 
@@ -105,7 +105,8 @@ describe Elastic::Transport::Transport::Base do
105
105
  let(:arguments) do
106
106
  {
107
107
  hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
108
- retry_on_failure: 2
108
+ retry_on_failure: 2,
109
+ adapter: :net_http
109
110
  }
110
111
  end
111
112
 
@@ -115,9 +116,9 @@ describe Elastic::Transport::Transport::Base do
115
116
  end
116
117
 
117
118
  it 'uses the client `retry_on_failure` value' do
118
- expect {
119
+ expect do
119
120
  client.transport.perform_request('GET', '/info')
120
- }.to raise_exception(Faraday::ConnectionFailed)
121
+ end.to raise_exception(Elastic::Transport::Transport::Error)
121
122
  end
122
123
  end
123
124
 
@@ -129,7 +130,8 @@ describe Elastic::Transport::Transport::Base do
129
130
  let(:arguments) do
130
131
  {
131
132
  hosts: ELASTICSEARCH_HOSTS,
132
- retry_on_status: ['404']
133
+ retry_on_status: ['404'],
134
+ adapter: :net_http
133
135
  }
134
136
  end
135
137
 
@@ -148,7 +150,7 @@ describe Elastic::Transport::Transport::Base do
148
150
  it 'uses the option `retry_on_failure` value' do
149
151
  expect do
150
152
  client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5)
151
- end.to raise_exception(Faraday::ConnectionFailed)
153
+ end.to raise_exception(Elastic::Transport::Transport::Error)
152
154
  end
153
155
  end
154
156
  end
@@ -160,8 +162,8 @@ describe Elastic::Transport::Transport::Base do
160
162
 
161
163
  let(:arguments) do
162
164
  {
163
- hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
164
- retry_on_failure: true
165
+ hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
166
+ retry_on_failure: true
165
167
  }
166
168
  end
167
169
 
@@ -171,9 +173,9 @@ describe Elastic::Transport::Transport::Base do
171
173
  end
172
174
 
173
175
  it 'uses the default `MAX_RETRIES` value' do
174
- expect {
176
+ expect do
175
177
  client.transport.perform_request('GET', '/info')
176
- }.to raise_exception(Faraday::ConnectionFailed)
178
+ end.to raise_exception(Elastic::Transport::Transport::Error)
177
179
  end
178
180
  end
179
181
 
@@ -183,9 +185,9 @@ describe Elastic::Transport::Transport::Base do
183
185
  end
184
186
 
185
187
  it 'uses the option `retry_on_failure` value' do
186
- expect {
188
+ expect do
187
189
  client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5)
188
- }.to raise_exception(Faraday::ConnectionFailed)
190
+ end.to raise_exception(Elastic::Transport::Transport::Error)
189
191
  end
190
192
  end
191
193
  end
@@ -197,8 +199,8 @@ describe Elastic::Transport::Transport::Base do
197
199
 
198
200
  let(:arguments) do
199
201
  {
200
- hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
201
- retry_on_failure: false
202
+ hosts: ['http://unavailable:9200', 'http://unavailable:9201'],
203
+ retry_on_failure: false
202
204
  }
203
205
  end
204
206
 
@@ -208,22 +210,21 @@ describe Elastic::Transport::Transport::Base do
208
210
  end
209
211
 
210
212
  it 'does not retry' do
211
- expect {
213
+ expect do
212
214
  client.transport.perform_request('GET', '/info')
213
- }.to raise_exception(Faraday::ConnectionFailed)
215
+ end.to raise_exception(Elastic::Transport::Transport::Error)
214
216
  end
215
217
  end
216
218
 
217
219
  context 'when `perform_request` is called with a `retry_on_failure` option value' do
218
-
219
220
  before do
220
221
  expect(client.transport).to receive(:get_connection).exactly(6).times.and_call_original
221
222
  end
222
223
 
223
224
  it 'uses the option `retry_on_failure` value' do
224
- expect {
225
+ expect do
225
226
  client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5)
226
- }.to raise_exception(Faraday::ConnectionFailed)
227
+ end.to raise_exception(Elastic::Transport::Transport::Error)
227
228
  end
228
229
  end
229
230
  end
@@ -245,7 +246,7 @@ describe Elastic::Transport::Transport::Base do
245
246
  it 'does not retry' do
246
247
  expect do
247
248
  client.transport.perform_request('GET', '/info')
248
- end.to raise_exception(Faraday::ConnectionFailed)
249
+ end.to raise_exception(Elastic::Transport::Transport::Error)
249
250
  end
250
251
  end
251
252
 
@@ -257,7 +258,7 @@ describe Elastic::Transport::Transport::Base do
257
258
  it 'uses the option `retry_on_failure` value' do
258
259
  expect do
259
260
  client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5)
260
- end.to raise_exception(Faraday::ConnectionFailed)
261
+ end.to raise_exception(Elastic::Transport::Transport::Error)
261
262
  end
262
263
  end
263
264
  end
@@ -191,8 +191,8 @@ describe Elastic::Transport::Client do
191
191
  it 'uses Faraday NetHttp' do
192
192
  expect(adapter).to eq Faraday::Adapter::NetHttp
193
193
  end
194
- end unless jruby?
195
- end
194
+ end
195
+ end unless jruby?
196
196
 
197
197
  context 'when the adapter is patron' do
198
198
  let(:adapter) do
@@ -204,9 +204,10 @@ describe Elastic::Transport::Client do
204
204
  end
205
205
 
206
206
  it 'uses Faraday with the adapter' do
207
+ require 'faraday/patron'
207
208
  expect(adapter).to eq Faraday::Adapter::Patron
208
209
  end
209
- end
210
+ end unless jruby?
210
211
 
211
212
  context 'when the adapter is typhoeus' do
212
213
  let(:adapter) do
@@ -214,6 +215,8 @@ describe Elastic::Transport::Client do
214
215
  end
215
216
 
216
217
  let(:client) do
218
+ require 'faraday/typhoeus' if is_faraday_v2?
219
+
217
220
  described_class.new(adapter: :typhoeus, enable_meta_header: false)
218
221
  end
219
222
 
@@ -222,6 +225,38 @@ describe Elastic::Transport::Client do
222
225
  end
223
226
  end unless jruby?
224
227
 
228
+ context 'when the adapter is excon' do
229
+ let(:adapter) do
230
+ client.transport.connections.all.first.connection.builder.adapter
231
+ end
232
+
233
+ let(:client) do
234
+ require 'faraday/excon'
235
+
236
+ described_class.new(adapter: :excon, enable_meta_header: false)
237
+ end
238
+
239
+ it 'uses Faraday with the adapter' do
240
+ expect(adapter).to eq Faraday::Adapter::Excon
241
+ end
242
+ end if is_faraday_v2?
243
+
244
+ context 'when the adapter is async-http' do
245
+ let(:adapter) do
246
+ client.transport.connections.all.first.connection.builder.adapter
247
+ end
248
+
249
+ let(:client) do
250
+ require 'async/http/faraday'
251
+
252
+ described_class.new(adapter: :async_http, enable_meta_header: false)
253
+ end
254
+
255
+ it 'uses Faraday with the adapter' do
256
+ expect(adapter).to eq Async::HTTP::Faraday::Adapter
257
+ end
258
+ end unless jruby? || !is_faraday_v2?
259
+
225
260
  context 'when the adapter is specified as a string key' do
226
261
  let(:adapter) do
227
262
  client.transport.connections.all.first.connection.builder.adapter
@@ -234,7 +269,7 @@ describe Elastic::Transport::Client do
234
269
  it 'uses Faraday with the adapter' do
235
270
  expect(adapter).to eq Faraday::Adapter::Patron
236
271
  end
237
- end
272
+ end unless jruby?
238
273
 
239
274
  context 'when the adapter can be detected', unless: jruby? do
240
275
  around do |example|
@@ -274,7 +309,7 @@ describe Elastic::Transport::Client do
274
309
  it 'sets the logger' do
275
310
  expect(handlers).to include(Faraday::Response::Logger)
276
311
  end
277
- end
312
+ end unless jruby?
278
313
  end
279
314
 
280
315
  shared_examples_for 'a client that extracts hosts' do
@@ -1187,21 +1222,54 @@ describe Elastic::Transport::Client do
1187
1222
  expect(logger).to have_received(:warn).with(warning)
1188
1223
  end
1189
1224
  end
1225
+
1226
+ context 'when Elasticsearch response includes long precision Float' do
1227
+ let(:client) do
1228
+ Elastic::Transport::Client.new(hosts: hosts)
1229
+ end
1230
+ before do
1231
+ expect_any_instance_of(Faraday::Connection).to receive(:run_request) do
1232
+ Elastic::Transport::Transport::Response.new(200, "{\"score\":1.11111111111111111}", { 'content-type' => 'application/json; charset=UTF-8' })
1233
+ end
1234
+ end
1235
+
1236
+ context 'when default JSON engine is used' do
1237
+ after do
1238
+ # Clear MultiJson's adapter
1239
+ ::MultiJson.instance_variable_set(:@adapter, nil)
1240
+ end
1241
+ it 'returns as a Float' do
1242
+ response = client.perform_request('GET', '/')
1243
+ score = response.body['score']
1244
+ expect(score).to eq 1.11111111111111111
1245
+ expect(score.class).to eq Float
1246
+ end
1247
+ end
1248
+
1249
+ unless defined?(JRUBY_VERSION)
1250
+ context 'when Oj is used as a JSON engine' do
1251
+ before do
1252
+ require 'oj'
1253
+ end
1254
+ after do
1255
+ # Clear MultiJson's adapter
1256
+ ::MultiJson.instance_variable_set(:@adapter, nil)
1257
+ end
1258
+
1259
+ it 'returns as a Float' do
1260
+ response = client.perform_request('GET', '/')
1261
+ score = response.body['score']
1262
+ expect(score).to eq 1.11111111111111111
1263
+ expect(score.class).to eq Float
1264
+ end
1265
+ end
1266
+ end
1267
+ end
1190
1268
  end
1191
1269
 
1192
1270
  context 'when the client connects to Elasticsearch' do
1193
1271
  let(:logger) do
1194
- Logger.new(STDERR).tap do |logger|
1195
- logger.formatter = proc do |severity, datetime, progname, msg|
1196
- color = case severity
1197
- when /INFO/ then :green
1198
- when /ERROR|WARN|FATAL/ then :red
1199
- when /DEBUG/ then :cyan
1200
- else :white
1201
- end
1202
- ANSI.ansi(severity[0] + ' ', color, :faint) + ANSI.ansi(msg, :white, :faint) + "\n"
1203
- end
1204
- end unless ENV['QUIET']
1272
+ Logger.new($stderr) unless ENV['QUIET']
1205
1273
  end
1206
1274
 
1207
1275
  let(:port) do
@@ -1263,6 +1331,8 @@ describe Elastic::Transport::Client do
1263
1331
  end
1264
1332
 
1265
1333
  context 'when the Faraday adapter is set in the block' do
1334
+ require 'faraday/net_http_persistent' if is_faraday_v2?
1335
+
1266
1336
  let(:client) do
1267
1337
  Elastic::Transport::Client.new(host: ELASTICSEARCH_HOSTS.first, logger: logger) do |client|
1268
1338
  client.adapter(:net_http_persistent)
@@ -1322,9 +1392,9 @@ describe Elastic::Transport::Client do
1322
1392
 
1323
1393
  it 'retries only the specified number of times' do
1324
1394
  expect(client.perform_request('GET', '_nodes/_local'))
1325
- expect {
1395
+ expect do
1326
1396
  client.perform_request('GET', '_nodes/_local')
1327
- }.to raise_exception(Faraday::ConnectionFailed)
1397
+ end.to raise_exception(Elastic::Transport::Transport::Error)
1328
1398
  end
1329
1399
  end
1330
1400
 
@@ -1414,6 +1484,9 @@ describe Elastic::Transport::Client do
1414
1484
  end
1415
1485
 
1416
1486
  context 'when using the HTTPClient adapter' do
1487
+ require 'faraday/httpclient'
1488
+ require 'mutex_m' if RUBY_VERSION >= '3.4'
1489
+
1417
1490
  let(:client) do
1418
1491
  described_class.new(hosts: ELASTICSEARCH_HOSTS, compression: true, adapter: :httpclient, enable_meta_header: false)
1419
1492
  end
@@ -53,7 +53,7 @@ if defined?(JRUBY_VERSION)
53
53
  expect(perform_request).to be_kind_of(Elastic::Transport::Transport::Response)
54
54
  end
55
55
 
56
- it 'run body with preper params' do
56
+ it 'runs body with proper params' do
57
57
  expect(
58
58
  client.transport.connections.first.connection
59
59
  ).to receive(:post).with(
@@ -121,7 +121,7 @@ if defined?(JRUBY_VERSION)
121
121
  gzip.close.string
122
122
  end
123
123
 
124
- it 'run body with preper params' do
124
+ it 'runs body with proper params' do
125
125
  expect(
126
126
  client.transport.connections.first.connection
127
127
  ).to receive(:post).with(*request_params).and_return(response)
@@ -141,6 +141,24 @@ if defined?(JRUBY_VERSION)
141
141
  end
142
142
  end
143
143
  end
144
+
145
+ context 'headers' do
146
+ it 'sends custom headers' do
147
+ client = Elastic::Transport::Client.new(
148
+ transport_class: described_class,
149
+ transport_options: { headers: { 'Elastic-Api-Version'=>'2023-10-31' } }
150
+ )
151
+ expect(
152
+ client.transport.connections.first.connection
153
+ ).to receive(:get).with(
154
+ 'http://localhost:9200/',
155
+ {
156
+ headers: expected_headers.merge({ 'Elastic-Api-Version'=>'2023-10-31' })
157
+ }
158
+ ).and_return(response)
159
+ client.perform_request('GET', '/', {}, nil, headers)
160
+ end
161
+ end
144
162
  end
145
163
  end
146
164
  end
@@ -26,14 +26,14 @@ describe Elastic::Transport::Client do
26
26
  let(:adapter_code) { "nh=#{defined?(Net::HTTP::VERSION) ? Net::HTTP::VERSION : Net::HTTP::HTTPVersion}" }
27
27
  let(:meta_header) do
28
28
  if jruby?
29
- "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elastic::Transport::VERSION},jv=#{ENV_JAVA['java.version']},jr=#{JRUBY_VERSION},fd=#{Faraday::VERSION},#{adapter_code}"
29
+ "et=#{meta_version},rb=#{RUBY_VERSION},t=#{Elastic::Transport::VERSION},jv=#{ENV_JAVA['java.version']},jr=#{JRUBY_VERSION},fd=#{Faraday::VERSION},#{adapter_code}"
30
30
  else
31
- "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elastic::Transport::VERSION},fd=#{Faraday::VERSION},#{adapter_code}"
31
+ "et=#{meta_version},rb=#{RUBY_VERSION},t=#{Elastic::Transport::VERSION},fd=#{Faraday::VERSION},#{adapter_code}"
32
32
  end
33
33
  end
34
34
 
35
35
  context 'client_meta_version' do
36
- let(:version) { ['7.1.0-alpha', '7.11.0.pre.1', '8.0.0-beta', '8.0.0.beta.2']}
36
+ let(:version) { ['7.1.0-alpha', '7.11.0.pre.1', '8.0.0-beta', '8.0.0.beta.2'] }
37
37
 
38
38
  it 'converts the version to X.X.Xp' do
39
39
  expect(client.send(:client_meta_version, '7.0.0-alpha')).to eq('7.0.0p')
@@ -81,9 +81,9 @@ describe Elastic::Transport::Client do
81
81
  context 'adapters' do
82
82
  let(:meta_header) do
83
83
  if jruby?
84
- "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elastic::Transport::VERSION},jv=#{ENV_JAVA['java.version']},jr=#{JRUBY_VERSION},fd=#{Faraday::VERSION}"
84
+ "et=#{meta_version},rb=#{RUBY_VERSION},t=#{Elastic::Transport::VERSION},jv=#{ENV_JAVA['java.version']},jr=#{JRUBY_VERSION},fd=#{Faraday::VERSION}"
85
85
  else
86
- "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elastic::Transport::VERSION},fd=#{Faraday::VERSION}"
86
+ "et=#{meta_version},rb=#{RUBY_VERSION},t=#{Elastic::Transport::VERSION},fd=#{Faraday::VERSION}"
87
87
  end
88
88
  end
89
89
  let(:client) { described_class.new(adapter: adapter) }
@@ -155,7 +155,7 @@ describe Elastic::Transport::Client do
155
155
  expect(headers).to include('x-elastic-client-meta' => meta)
156
156
 
157
157
  Typhoeus = @klass if was_required
158
- end unless jruby?
158
+ end
159
159
 
160
160
  it 'sets adapter in the meta header' do
161
161
  require 'typhoeus'
@@ -163,7 +163,7 @@ describe Elastic::Transport::Client do
163
163
  meta = "#{meta_header},ty=#{Typhoeus::VERSION}"
164
164
  expect(headers).to include('x-elastic-client-meta' => meta)
165
165
  end
166
- end
166
+ end unless jruby?
167
167
 
168
168
  unless jruby?
169
169
  let(:adapter) { :patron }
@@ -246,9 +246,9 @@ describe Elastic::Transport::Client do
246
246
  let(:subject) { client.instance_variable_get('@arguments')[:transport_options][:headers] }
247
247
  let(:meta_header) do
248
248
  if jruby?
249
- "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elastic::Transport::VERSION},jv=#{ENV_JAVA['java.version']},jr=#{JRUBY_VERSION}"
249
+ "et=#{meta_version},rb=#{RUBY_VERSION},t=#{Elastic::Transport::VERSION},jv=#{ENV_JAVA['java.version']},jr=#{JRUBY_VERSION}"
250
250
  else
251
- "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elastic::Transport::VERSION}"
251
+ "et=#{meta_version},rb=#{RUBY_VERSION},t=#{Elastic::Transport::VERSION}"
252
252
  end
253
253
  end
254
254
 
@@ -258,14 +258,29 @@ describe Elastic::Transport::Client do
258
258
  end
259
259
  end
260
260
 
261
- context 'when using a different service version' do
261
+ context 'when Elasticsearch is being used' do
262
+ before do
263
+ stub_const('Elastic::ELASTICSEARCH_SERVICE_VERSION', [:es, '8.0.0'])
264
+ end
265
+
266
+ let(:client) { Elastic::Transport::Client.new }
267
+
268
+ it 'sets the service version in the metaheader' do
269
+ allow_any_instance_of(Kernel).to receive(:caller).and_return(['elasticsearch'])
270
+ expect(subject['x-elastic-client-meta']).to match(regexp)
271
+ expect(subject['x-elastic-client-meta']).to start_with('es=8.0.0')
272
+ end
273
+ end
274
+
275
+ context 'when Enterprise Search is being used' do
262
276
  before do
263
- stub_const('Elastic::ELASTICSEARCH_SERVICE_VERSION', [:ent, '8.0.0'])
277
+ stub_const('Elastic::ENTERPRISE_SERVICE_VERSION', [:ent, '8.0.0'])
264
278
  end
265
279
 
266
280
  let(:client) { Elastic::Transport::Client.new }
267
281
 
268
282
  it 'sets the service version in the metaheader' do
283
+ allow_any_instance_of(Kernel).to receive(:caller).and_return(['enterprise-search-ruby'])
269
284
  expect(subject['x-elastic-client-meta']).to match(regexp)
270
285
  expect(subject['x-elastic-client-meta']).to start_with('ent=8.0.0')
271
286
  end