meilisearch 0.31.0 → 0.32.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d6cedcf8f0f86e12bfaee3c4f1464af380f1b6df1943746fc2f954aba8aa1090
4
- data.tar.gz: c387d93dcfbf3fadd5682469c2b0aab284603afbfa503d77fc9ee674966a724a
3
+ metadata.gz: 10979c6d34127afb63d73049967287282428a19ae0d0da81e318ebecbdd5061a
4
+ data.tar.gz: 5ab882422d999ad86bba6b7b4278b511ca63e47292b9a02c2d3ee62adcecb1a0
5
5
  SHA512:
6
- metadata.gz: fbd5af58608cb342538232663cddc27d073af9aa3d598385c9a227ef615eb556b8c0f0f6195a977fe89ac542b4efd635aae14747a82b21a821c89272ab619b22
7
- data.tar.gz: 15caa179a6f58ff36d2ec4abf1d9b40f8477ba62cbfe49cf0f3944465ed1a2b2c8a5cdc69ab15f9ecc42ca525ce6b553adc17444727adf46d1161ec8b1732e1f
6
+ metadata.gz: d9dfc97758734f2501107df819bb4a2a260af515acce221726a042dc193f3cd1abc3a7d7eaed0cdd218c549aad6b63fc6d661940fc7395fa6180a5d5293844c0
7
+ data.tar.gz: 370ceb4c95bfa7bc3df49a780edc030d22469898cc325eba7a00db6d2275a39c1af98e49cf77ea4779d9823768d7b79b851449930e9c73d8d526cec8948a1a3e
@@ -1,18 +1,51 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Meilisearch
4
+ # Manages a connection to a Meilisearch server.
5
+ # client = Meilisearch::Client.new(MEILISEARCH_URL, MASTER_KEY, options)
6
+ #
7
+ # @see #indexes Managing search indexes
8
+ # @see #keys Managing API keys
9
+ # @see #stats View usage statistics
10
+ # @see #tasks Managing ongoing tasks
11
+ # @see #health Health checking
12
+ # @see #create_dump
13
+ # @see #create_snapshot
4
14
  class Client < HTTPRequest
5
15
  include Meilisearch::TenantToken
6
16
  include Meilisearch::MultiSearch
7
17
 
8
18
  ### INDEXES
9
19
 
20
+ # Fetch indexes in instance, returning the raw server response.
21
+ #
22
+ # Unless you have a good reason to, {#indexes} should be used instead.
23
+ #
24
+ # @see #indexes
25
+ # @see https://www.meilisearch.com/docs/reference/api/indexes#list-all-indexes Meilisearch API reference
26
+ # @param options [Hash{Symbol => Object}] limit and offset options
27
+ # @return [Hash{String => Object}]
28
+ # {index response object}[https://www.meilisearch.com/docs/reference/api/indexes#response]
10
29
  def raw_indexes(options = {})
11
30
  body = Utils.transform_attributes(options.transform_keys(&:to_sym).slice(:limit, :offset))
12
31
 
13
32
  http_get('/indexes', body)
14
33
  end
15
34
 
35
+ # Swap two indexes.
36
+ #
37
+ # Can be used as a convenient way to rebuild an index while keeping it operational.
38
+ # client.index('a_swap').add_documents({})
39
+ # client.swap_indexes(['a', 'a_swap'])
40
+ #
41
+ # Multiple swaps may be done with one request:
42
+ # client.swap_indexes(['a', 'a_swap'], ['b', 'b_swap'])
43
+ #
44
+ # @see https://www.meilisearch.com/docs/reference/api/indexes#swap-indexes Meilisearch API reference
45
+ #
46
+ # @param options [Array<Array(String, String)>] the indexes to swap
47
+ # @return [Models::Task] the async task that swaps the indexes
48
+ # @raise [ApiError]
16
49
  def swap_indexes(*options)
17
50
  mapped_array = options.map { |arr| { indexes: arr } }
