traject 3.8.3 → 3.9.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.
@@ -0,0 +1,428 @@
1
+ require 'test_helper'
2
+ require 'httpx'
3
+ require 'traject/solr_json_httpx_writer'
4
+ require 'thread'
5
+ require 'json'
6
+ require 'stringio'
7
+ require 'logger'
8
+
9
+
10
+ require "httpx/adapters/webmock"
11
+ WebMock.enable! # not sure why we need this
12
+
13
+
14
+ # Some basic tests, using a mocked http client so we can see what it did --
15
+ # these tests do not run against a real solr server at present.
16
+ describe "Traject::SolrJsonHttpxWriter" do
17
+ TEST_SOLR_URL = "http://example.com/solr"
18
+
19
+ #######
20
+ # utilities to help testing
21
+ #######
22
+
23
+ def context_with(hash)
24
+ Traject::Indexer::Context.new(:output_hash => hash)
25
+ end
26
+
27
+ def create_writer(settings = {})
28
+ settings = {
29
+ "solr.url" => TEST_SOLR_URL,
30
+ }.merge!(settings)
31
+
32
+ Traject::SolrJsonHttpxWriter.new(settings)
33
+ end
34
+
35
+ # strio = StringIO.new
36
+ # logger_to_strio(strio)
37
+ #
38
+ # Later check for strio.string for contents
39
+ def logger_to_strio(strio)
40
+ # Yell makes this hard, let's do it with an ordinary logger, think
41
+ # it's okay.
42
+ Logger.new(strio)
43
+ end
44
+
45
+ #########
46
+ # Actual tests
47
+ #########
48
+
49
+ before do
50
+ @writer = create_writer
51
+ end
52
+
53
+ it "defaults to 1 bg thread" do
54
+ assert_equal 1, @writer.thread_pool_size
55
+ end
56
+
57
+ it "adds a document" do
58
+ stub_request(:post, "#{TEST_SOLR_URL}/update/json").to_return(status: 200)
59
+
60
+ @writer.put context_with({"id" => "one", "key" => ["value1", "value2"]})
61
+ @writer.close
62
+
63
+ assert_requested :post, "#{TEST_SOLR_URL}/update/json" do |request|
64
+ posted_json = JSON.parse(request.body)
65
+ assert_equal [{"id" => "one", "key" => ["value1", "value2"]}], posted_json
66
+ end
67
+ end
68
+
69
+ it "adds more than a batch in batches" do
70
+ stub_request(:post, "#{TEST_SOLR_URL}/update/json").to_return(status: 200)
71
+
72
+ (Traject::SolrJsonHttpxWriter::DEFAULT_BATCH_SIZE + 1).times do |i|
73
+ doc = {"id" => "doc_#{i}", "key" => "value"}
74
+ @writer.put context_with(doc)
75
+ end
76
+ @writer.close
77
+
78
+ assert_requested(:post, "#{TEST_SOLR_URL}/update/json", times: 2)
79
+
80
+ # first batch with 100
81
+ assert_requested(:post, "#{TEST_SOLR_URL}/update/json") do |request|
82
+ posted_json = JSON.parse(request.body)
83
+ posted_json.length == Traject::SolrJsonHttpxWriter::DEFAULT_BATCH_SIZE
84
+ end
85
+
86
+ # second batch with just one
87
+ assert_requested(:post, "#{TEST_SOLR_URL}/update/json") do |request|
88
+ posted_json = JSON.parse(request.body)
89
+ posted_json.length == 1
90
+ end
91
+ end
92
+
93
+ it "retries batch as individual records on failure" do
94
+ # capture post bodies for easier testing
95
+ post_bodies = []
96
+ stub_request(:post, "#{TEST_SOLR_URL}/update/json").with { |request|
97
+ post_bodies << JSON.parse(request.body)
98
+ true
99
+ }.to_return(status: 500)
100
+
101
+ @writer = create_writer("solr_writer.batch_size" => 2, "solr_writer.max_skipped" => 10)
102
+
103
+ 2.times do |i|
104
+ @writer.put context_with({"id" => "doc_#{i}", "key" => "value"})
105
+ end
106
+ @writer.close
107
+
108
+ # 1 batch, then 2 for re-trying each individually
109
+ assert_requested(:post, "#{TEST_SOLR_URL}/update/json", times: 3)
110
+
111
+ batch_update = post_bodies.first
112
+
113
+ assert_length 2, batch_update
114
+
115
+ individual_update1, individual_update2 = post_bodies[1], post_bodies[2]
116
+ assert_length 1, individual_update1
117
+ assert_length 1, individual_update2
118
+ end
119
+
120
+ it "includes Solr reported error in base error message" do
121
+ stub_request(:post, "#{TEST_SOLR_URL}/update/json").
122
+ to_return(
123
+ status: 400,
124
+ headers: { content_type: "application/json;charset=utf-8" },
125
+ body: { "responseHeader"=>{"status"=>400, "QTime"=>0},
126
+ "error"=>{
127
+ "metadata"=>["error-class", "org.apache.solr.common.SolrException", "root-error-class", "org.apache.solr.common.SolrException"],
128
+ "msg"=>"ERROR: this is a solr error",
129
+ "code"=>400
130
+ }
131
+ }.to_json
132
+ )
133
+
134
+
135
+ @writer = create_writer("solr_writer.batch_size" => 1, "solr_writer.max_skipped" => 0)
136
+
137
+ error = assert_raises(Traject::SolrJsonHttpxWriter::MaxSkippedRecordsExceeded) {
138
+ @writer.put context_with({"id" => "doc_1", "key" => "value"})
139
+ @writer.close
140
+ }
141
+ assert_match(/ERROR: this is a solr error/, error.message)
142
+ end
143
+
144
+ it "can #flush" do
145
+ stub_request(:post, "#{TEST_SOLR_URL}/update/json")
146
+
147
+ 2.times do |i|
148
+ doc = {"id" => "doc_#{i}", "key" => "value"}
149
+ @writer.put context_with(doc)
150
+ end
151
+
152
+ # Hasn't yet written
153
+ assert_not_requested(:post, "#{TEST_SOLR_URL}/update/json")
154
+
155
+ @writer.flush
156
+
157
+ # Has flushed to solr
158
+ assert_requested(:post, "#{TEST_SOLR_URL}/update/json")
159
+ end
160
+
161
+ # We check the header on an actual (mocked) request, rather than looking at
162
+ # the httpx client's configured options -- httpx 1.7 moved basic auth out of
163
+ # the client's default headers and into an option only applied at request time.
164
+ AUTH_TEST_URL = "http://example.com/solr/foo"
165
+
166
+ def write_one_doc(writer)
167
+ writer.put context_with({"id" => "one"})
168
+ writer.close
169
+ end
170
+
171
+ it "defaults to not setting basic authentication" do
172
+ stub_request(:post, "#{AUTH_TEST_URL}/update/json")
173
+
174
+ write_one_doc Traject::SolrJsonHttpxWriter.new("solr.url" => AUTH_TEST_URL)
175
+
176
+ assert_requested(:post, "#{AUTH_TEST_URL}/update/json") do |request|
177
+ request.headers["Authorization"].nil?
178
+ end
179
+ end
180
+
181
+ describe "HTTP basic auth" do
182
+ let(:expected_auth_header) { "Basic #{Base64.strict_encode64("foo:bar")}" }
183
+
184
+ it "supports basic authentication settings" do
185
+ stub_request(:post, "#{AUTH_TEST_URL}/update/json")
186
+
187
+ write_one_doc Traject::SolrJsonHttpxWriter.new(
188
+ "solr.url" => AUTH_TEST_URL,
189
+ "solr_writer.basic_auth_user" => "foo",
190
+ "solr_writer.basic_auth_password" => "bar",
191
+ )
192
+
193
+ assert_requested(:post, "#{AUTH_TEST_URL}/update/json",
194
+ headers: { "Authorization" => expected_auth_header })
195
+ end
196
+
197
+ it "supports basic auth from solr.url" do
198
+ stub_request(:post, "#{AUTH_TEST_URL}/update/json")
199
+
200
+ write_one_doc Traject::SolrJsonHttpxWriter.new("solr.url" => "http://foo:bar@example.com/solr/foo")
201
+
202
+ assert_requested(:post, "#{AUTH_TEST_URL}/update/json",
203
+ headers: { "Authorization" => expected_auth_header })
204
+ end
205
+
206
+ it "does not log basic auth from solr.url" do
207
+ string_io = StringIO.new
208
+ settings = {
209
+ "solr.url" => "http://secret_username:secret_password@example.com/solr/foo",
210
+ "logger" => Logger.new(string_io)
211
+ }
212
+
213
+
214
+ writer = Traject::SolrJsonHttpxWriter.new(settings)
215
+
216
+ refute_includes string_io.string, "secret_username:secret_password"
217
+ assert_includes string_io.string, "(with HTTP basic auth)"
218
+ end
219
+ end
220
+
221
+ describe "commit" do
222
+ it "commits on close when set" do
223
+ stub_request(:post, "#{TEST_SOLR_URL}/update/json")
224
+ stub_request(:get, "#{TEST_SOLR_URL}/update/json?commit=true")
225
+
226
+ @writer = create_writer("solr.url" => TEST_SOLR_URL, "solr_writer.commit_on_close" => "true")
227
+ @writer.put context_with({"id" => "one", "key" => ["value1", "value2"]})
228
+ @writer.close
229
+
230
+ assert_requested(:get, "#{TEST_SOLR_URL}/update/json?commit=true")
231
+ end
232
+
233
+ it "commits on close with commit_solr_update_args" do
234
+ stub_request(:post, "#{TEST_SOLR_URL}/update/json")
235
+ stub_request(:get, "#{TEST_SOLR_URL}/update/json?softCommit=true")
236
+
237
+ @writer = create_writer(
238
+ "solr.url" => TEST_SOLR_URL,
239
+ "solr_writer.commit_on_close" => "true",
240
+ "solr_writer.commit_solr_update_args" => { softCommit: true }
241
+ )
242
+ @writer.put context_with({"id" => "one", "key" => ["value1", "value2"]})
243
+ @writer.close
244
+
245
+ assert_requested(:get, "#{TEST_SOLR_URL}/update/json?softCommit=true")
246
+ end
247
+
248
+ it "can manually send commit" do
249
+ stub_request(:get, "#{TEST_SOLR_URL}/update/json?commit=true")
250
+
251
+ @writer = create_writer("solr.url" => TEST_SOLR_URL)
252
+ @writer.commit
253
+
254
+ assert_requested(:get, "#{TEST_SOLR_URL}/update/json?commit=true")
255
+ end
256
+
257
+ it "can manually send commit with specified args" do
258
+ stub_request(:get, "#{TEST_SOLR_URL}/update/json?commit=true&optimize=true&waitFlush=false")
259
+
260
+ @writer = create_writer("solr.url" => TEST_SOLR_URL, "solr_writer.commit_solr_update_args" => { softCommit: true })
261
+ @writer.commit(commit: true, optimize: true, waitFlush: false)
262
+
263
+ assert_requested(:get, "#{TEST_SOLR_URL}/update/json?commit=true&optimize=true&waitFlush=false")
264
+ end
265
+
266
+ it "uses commit_solr_update_args settings by default" do
267
+ stub_request(:get, "#{TEST_SOLR_URL}/update/json?softCommit=true")
268
+
269
+ @writer = create_writer(
270
+ "solr.url" => TEST_SOLR_URL,
271
+ "solr_writer.commit_solr_update_args" => { softCommit: true }
272
+ )
273
+ @writer.commit
274
+
275
+ assert_requested(:get, "#{TEST_SOLR_URL}/update/json?softCommit=true")
276
+ end
277
+
278
+ it "overrides commit_solr_update_args with method arg" do
279
+ stub_request(:get, "#{TEST_SOLR_URL}/update/json?commit=true")
280
+
281
+ @writer = create_writer(
282
+ "solr.url" => TEST_SOLR_URL,
283
+ "solr_writer.commit_solr_update_args" => { softCommit: true, foo: "bar" }
284
+ )
285
+ @writer.commit(commit: true)
286
+
287
+ assert_requested(:get, "#{TEST_SOLR_URL}/update/json?commit=true")
288
+ end
289
+ end
290
+
291
+ describe "solr_writer.solr_update_args" do
292
+ before do
293
+ @writer = create_writer("solr_writer.solr_update_args" => { softCommit: true } )
294
+ end
295
+
296
+ it "sends update args" do
297
+ stub_request(:post, "#{TEST_SOLR_URL}/update/json?softCommit=true")
298
+
299
+ @writer.put context_with({"id" => "one", "key" => ["value1", "value2"]})
300
+ @writer.close
301
+
302
+ assert_requested(:post, "#{TEST_SOLR_URL}/update/json?softCommit=true")
303
+ end
304
+
305
+ it "sends update args with delete" do
306
+ stub_request(:post, "#{TEST_SOLR_URL}/update/json?softCommit=true")
307
+
308
+ @writer.delete("test-id")
309
+ @writer.close
310
+
311
+ assert_requested(:post, "#{TEST_SOLR_URL}/update/json?softCommit=true", times: 1)
312
+ end
313
+
314
+ it "sends update args on individual-retry after batch failure" do
315
+ stub_request(:post, "#{TEST_SOLR_URL}/update/json?softCommit=true").to_return(status: 500)
316
+
317
+ @writer = create_writer(
318
+ "solr_writer.batch_size" => 2,
319
+ "solr_writer.max_skipped" => 10,
320
+ "solr_writer.solr_update_args" => { softCommit: true }
321
+ )
322
+
323
+ 2.times do |i|
324
+ @writer.put context_with({"id" => "doc_#{i}", "key" => "value"})
325
+ end
326
+ @writer.close
327
+
328
+ # 1 batch, then 2 for re-trying each individually
329
+ assert_requested(:post, "#{TEST_SOLR_URL}/update/json?softCommit=true", times: 3)
330
+ end
331
+ end
332
+
333
+ describe "skipped records" do
334
+ it "skips and reports under max_skipped" do
335
+ stub_request(:post, "#{TEST_SOLR_URL}/update/json").to_return(status: 500)
336
+
337
+ strio = StringIO.new
338
+ @writer = create_writer("solr_writer.max_skipped" => 10, "logger" => logger_to_strio(strio))
339
+
340
+ 10.times do |i|
341
+ @writer.put context_with("id" => "doc_#{i}", "key" => "value")
342
+ end
343
+ @writer.close
344
+
345
+ assert_equal 10, @writer.skipped_record_count
346
+
347
+ logged = strio.string
348
+
349
+ 10.times do |i|
350
+ assert_match(/ERROR.*Could not add record <output_id:doc_#{i}>: Solr error response: 500/, logged)
351
+ end
352
+ end
353
+
354
+ it "raises when skipped more than max_skipped" do
355
+ stub_request(:post, "#{TEST_SOLR_URL}/update/json").to_return(status: 500)
356
+
357
+ @writer = create_writer("solr_writer.max_skipped" => 5)
358
+
359
+ e = assert_raises(RuntimeError) do
360
+ 6.times do |i|
361
+ @writer.put context_with("id" => "doc_#{i}", "key" => "value")
362
+ end
363
+ @writer.close
364
+ end
365
+
366
+ assert_includes e.message, "Exceeded maximum number of skipped records"
367
+ end
368
+
369
+ it "raises on one skipped record when max_skipped is 0" do
370
+ stub_request(:post, "#{TEST_SOLR_URL}/update/json").to_return(status: 500)
371
+
372
+ @writer = create_writer("solr_writer.max_skipped" => 0)
373
+
374
+ _e = assert_raises(RuntimeError) do
375
+ @writer.put context_with("id" => "doc_1", "key" => "value")
376
+ @writer.close
377
+ end
378
+ end
379
+
380
+
381
+ it "when catching additional skip errors, raise RuntimeError" do
382
+ stub_request(:post, "#{TEST_SOLR_URL}/update/json").to_raise(ArgumentError.new('bad stuff'))
383
+
384
+ strio = StringIO.new
385
+ @writer = create_writer(
386
+ "solr_writer.max_skipped" => 0,
387
+ "logger" => logger_to_strio(strio),
388
+ "solr_writer.skippable_exceptions" => [ArgumentError]
389
+ )
390
+
391
+ _e = assert_raises(Traject::SolrJsonHttpxWriter::MaxSkippedRecordsExceeded) do
392
+ @writer.put context_with("id" => "doc_1", "key" => "value")
393
+ @writer.close
394
+ end
395
+ logged = strio.string
396
+ assert_includes logged, 'ArgumentError: bad stuff'
397
+ end
398
+ end
399
+
400
+ describe "#delete" do
401
+ it "deletes" do
402
+ stub_request(:post, "#{TEST_SOLR_URL}/update/json")
403
+
404
+ id = "123456"
405
+ @writer.delete(id)
406
+
407
+ assert_requested(:post, "#{TEST_SOLR_URL}/update/json", body: JSON.generate({"delete" => id}))
408
+ end
409
+
410
+ it "raises on non-200 http response" do
411
+ stub_request(:post, "#{TEST_SOLR_URL}/update/json").to_return(status: 500)
412
+
413
+ assert_raises(RuntimeError) do
414
+ @writer.delete("12345")
415
+ end
416
+ end
417
+ end
418
+
419
+ describe "#delete_all!" do
420
+ it "deletes all" do
421
+ stub_request(:post, "#{TEST_SOLR_URL}/update/json")
422
+
423
+ @writer.delete_all!
424
+
425
+ assert_requested(:post, "#{TEST_SOLR_URL}/update/json", body: JSON.generate({"delete" => { "query" => "*:*"}}))
426
+ end
427
+ end
428
+ end
data/test/test_helper.rb CHANGED
@@ -3,6 +3,7 @@ require 'minitest/autorun'
3
3
  require 'minitest/spec'
4
4
 
5
5
  require 'webmock/minitest'
6
+ require "httpx/adapters/webmock"
6
7
 
7
8
  require 'traject'
8
9
  require 'marc'
data/traject.gemspec CHANGED
@@ -30,15 +30,17 @@ Gem::Specification.new do |spec|
30
30
  spec.add_dependency "yell" # logging
31
31
  spec.add_dependency "dot-properties", ">= 0.1.1" # reading java style .properties
32
32
  spec.add_dependency "httpclient", "~> 2.5"
33
+ spec.add_dependency "httpx", "~> 1.4"
33
34
  spec.add_dependency "mutex_m" # httpclient has an undelcared dep needed in ruby 3.4+, not yet released. https://github.com/nahi/httpclient/pull/455
35
+ spec.add_dependency "base64" # no longer inlcuded in stdlib
34
36
 
35
- spec.add_dependency "http", ">= 3.0", "< 6" # used in oai_pmh_reader, may use more extensively in future instead of httpclient
37
+ spec.add_dependency "http", ">= 3.0", "< 7" # used in oai_pmh_reader, may use more extensively in future instead of httpclient
36
38
  spec.add_dependency 'marc-fastxmlwriter', '~>1.0' # fast marc->xml
37
39
  spec.add_dependency "nokogiri", "~> 1.9" # NokogiriIndexer
38
40
 
39
- spec.add_development_dependency 'bundler', '>= 1.7', '< 3'
40
-
41
41
  spec.add_development_dependency "rake"
42
42
  spec.add_development_dependency "minitest"
43
43
  spec.add_development_dependency "rspec-mocks", '~> 3.4'
44
+ # rspec-mocks no longer includes rspec-core as a dependency, we need to include it.
45
+ spec.add_development_dependency "rspec-core", '~> 3.4'
44
46
  end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: traject
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.8.3
4
+ version: 3.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Rochkind
8
8
  - Bill Dueber
9
+ autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2025-01-28 00:00:00.000000000 Z
12
+ date: 2026-07-21 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: concurrent-ruby
@@ -128,6 +129,20 @@ dependencies:
128
129
  - - "~>"
129
130
  - !ruby/object:Gem::Version
130
131
  version: '2.5'
132
+ - !ruby/object:Gem::Dependency
133
+ name: httpx
134
+ requirement: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.4'
139
+ type: :runtime
140
+ prerelease: false
141
+ version_requirements: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '1.4'
131
146
  - !ruby/object:Gem::Dependency
132
147
  name: mutex_m
133
148
  requirement: !ruby/object:Gem::Requirement
@@ -142,6 +157,20 @@ dependencies:
142
157
  - - ">="
143
158
  - !ruby/object:Gem::Version
144
159
  version: '0'
160
+ - !ruby/object:Gem::Dependency
161
+ name: base64
162
+ requirement: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ type: :runtime
168
+ prerelease: false
169
+ version_requirements: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
145
174
  - !ruby/object:Gem::Dependency
146
175
  name: http
147
176
  requirement: !ruby/object:Gem::Requirement
@@ -151,7 +180,7 @@ dependencies:
151
180
  version: '3.0'
152
181
  - - "<"
153
182
  - !ruby/object:Gem::Version
154
- version: '6'
183
+ version: '7'
155
184
  type: :runtime
156
185
  prerelease: false
157
186
  version_requirements: !ruby/object:Gem::Requirement
@@ -161,7 +190,7 @@ dependencies:
161
190
  version: '3.0'
162
191
  - - "<"
163
192
  - !ruby/object:Gem::Version
164
- version: '6'
193
+ version: '7'
165
194
  - !ruby/object:Gem::Dependency
166
195
  name: marc-fastxmlwriter
167
196
  requirement: !ruby/object:Gem::Requirement
@@ -191,27 +220,21 @@ dependencies:
191
220
  - !ruby/object:Gem::Version
192
221
  version: '1.9'
193
222
  - !ruby/object:Gem::Dependency
194
- name: bundler
223
+ name: rake
195
224
  requirement: !ruby/object:Gem::Requirement
196
225
  requirements:
197
226
  - - ">="
198
227
  - !ruby/object:Gem::Version
199
- version: '1.7'
200
- - - "<"
201
- - !ruby/object:Gem::Version
202
- version: '3'
228
+ version: '0'
203
229
  type: :development
204
230
  prerelease: false
205
231
  version_requirements: !ruby/object:Gem::Requirement
206
232
  requirements:
207
233
  - - ">="
208
234
  - !ruby/object:Gem::Version
209
- version: '1.7'
210
- - - "<"
211
- - !ruby/object:Gem::Version
212
- version: '3'
235
+ version: '0'
213
236
  - !ruby/object:Gem::Dependency
214
- name: rake
237
+ name: minitest
215
238
  requirement: !ruby/object:Gem::Requirement
216
239
  requirements:
217
240
  - - ">="
@@ -225,21 +248,21 @@ dependencies:
225
248
  - !ruby/object:Gem::Version
226
249
  version: '0'
227
250
  - !ruby/object:Gem::Dependency
228
- name: minitest
251
+ name: rspec-mocks
229
252
  requirement: !ruby/object:Gem::Requirement
230
253
  requirements:
231
- - - ">="
254
+ - - "~>"
232
255
  - !ruby/object:Gem::Version
233
- version: '0'
256
+ version: '3.4'
234
257
  type: :development
235
258
  prerelease: false
236
259
  version_requirements: !ruby/object:Gem::Requirement
237
260
  requirements:
238
- - - ">="
261
+ - - "~>"
239
262
  - !ruby/object:Gem::Version
240
- version: '0'
263
+ version: '3.4'
241
264
  - !ruby/object:Gem::Dependency
242
- name: rspec-mocks
265
+ name: rspec-core
243
266
  requirement: !ruby/object:Gem::Requirement
244
267
  requirements:
245
268
  - - "~>"
@@ -252,6 +275,7 @@ dependencies:
252
275
  - - "~>"
253
276
  - !ruby/object:Gem::Version
254
277
  version: '3.4'
278
+ description:
255
279
  email:
256
280
  - none@nowhere.org
257
281
  executables:
@@ -316,6 +340,7 @@ files:
316
340
  - lib/traject/null_writer.rb
317
341
  - lib/traject/oai_pmh_nokogiri_reader.rb
318
342
  - lib/traject/qualified_const_get.rb
343
+ - lib/traject/solr_json_httpx_writer.rb
319
344
  - lib/traject/solr_json_writer.rb
320
345
  - lib/traject/thread_pool.rb
321
346
  - lib/traject/translation_map.rb
@@ -357,6 +382,7 @@ files:
357
382
  - test/marc_reader_test.rb
358
383
  - test/nokogiri_reader_test.rb
359
384
  - test/oai_pmh_nokogiri_reader_test.rb
385
+ - test/solr_json_httpx_writer_test.rb
360
386
  - test/solr_json_writer_test.rb
361
387
  - test/test_helper.rb
362
388
  - test/test_support/245_no_ab.marc
@@ -414,6 +440,7 @@ homepage: http://github.com/traject/traject
414
440
  licenses:
415
441
  - MIT
416
442
  metadata: {}
443
+ post_install_message:
417
444
  rdoc_options: []
418
445
  require_paths:
419
446
  - lib
@@ -428,7 +455,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
428
455
  - !ruby/object:Gem::Version
429
456
  version: '0'
430
457
  requirements: []
431
- rubygems_version: 3.6.2
458
+ rubygems_version: 3.1.6
459
+ signing_key:
432
460
  specification_version: 4
433
461
  summary: An easy to use, high-performance, flexible and extensible metadata transformation
434
462
  system, focused on library-archives-museums input, and indexing to Solr as output.
@@ -462,6 +490,7 @@ test_files:
462
490
  - test/marc_reader_test.rb
463
491
  - test/nokogiri_reader_test.rb
464
492
  - test/oai_pmh_nokogiri_reader_test.rb
493
+ - test/solr_json_httpx_writer_test.rb
465
494
  - test/solr_json_writer_test.rb
466
495
  - test/test_helper.rb
467
496
  - test/test_support/245_no_ab.marc