18
51
 
@@ -20,6 +53,13 @@ module Meilisearch
20
53
  Models::Task.new(response, task_endpoint)
21
54
  end
22
55
 
56
+ # Fetch indexes in instance.
57
+ #
58
+ # @see https://www.meilisearch.com/docs/reference/api/indexes#list-all-indexes Meilisearch API reference
59
+ # @param options [Hash{Symbol => Object}] limit and offset options
60
+ # @return [Hash{String => Object}]
61
+ # {index response object}[https://www.meilisearch.com/docs/reference/api/indexes#response]
62
+ # with results mapped to instances of {Index}
23
63
  def indexes(options = {})
24
64
  response = raw_indexes(options)
25
65
 
@@ -30,9 +70,23 @@ module Meilisearch
30
70
  response
31
71
  end
32
72
 
33
- # Usage:
34
- # client.create_index('indexUID')
35
- # client.create_index('indexUID', primary_key: 'id')
73
+ # Create a new empty index.
74
+ #
75
+ # client.create_index('indexUID')
76
+ # client.create_index('indexUID', primary_key: 'id')
77
+ #
78
+ # Indexes are also created when accessed:
79
+ #
80
+ # client.index('new_index').add_documents({})
81
+ #
82
+ # @see #index
83
+ # @see https://www.meilisearch.com/docs/reference/api/indexes#create-an-index Meilisearch API reference
84
+ #
85
+ # @param index_uid [String] the uid of the new index
86
+ # @param options [Hash{Symbol => Object}, nil] snake_cased options of {the endpoint}[https://www.meilisearch.com/docs/reference/api/indexes#create-an-index]
87
+ #
88
+ # @raise [ApiError]
89
+ # @return [Models::Task] the async task that creates the index
36
90
  def create_index(index_uid, options = {})
37
91
  body = Utils.transform_attributes(options.merge(uid: index_uid))
38
92
 
@@ -41,8 +95,14 @@ module Meilisearch
41
95
  Models::Task.new(response, task_endpoint)
42
96
  end
43
97
 
44
- # Synchronous version of create_index.
45
- # Waits for the task to be achieved, be careful when using it.
98
+ # Synchronous version of {#create_index}.
99
+ #
100
+ # @deprecated
101
+ # use {Models::Task#await} on task returned from {#create_index}
102
+ #
103
+ # client.create_index('foo').await
104
+ #
105
+ # Waits for the task to be achieved with a busy loop, be careful when using it.
46
106
  def create_index!(index_uid, options = {})
47
107
  Utils.soft_deprecate(
48
108
  'Client#create_index!',
@@ -52,42 +112,131 @@ module Meilisearch
52
112
  create_index(index_uid, options).await
53
113
  end
54
114
 
115
+ # Delete an index.
116
+ #
117
+ # @param index_uid [String] the uid of the index to be deleted
118
+ # @return [Models::Task] the async task deleting the index
55
119
  def delete_index(index_uid)
56
120
  index_object(index_uid).delete
57
121
  end
58
122
 
59
- # Usage:
60
- # client.index('indexUID')
123
+ # Get index with given uid.
124
+ #
125
+ # Indexes that don't exist are lazily created by Meilisearch.
126
+ # index = client.index('index_uid')
127
+ # index.add_documents({}) # index is created here if it did not exist
128
+ #
129
+ # @see Index
130
+ # @param index_uid [String] the uid of the index to get
131
+ # @return [Index]
61
132
  def index(index_uid)
62
133
  index_object(index_uid)
63
134
  end
64
135
 
136
+ # Shorthand for
137
+ # client.index(index_uid).fetch_info
138
+ #
139
+ # @see Index#fetch_info
140
+ # @param index_uid [String] uid of the index
65
141
  def fetch_index(index_uid)
66
142
  index_object(index_uid).fetch_info
67
143
  end
68
144
 
145
+ # Shorthand for
146
+ # client.index(index_uid).fetch_raw_info
147
+ #
148
+ # @see Index#fetch_raw_info
149
+ # @param index_uid [String] uid of the index
69
150
  def fetch_raw_index(index_uid)
70
151
  index_object(index_uid).fetch_raw_info
71
152
  end
72
153
 
73
154
  ### KEYS
74
155
 
156
+ # Get all API keys
157
+ #
158
+ # This and other key methods require that the Meilisearch instance have a
159
+ # {master key}[https://www.meilisearch.com/docs/learn/security/differences_master_api_keys#master-key]
160
+ # set.
161
+ #
162
+ # @see #create_key #create_key to create keys and set their scope
163
+ # @see #key #key to fetch one key
164
+ # @see https://www.meilisearch.com/docs/reference/api/keys#get-all-keys Meilisearch API reference
165
+ # @param limit [String, Integer, nil] limit the number of returned keys
166
+ # @param offset [String, Integer, nil] skip the first +offset+ keys,
167
+ # useful for paging.
168
+ #
169
+ # @return [Hash{String => Object}] a {keys response}[https://www.meilisearch.com/docs/reference/api/keys#response]
75
170
  def keys(limit: nil, offset: nil)
76
171
  body = { limit: limit, offset: offset }.compact
77
172
 
78
173
  http_get '/keys', body
79
174
  end
80
175
 
176
+ # Get a specific API key.
177
+ #
178
+ # # obviously this example uid will not correspond to a key on your server
179
+ # # please replace it with your own key's uid
180
+ # uid = '6062abda-a5aa-4414-ac91-ecd7944c0f8d'
181
+ # client.key(uid)
182
+ #
183
+ # This and other key methods require that the Meilisearch instance have a
184
+ # {master key}[https://www.meilisearch.com/docs/learn/security/differences_master_api_keys#master-key]
185
+ # set.
186
+ #
187
+ # @see #keys #keys to get all keys in the instance
188
+ # @see #create_key #create_key to create keys and set their scope
189
+ # @see https://www.meilisearch.com/docs/reference/api/keys#get-one-key Meilisearch API reference
190
+ # @param uid_or_key [String] either the uuidv4 that is the key's
191
+ # {uid}[https://www.meilisearch.com/docs/reference/api/keys#uid] or
192
+ # a hash of the uid and the master key that is the key's
193
+ # {key}[https://www.meilisearch.com/docs/reference/api/keys#key] field
194
+ #
195
+ # @return [Hash{String => Object}] a {key object}[https://www.meilisearch.com/docs/reference/api/keys#key-object]
81
196
  def key(uid_or_key)
82
197
  http_get "/keys/#{uid_or_key}"
83
198
  end
84
199
 
200
+ # Create a new API key.
201
+ #
202
+ # require 'date_core'
203
+ # ten_days_later = (DateTime.now + 10).rfc3339
204
+ # client.create_key(actions: ['*'], indexes: ['*'], expires_at: ten_days_later)
205
+ #
206
+ # This and other key methods require that the Meilisearch instance have a
207
+ # {master key}[https://www.meilisearch.com/docs/learn/security/differences_master_api_keys#master-key]
208
+ # set.
209
+ #
210
+ # @see #update_key #update_key to edit an existing key
211
+ # @see #keys #keys to get all keys in the instance
212
+ # @see #key #key to fetch one key
213
+ # @see https://www.meilisearch.com/docs/reference/api/keys#create-a-key Meilisearch API reference
214
+ # @param key_options [Hash{Symbol => Object}] the key options of which the required are
215
+ # - +:actions+ +Array+ of API actions allowed for key, +["*"]+ for all
216
+ # - +:indexes+ +Array+ of indexes key can act on, +["*"]+ for all
217
+ # - +:expires_at+ expiration datetime in
218
+ # {RFC 3339}[https://www.ietf.org/rfc/rfc3339.txt] format, nil if the key never expires
219
+ #
220
+ # @return [Hash{String => Object}] a {key object}[https://www.meilisearch.com/docs/reference/api/keys#key-object]
85
221
  def create_key(key_options)
86
222
  body = Utils.transform_attributes(key_options)
87
223
 
88
224
  http_post '/keys', body
89
225
  end
90
226
 
227
+ # Update an existing API key.
228
+ #
229
+ # This and other key methods require that the Meilisearch instance have a
230
+ # {master key}[https://www.meilisearch.com/docs/learn/security/differences_master_api_keys#master-key]
231
+ # set.
232
+ #
233
+ # @see #create_key #create_key to create a new key
234
+ # @see #keys #keys to get all keys in the instance
235
+ # @see #key #key to fetch one key
236
+ # @see https://www.meilisearch.com/docs/reference/api/keys#update-a-key Meilisearch API reference
237
+ # @param key_options [Hash{Symbol => Object}] see {#create_key}
238
+ #
239
+ # @return [Hash{String => Object}] a {key object}[https://www.meilisearch.com/docs/reference/api/keys#key-object]
91
240
  def update_key(uid_or_key, key_options)
92
241
  body = Utils.transform_attributes(key_options)
93
242
  body = body.slice('description', 'name')
@@ -95,12 +244,34 @@ module Meilisearch
95
244
  http_patch "/keys/#{uid_or_key}", body
96
245
  end
97
246
 
247
+ # Delete an API key.
248
+ #
249
+ # # obviously this example uid will not correspond to a key on your server
250
+ # # please replace it with your own key's uid
251
+ # uid = '6062abda-a5aa-4414-ac91-ecd7944c0f8d'
252
+ # client.delete_key(uid)
253
+ #
254
+ # This and other key methods require that the Meilisearch instance have a
255
+ # {master key}[https://www.meilisearch.com/docs/learn/security/differences_master_api_keys#master-key]
256
+ # set.
257
+ #
258
+ # @see #keys #keys to get all keys in the instance
259
+ # @see #create_key #create_key to create keys and set their scope
260
+ # @see https://www.meilisearch.com/docs/reference/api/keys#delete-a-key Meilisearch API reference
261
+ # @param uid_or_key [String] either the uuidv4 that is the key's
262
+ # {uid}[https://www.meilisearch.com/docs/reference/api/keys#uid] or
263
+ # a hash of the uid and the master key that is the key's
264
+ # {key}[https://www.meilisearch.com/docs/reference/api/keys#key] field
98
265
  def delete_key(uid_or_key)
99
266
  http_delete "/keys/#{uid_or_key}"
100
267
  end
101
268
 
102
269
  ### HEALTH
103
270
 
271
+ # Check if Meilisearch instance is healthy.
272
+ #
273
+ # @see #health
274
+ # @return [bool] whether or not the +/health+ endpoint raises any errors
104
275
  def healthy?
105
276
  http_get '/health'
106
277
  true
@@ -108,22 +279,51 @@ module Meilisearch
108
279
  false
109
280
  end
110
281
 
282
+ # Check health of Meilisearch instance.
283
+ #
284
+ # @see https://www.meilisearch.com/docs/reference/api/health#get-health Meilisearch API reference
285
+ # @return [Hash{String => Object}] the health report from the Meilisearch instance
111
286
  def health
112
287
  http_get '/health'
113
288
  end
114
289
 
115
290
  ### STATS
116
291
 
292
+ # Check version of Meilisearch server
293
+ #
294
+ # @see https://www.meilisearch.com/docs/reference/api/version#get-version-of-meilisearch Meilisearch API reference
295
+ # @return [Hash{String => String}] package version and last commit of Meilisearch server, see
296
+ # {version object}[https://www.meilisearch.com/docs/reference/api/version#version-object]
117
297
  def version
118
298
  http_get '/version'
119
299
  end
120
300
 
301
+ # Get stats of all indexes in instance.
302
+ #
303
+ # @see Index#stats
304
+ # @see https://www.meilisearch.com/docs/reference/api/stats#get-stats-of-all-indexes Meilisearch API reference
305
+ # @return [Hash{String => Object}] see {stats object}[https://www.meilisearch.com/docs/reference/api/stats#stats-object]
121
306
  def stats
122
307
  http_get '/stats'
123
308
  end
124
309
 
125
310
  ### DUMPS
126
311
 
312
+ # Create a database dump.
313
+ #
314
+ # Dumps are "blueprints" which can be used to restore your database. Restoring
315
+ # a dump requires reindexing all documents and is therefore inefficient.
316
+ #
317
+ # Dumps are created by the Meilisearch server in the directory where the server is started
318
+ # under +dumps/+ by default.
319
+ #
320
+ # @see https://www.meilisearch.com/docs/learn/advanced/snapshots_vs_dumps
321
+ # The difference between snapshots and dumps
322
+ # @see https://www.meilisearch.com/docs/learn/advanced/dumps
323
+ # Meilisearch documentation on how to use dumps
324
+ # @see https://www.meilisearch.com/docs/reference/api/dump#create-a-dump
325
+ # Meilisearch API reference
326
+ # @return [Models::Task] the async task that is creating the dump
127
327
  def create_dump
128
328
  response = http_post '/dumps'
129
329
  Models::Task.new(response, task_endpoint)
@@ -131,42 +331,151 @@ module Meilisearch
131
331
 
132
332
  ### SNAPSHOTS
133
333
 
334
+ # Create a database snapshot.
335
+ #
336
+ # Snapshots are exact copies of the Meilisearch database. As such they are pre-indexed
337
+ # and restoring one is a very efficient operation.
338
+ #
339
+ # Snapshots are not compatible between Meilisearch versions. Snapshot creation takes priority
340
+ # over other tasks.
341
+ #
342
+ # Snapshots are created by the Meilisearch server in the directory where the server is started
343
+ # under +snapshots/+ by default.
344
+ #
345
+ # @see https://www.meilisearch.com/docs/learn/advanced/snapshots_vs_dumps
346
+ # The difference between snapshots and dumps
347
+ # @see https://www.meilisearch.com/docs/learn/advanced/snapshots
348
+ # Meilisearch documentation on how to use snapshots
349
+ # @see https://www.meilisearch.com/docs/reference/api/snapshots#create-a-snapshot
350
+ # Meilisearch API reference
351
+ # @return [Models::Task] the async task that is creating the snapshot
134
352
  def create_snapshot
135
353
  http_post '/snapshots'
136
354
  end
137
355
 
138
356
  ### TASKS
139
357
 
358
+ # Cancel tasks matching the filter.
359
+ #
360
+ # This route is meant to be used with options, please see the API reference.
361
+ #
362
+ # Operations in Meilisearch are done asynchronously using "tasks".
363
+ # Tasks report their progress and status.
364
+ #
365
+ # Warning: This does not return instances of {Models::Task}. This is a raw
366
+ # call to the Meilisearch API and the return is not modified.
367
+ #
368
+ # @see https://www.meilisearch.com/docs/reference/api/tasks#task-object The Task Object
369
+ # @see https://www.meilisearch.com/docs/reference/api/tasks#cancel-tasks Meilisearch API reference
370
+ # @param options [Hash{Symbol => Object}] task search options as snake cased symbols, see the API reference
371
+ # @return [Hash{String => Object}] a Meilisearch task that is canceling other tasks
140
372
  def cancel_tasks(options = {})
141
373
  task_endpoint.cancel_tasks(options)
142
374
  end
143
375
 
376
+ # Cancel tasks matching the filter.
377
+ #
378
+ # This route is meant to be used with options, please see the API reference.
379
+ #
380
+ # Operations in Meilisearch are done asynchronously using "tasks".
381
+ # Tasks report their progress and status.
382
+ #
383
+ # Warning: This does not return instances of {Models::Task}. This is a raw
384
+ # call to the Meilisearch API and the return is not modified.
385
+ #
386
+ # Tasks are run in batches, see {#batches}.
387
+ #
388
+ # @see https://www.meilisearch.com/docs/reference/api/tasks#task-object The Task Object
389
+ # @see https://www.meilisearch.com/docs/reference/api/tasks#cancel-tasks Meilisearch API reference
390
+ # @param options [Hash{Symbol => Object}] task search options as snake cased symbols, see the API reference
391
+ # @return [Hash{String => Object}] a Meilisearch task that is canceling other tasks
144
392
  def delete_tasks(options = {})
145
393
  task_endpoint.delete_tasks(options)
146
394
  end
147
395
 
396
+ # Get Meilisearch tasks matching the filters.
397
+ #
398
+ # Operations in Meilisearch are done asynchronously using "tasks".
399
+ # Tasks report their progress and status.
400
+ #
401
+ # Warning: This does not return instances of {Models::Task}. This is a raw
402
+ # call to the Meilisearch API and the return is not modified.
403
+ #
404
+ # @see https://www.meilisearch.com/docs/reference/api/tasks#task-object The Task Object
405
+ # @see https://www.meilisearch.com/docs/reference/api/tasks#get-tasks Meilisearch API reference
406
+ # @param options [Hash{Symbol => Object}] task search options as snake cased symbols, see the API reference
407
+ # @return [Hash{String => Object}] results of the task search, see API reference
148
408
  def tasks(options = {})
149
409
  task_endpoint.task_list(options)
150
410
  end
151
411
 
412
+ # Get one task.
413
+ #
414
+ # Operations in Meilisearch are done asynchronously using "tasks".
415
+ # Tasks report their progress and status.
416
+ #
417
+ # Warning: This does not return instances of {Models::Task}. This is a raw
418
+ # call to the Meilisearch API and the return is not modified.
419
+ #
420
+ # @see https://www.meilisearch.com/docs/reference/api/tasks#task-object The Task Object
421
+ # @see https://www.meilisearch.com/docs/reference/api/tasks#get-one-task Meilisearch API reference
422
+ # @param task_uid [String] uid of the requested task
423
+ # @return [Hash{String => Object}] a Meilisearch task object (see above)
152
424
  def task(task_uid)
153
425
  task_endpoint.task(task_uid)
154
426
  end
155
427
 
156
- def wait_for_task(task_uid, timeout_in_ms = 5000, interval_in_ms = 50)
428
+ # Wait for a task in a busy loop.
429
+ #
430
+ # Try to avoid using it. Wrapper around {Task#wait_for_task}.
431
+ # @see Task#wait_for_task
432
+ def wait_for_task(
433
+ task_uid,
434
+ timeout_in_ms = Models::Task.default_timeout_ms,
435
+ interval_in_ms = Models::Task.default_interval_ms
436
+ )
157
437
  task_endpoint.wait_for_task(task_uid, timeout_in_ms, interval_in_ms)
158
438
  end
159
439
 
160
440
  ### BATCHES
161
441
 
442
+ # Get Meilisearch task batches matching the filters.
443
+ #
444
+ # Operations in Meilisearch are done asynchronously using "tasks".
445
+ # Tasks are run in batches.
446
+ #
447
+ # @see https://www.meilisearch.com/docs/reference/api/batches#batch-object The Batch Object
448
+ # @see https://www.meilisearch.com/docs/reference/api/batches#get-batches Meilisearch API reference
449
+ # @param options [Hash{Symbol => Object}] task search options as snake cased symbols, see the API reference
450
+ # @return [Hash{String => Object}] results of the batches search, see API reference
162
451
  def batches(options = {})
163
452
  http_get '/batches', options
164
453
  end
165
454
 
455
+ # Get a single Meilisearch task batch matching +batch_uid+.
456
+ #
457
+ # Operations in Meilisearch are done asynchronously using "tasks".
458
+ # Tasks are run in batches.
459
+ #
460
+ # @see https://www.meilisearch.com/docs/reference/api/batches#batch-object The Batch Object
461
+ # @see https://www.meilisearch.com/docs/reference/api/batches#get-one-batch Meilisearch API reference
462
+ # @param batch_uid [String] the uid of the request batch
463
+ # @return [Hash{String => Object}] a batch object, see above
166
464
  def batch(batch_uid)
167
465
  http_get "/batches/#{batch_uid}"
168
466
  end
169
467
 
468
+ ### EXPERIMENTAL FEATURES
469
+
470
+ def experimental_features
471
+ http_get '/experimental-features'
472
+ end
473
+
474
+ def update_experimental_features(expe_feat_changes)
475
+ expe_feat_changes = Utils.transform_attributes(expe_feat_changes)
476
+ http_patch '/experimental-features', expe_feat_changes
477
+ end
478
+
170
479
  private
171
480
 
172
481
  def index_object(uid, primary_key = nil)
@@ -10,8 +10,9 @@ module Meilisearch
10
10
  attr_reader :options, :headers
11
11
 
12
12
  DEFAULT_OPTIONS = {
13
- timeout: 1,
14
- max_retries: 0,
13
+ timeout: 10,
14
+ max_retries: 2,
15
+ retry_multiplier: 1.2,
15
16
  convert_body?: true
16
17
  }.freeze
17
18
 
@@ -29,7 +30,8 @@ module Meilisearch
29
30
  config: {
30
31
  query_params: query_params,
31
32
  headers: remove_headers(@headers.dup.merge(options[:headers] || {}), 'Content-Type'),
32
- options: @options.merge(options)
33
+ options: @options.merge(options),
34
+ method_type: :get
33
35
  }
34
36
  )
35
37
  end
@@ -42,7 +44,8 @@ module Meilisearch
42
44
  query_params: query_params,
43
45
  body: body,
44
46
  headers: @headers.dup.merge(options[:headers] || {}),
45
- options: @options.merge(options)
47
+ options: @options.merge(options),
48
+ method_type: :post
46
49
  }
47
50
  )
48
51
  end
@@ -55,7 +58,8 @@ module Meilisearch
55
58
  query_params: query_params,
56
59
  body: body,
57
60
  headers: @headers.dup.merge(options[:headers] || {}),
58
- options: @options.merge(options)
61
+ options: @options.merge(options),
62
+ method_type: :put
59
63
  }
60
64
  )
61
65
  end
@@ -68,7 +72,8 @@ module Meilisearch
68
72
  query_params: query_params,
69
73
  body: body,
70
74
  headers: @headers.dup.merge(options[:headers] || {}),
71
- options: @options.merge(options)
75
+ options: @options.merge(options),
76
+ method_type: :patch
72
77
  }
73
78
  )
74
79
  end
@@ -80,7 +85,8 @@ module Meilisearch
80
85
  config: {
81
86
  query_params: query_params,
82
87
  headers: remove_headers(@headers.dup.merge(options[:headers] || {}), 'Content-Type'),
83
- options: @options.merge(options)
88
+ options: @options.merge(options),
89
+ method_type: :delete
84
90
  }
85
91
  )
86
92
  end
@@ -102,15 +108,23 @@ module Meilisearch
102
108
  data.delete_if { |k| keys.include?(k) }
103
109
  end
104
110
 
105
- def send_request(http_method, relative_path, config: {})
106
- config = http_config(config[:query_params], config[:body], config[:options], config[:headers])
111
+ def send_request(http_method, relative_path, config:)
112
+ attempts = 0
113
+ retry_multiplier = config.dig(:options, :retry_multiplier)
114
+ max_retries = config.dig(:options, :max_retries)
115
+ request_config = http_config(config[:query_params], config[:body], config[:options], config[:headers])
107
116
 
108
117
  begin
109
- response = http_method.call(@base_url + relative_path, config)
118
+ response = http_method.call(@base_url + relative_path, request_config)
110
119
  rescue Errno::ECONNREFUSED, Errno::EPIPE => e
111
120
  raise CommunicationError, e.message
112
- rescue Net::ReadTimeout, Net::OpenTimeout => e
113
- raise TimeoutError, e.message
121
+ rescue Net::OpenTimeout, Net::ReadTimeout => e
122
+ attempts += 1
123
+ raise TimeoutError, e.message unless attempts <= max_retries && safe_to_retry?(config[:method_type], e)
124
+
125
+ sleep(retry_multiplier**attempts)
126
+
127
+ retry
114
128
  end
115
129
 
116
130
  validate(response)
@@ -132,5 +146,10 @@ module Meilisearch
132
146
 
133
147
  response.parsed_response
134
148
  end
149
+
150
+ # Ensures the only retryable error is a timeout didn't reached the server
151
+ def safe_to_retry?(method_type, error)
152
+ method_type == :get || ([:post, :put, :patch, :delete].include?(method_type) && error.is_a?(Net::OpenTimeout))
153
+ end
135
154
  end
136
155
  end
@@ -5,6 +5,21 @@ require 'forwardable'
5
5
  module Meilisearch
6
6
  module Models
7
7
  class Task
8
+ DEFAULT_TIMEOUT_MS = 5000
9
+ DEFAULT_INTERVAL_MS = 50
10
+
11
+ class << self
12
+ attr_writer :default_timeout_ms, :default_interval_ms
13
+
14
+ def default_timeout_ms
15
+ @default_timeout_ms || DEFAULT_TIMEOUT_MS
16
+ end
17
+
18
+ def default_interval_ms
19
+ @default_interval_ms || DEFAULT_INTERVAL_MS
20
+ end
21
+ end
22
+
8
23
  extend Forwardable
9
24
 
10
25
  # Maintain backwards compatibility with task hash return type
@@ -96,7 +111,10 @@ module Meilisearch
96
111
  self
97
112
  end
98
113
 
99
- def await(timeout_in_ms = 5000, interval_in_ms = 50)
114
+ def await(
115
+ timeout_in_ms = self.class.default_timeout_ms,
116
+ interval_in_ms = self.class.default_interval_ms
117
+ )
100
118
  refresh with: @task_endpoint.wait_for_task(uid, timeout_in_ms, interval_in_ms) unless finished?
101
119
 
102
120
  self
@@ -36,7 +36,19 @@ module Meilisearch
36
36
  http_delete '/tasks', Utils.parse_query(options, ALLOWED_CANCELATION_PARAMS)
37
37
  end
38
38
 
39
- def wait_for_task(task_uid, timeout_in_ms = 5000, interval_in_ms = 50)
39
+ # Wait for a task with a busy loop.
40
+ #
41
+ # Not recommended, try to avoid interacting with Meilisearch synchronously.
42
+ # @param task_uid [String] uid of the task to wait on
43
+ # @param timeout_in_ms [Integer] the maximum amount of time to wait for a task
44
+ # in milliseconds
45
+ # @param interval_in_ms [Integer] how long to stay parked in the busy loop
46
+ # in milliseconds
47
+ def wait_for_task(
48
+ task_uid,
49
+ timeout_in_ms = Models::Task.default_timeout_ms,
50
+ interval_in_ms = Models::Task.default_interval_ms
51
+ )
40
52
  Timeout.timeout(timeout_in_ms.to_f / 1000) do
41
53
  loop do
42
54
  task = task(task_uid)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Meilisearch
4
- VERSION = '0.31.0'
4
+ VERSION = '0.32.0'
5
5
 
6
6
  def self.qualified_version
7
7
  "Meilisearch Ruby (v#{VERSION})"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: meilisearch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.31.0
4
+ version: 0.32.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Meili
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-02-05 00:00:00.000000000 Z
11
+ date: 2025-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty