meilisearch 0.31.0 → 0.33.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: e9ae113cf3a2e76decccea64b9bcd1a0402c56c22af08eecbc3ad4e022db482b
4
+ data.tar.gz: 6ee5da205a21e8343e4fcb64ffc51e9919feab370349a86b51efebde086f0195
5
5
  SHA512:
6
- metadata.gz: fbd5af58608cb342538232663cddc27d073af9aa3d598385c9a227ef615eb556b8c0f0f6195a977fe89ac542b4efd635aae14747a82b21a821c89272ab619b22
7
- data.tar.gz: 15caa179a6f58ff36d2ec4abf1d9b40f8477ba62cbfe49cf0f3944465ed1a2b2c8a5cdc69ab15f9ecc42ca525ce6b553adc17444727adf46d1161ec8b1732e1f
6
+ metadata.gz: 7594ebc7211f25bac52762dd9c0081e21979d604045c8e82d4e4ec0e1162eb826ff7c8fc1a27138e5487f2c0c64aba37c48c3a00c5e67f7d1c315e248333ae0c
7
+ data.tar.gz: 7898077e4b8aa6d3ae2e87e6238815f3f807143cde266e6b307a90490038e13dd8171d55b00d30de06392e540168aaf1c25d48e480caea49afc7c2d2c6fde54e
data/README.md CHANGED
@@ -21,7 +21,6 @@
21
21
  <img src="https://codecov.io/gh/meilisearch/meilisearch-ruby/branch/main/graph/badge.svg?token=9J7LRP11IR"/>
22
22
  </a>
23
23
  <a href="https://github.com/meilisearch/meilisearch-ruby/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-informational" alt="License"></a>
24
- <a href="https://ms-bors.herokuapp.com/repositories/6"><img src="https://bors.tech/images/badge_small.svg" alt="Bors enabled"></a>
25
24
  </p>
26
25
 
27
26
  <p align="center">⚡ The Meilisearch API client written for Ruby 💎</p>
@@ -1,25 +1,70 @@
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
+ # You can also pass hashes explicitly (renaming supported):
45
+ # client.swap_indexes({ indexes: ['a', 'a_swap'] }, { indexes: ['uid', 'uid_renamed'], rename: true })
46
+ #
47
+ # Renaming requires the target uid to be absent.
48
+ #
49
+ # @see https://www.meilisearch.com/docs/reference/api/indexes#swap-indexes Meilisearch API reference
50
+ #
51
+ # @param options [Array<Array(String, String)>, Array<Hash>] the index pairs to swap
52
+ # @return [Models::Task] the async task that swaps the indexes
53
+ # @raise [ApiError]
16
54
  def swap_indexes(*options)
17
- mapped_array = options.map { |arr| { indexes: arr } }
55
+ mapped_array = options.map { |entry| entry.is_a?(Hash) ? entry : { indexes: entry } }
18
56
 
19
57
  response = http_post '/swap-indexes', mapped_array
20
58
  Models::Task.new(response, task_endpoint)
21
59
  end
22
60
 
61
+ # Fetch indexes in instance.
62
+ #
63
+ # @see https://www.meilisearch.com/docs/reference/api/indexes#list-all-indexes Meilisearch API reference
64
+ # @param options [Hash{Symbol => Object}] limit and offset options
65
+ # @return [Hash{String => Object}]
66
+ # {index response object}[https://www.meilisearch.com/docs/reference/api/indexes#response]
67
+ # with results mapped to instances of {Index}
23
68
  def indexes(options = {})
24
69
  response = raw_indexes(options)
25
70
 
@@ -30,9 +75,23 @@ module Meilisearch
30
75
  response
31
76
  end
32
77
 
33
- # Usage:
34
- # client.create_index('indexUID')
35
- # client.create_index('indexUID', primary_key: 'id')
78
+ # Create a new empty index.
79
+ #
80
+ # client.create_index('indexUID')
81
+ # client.create_index('indexUID', primary_key: 'id')
82
+ #
83
+ # Indexes are also created when accessed:
84
+ #
85
+ # client.index('new_index').add_documents({})
86
+ #
87
+ # @see #index
88
+ # @see https://www.meilisearch.com/docs/reference/api/indexes#create-an-index Meilisearch API reference
89
+ #
90
+ # @param index_uid [String] the uid of the new index
91
+ # @param options [Hash{Symbol => Object}, nil] snake_cased options of {the endpoint}[https://www.meilisearch.com/docs/reference/api/indexes#create-an-index]
92
+ #
93
+ # @raise [ApiError]
94
+ # @return [Models::Task] the async task that creates the index
36
95
  def create_index(index_uid, options = {})
37
96
  body = Utils.transform_attributes(options.merge(uid: index_uid))
38
97
 
@@ -41,8 +100,14 @@ module Meilisearch
41
100
  Models::Task.new(response, task_endpoint)
42
101
  end
43
102
 
44
- # Synchronous version of create_index.
45
- # Waits for the task to be achieved, be careful when using it.
103
+ # Synchronous version of {#create_index}.
104
+ #
105
+ # @deprecated
106
+ # use {Models::Task#await} on task returned from {#create_index}
107
+ #
108
+ # client.create_index('foo').await
109
+ #
110
+ # Waits for the task to be achieved with a busy loop, be careful when using it.
46
111
  def create_index!(index_uid, options = {})
47
112
  Utils.soft_deprecate(
48
113
  'Client#create_index!',
@@ -52,42 +117,131 @@ module Meilisearch
52
117
  create_index(index_uid, options).await
53
118
  end
54
119
 
120
+ # Delete an index.
121
+ #
122
+ # @param index_uid [String] the uid of the index to be deleted
123
+ # @return [Models::Task] the async task deleting the index
55
124
  def delete_index(index_uid)
56
125
  index_object(index_uid).delete
57
126
  end
58
127
 
59
- # Usage:
60
- # client.index('indexUID')
128
+ # Get index with given uid.
129
+ #
130
+ # Indexes that don't exist are lazily created by Meilisearch.
131
+ # index = client.index('index_uid')
132
+ # index.add_documents({}) # index is created here if it did not exist
133
+ #
134
+ # @see Index
135
+ # @param index_uid [String] the uid of the index to get
136
+ # @return [Index]
61
137
  def index(index_uid)
62
138
  index_object(index_uid)
63
139
  end
64
140
 
141
+ # Shorthand for
142
+ # client.index(index_uid).fetch_info
143
+ #
144
+ # @see Index#fetch_info
145
+ # @param index_uid [String] uid of the index
65
146
  def fetch_index(index_uid)
66
147
  index_object(index_uid).fetch_info
67
148
  end
68
149
 
150
+ # Shorthand for
151
+ # client.index(index_uid).fetch_raw_info
152
+ #
153
+ # @see Index#fetch_raw_info
154
+ # @param index_uid [String] uid of the index
69
155
  def fetch_raw_index(index_uid)
70
156
  index_object(index_uid).fetch_raw_info
71
157
  end
72
158
 
73
159
  ### KEYS
74
160
 
161
+ # Get all API keys
162
+ #
163
+ # This and other key methods require that the Meilisearch instance have a
164
+ # {master key}[https://www.meilisearch.com/docs/learn/security/differences_master_api_keys#master-key]
165
+ # set.
166
+ #
167
+ # @see #create_key #create_key to create keys and set their scope
168
+ # @see #key #key to fetch one key
169
+ # @see https://www.meilisearch.com/docs/reference/api/keys#get-all-keys Meilisearch API reference
170
+ # @param limit [String, Integer, nil] limit the number of returned keys
171
+ # @param offset [String, Integer, nil] skip the first +offset+ keys,
172
+ # useful for paging.
173
+ #
174
+ # @return [Hash{String => Object}] a {keys response}[https://www.meilisearch.com/docs/reference/api/keys#response]
75
175
  def keys(limit: nil, offset: nil)
76
176
  body = { limit: limit, offset: offset }.compact
77
177
 
78
178
  http_get '/keys', body
79
179
  end
80
180
 
181
+ # Get a specific API key.
182
+ #
183
+ # # obviously this example uid will not correspond to a key on your server
184
+ # # please replace it with your own key's uid
185
+ # uid = '6062abda-a5aa-4414-ac91-ecd7944c0f8d'
186
+ # client.key(uid)
187
+ #
188
+ # This and other key methods require that the Meilisearch instance have a
189
+ # {master key}[https://www.meilisearch.com/docs/learn/security/differences_master_api_keys#master-key]
190
+ # set.
191
+ #
192
+ # @see #keys #keys to get all keys in the instance
193
+ # @see #create_key #create_key to create keys and set their scope
194
+ # @see https://www.meilisearch.com/docs/reference/api/keys#get-one-key Meilisearch API reference
195
+ # @param uid_or_key [String] either the uuidv4 that is the key's
196
+ # {uid}[https://www.meilisearch.com/docs/reference/api/keys#uid] or
197
+ # a hash of the uid and the master key that is the key's
198
+ # {key}[https://www.meilisearch.com/docs/reference/api/keys#key] field
199
+ #
200
+ # @return [Hash{String => Object}] a {key object}[https://www.meilisearch.com/docs/reference/api/keys#key-object]
81
201
  def key(uid_or_key)
82
202
  http_get "/keys/#{uid_or_key}"
83
203
  end
84
204
 
205
+ # Create a new API key.
206
+ #
207
+ # require 'date_core'
208
+ # ten_days_later = (DateTime.now + 10).rfc3339
209
+ # client.create_key(actions: ['*'], indexes: ['*'], expires_at: ten_days_later)
210
+ #
211
+ # This and other key methods require that the Meilisearch instance have a
212
+ # {master key}[https://www.meilisearch.com/docs/learn/security/differences_master_api_keys#master-key]
213
+ # set.
214
+ #
215
+ # @see #update_key #update_key to edit an existing key
216
+ # @see #keys #keys to get all keys in the instance
217
+ # @see #key #key to fetch one key
218
+ # @see https://www.meilisearch.com/docs/reference/api/keys#create-a-key Meilisearch API reference
219
+ # @param key_options [Hash{Symbol => Object}] the key options of which the required are
220
+ # - +:actions+ +Array+ of API actions allowed for key, +["*"]+ for all
221
+ # - +:indexes+ +Array+ of indexes key can act on, +["*"]+ for all
222
+ # - +:expires_at+ expiration datetime in
223
+ # {RFC 3339}[https://www.ietf.org/rfc/rfc3339.txt] format, nil if the key never expires
224
+ #
225
+ # @return [Hash{String => Object}] a {key object}[https://www.meilisearch.com/docs/reference/api/keys#key-object]
85
226
  def create_key(key_options)
86
227
  body = Utils.transform_attributes(key_options)
87
228
 
88
229
  http_post '/keys', body
89
230
  end
90
231
 
232
+ # Update an existing API key.
233
+ #
234
+ # This and other key methods require that the Meilisearch instance have a
235
+ # {master key}[https://www.meilisearch.com/docs/learn/security/differences_master_api_keys#master-key]
236
+ # set.
237
+ #
238
+ # @see #create_key #create_key to create a new key
239
+ # @see #keys #keys to get all keys in the instance
240
+ # @see #key #key to fetch one key
241
+ # @see https://www.meilisearch.com/docs/reference/api/keys#update-a-key Meilisearch API reference
242
+ # @param key_options [Hash{Symbol => Object}] see {#create_key}
243
+ #
244
+ # @return [Hash{String => Object}] a {key object}[https://www.meilisearch.com/docs/reference/api/keys#key-object]
91
245
  def update_key(uid_or_key, key_options)
92
246
  body = Utils.transform_attributes(key_options)
93
247
  body = body.slice('description', 'name')
@@ -95,12 +249,34 @@ module Meilisearch
95
249
  http_patch "/keys/#{uid_or_key}", body
96
250
  end
97
251
 
252
+ # Delete an API key.
253
+ #
254
+ # # obviously this example uid will not correspond to a key on your server
255
+ # # please replace it with your own key's uid
256
+ # uid = '6062abda-a5aa-4414-ac91-ecd7944c0f8d'
257
+ # client.delete_key(uid)
258
+ #
259
+ # This and other key methods require that the Meilisearch instance have a
260
+ # {master key}[https://www.meilisearch.com/docs/learn/security/differences_master_api_keys#master-key]
261
+ # set.
262
+ #
263
+ # @see #keys #keys to get all keys in the instance
264
+ # @see #create_key #create_key to create keys and set their scope
265
+ # @see https://www.meilisearch.com/docs/reference/api/keys#delete-a-key Meilisearch API reference
266
+ # @param uid_or_key [String] either the uuidv4 that is the key's
267
+ # {uid}[https://www.meilisearch.com/docs/reference/api/keys#uid] or
268
+ # a hash of the uid and the master key that is the key's
269
+ # {key}[https://www.meilisearch.com/docs/reference/api/keys#key] field
98
270
  def delete_key(uid_or_key)
99
271
  http_delete "/keys/#{uid_or_key}"
100
272
  end
101
273
 
102
274
  ### HEALTH
103
275
 
276
+ # Check if Meilisearch instance is healthy.
277
+ #
278
+ # @see #health
279
+ # @return [bool] whether or not the +/health+ endpoint raises any errors
104
280
  def healthy?
105
281
  http_get '/health'
106
282
  true
@@ -108,22 +284,51 @@ module Meilisearch
108
284
  false
109
285
  end
110
286
 
287
+ # Check health of Meilisearch instance.
288
+ #
289
+ # @see https://www.meilisearch.com/docs/reference/api/health#get-health Meilisearch API reference
290
+ # @return [Hash{String => Object}] the health report from the Meilisearch instance
111
291
  def health
112
292
  http_get '/health'
113
293
  end
114
294
 
115
295
  ### STATS
116
296
 
297
+ # Check version of Meilisearch server
298
+ #
299
+ # @see https://www.meilisearch.com/docs/reference/api/version#get-version-of-meilisearch Meilisearch API reference
300
+ # @return [Hash{String => String}] package version and last commit of Meilisearch server, see
301
+ # {version object}[https://www.meilisearch.com/docs/reference/api/version#version-object]
117
302
  def version
118
303
  http_get '/version'
119
304
  end
120
305
 
306
+ # Get stats of all indexes in instance.
307
+ #
308
+ # @see Index#stats
309
+ # @see https://www.meilisearch.com/docs/reference/api/stats#get-stats-of-all-indexes Meilisearch API reference
310
+ # @return [Hash{String => Object}] see {stats object}[https://www.meilisearch.com/docs/reference/api/stats#stats-object]
121
311
  def stats
122
312
  http_get '/stats'
123
313
  end
124
314
 
125
315
  ### DUMPS
126
316
 
317
+ # Create a database dump.
318
+ #
319
+ # Dumps are "blueprints" which can be used to restore your database. Restoring
320
+ # a dump requires reindexing all documents and is therefore inefficient.
321
+ #
322
+ # Dumps are created by the Meilisearch server in the directory where the server is started
323
+ # under +dumps/+ by default.
324
+ #
325
+ # @see https://www.meilisearch.com/docs/learn/advanced/snapshots_vs_dumps
326
+ # The difference between snapshots and dumps
327
+ # @see https://www.meilisearch.com/docs/learn/advanced/dumps
328
+ # Meilisearch documentation on how to use dumps
329
+ # @see https://www.meilisearch.com/docs/reference/api/dump#create-a-dump
330
+ # Meilisearch API reference
331
+ # @return [Models::Task] the async task that is creating the dump
127
332
  def create_dump
128
333
  response = http_post '/dumps'
129
334
  Models::Task.new(response, task_endpoint)
@@ -131,42 +336,151 @@ module Meilisearch
131
336
 
132
337
  ### SNAPSHOTS
133
338
 
339
+ # Create a database snapshot.
340
+ #
341
+ # Snapshots are exact copies of the Meilisearch database. As such they are pre-indexed
342
+ # and restoring one is a very efficient operation.
343
+ #
344
+ # Snapshots are not compatible between Meilisearch versions. Snapshot creation takes priority
345
+ # over other tasks.
346
+ #
347
+ # Snapshots are created by the Meilisearch server in the directory where the server is started
348
+ # under +snapshots/+ by default.
349
+ #
350
+ # @see https://www.meilisearch.com/docs/learn/advanced/snapshots_vs_dumps
351
+ # The difference between snapshots and dumps
352
+ # @see https://www.meilisearch.com/docs/learn/advanced/snapshots
353
+ # Meilisearch documentation on how to use snapshots
354
+ # @see https://www.meilisearch.com/docs/reference/api/snapshots#create-a-snapshot
355
+ # Meilisearch API reference
356
+ # @return [Models::Task] the async task that is creating the snapshot
134
357
  def create_snapshot
135
358
  http_post '/snapshots'
136
359
  end
137
360
 
138
361
  ### TASKS
139
362
 
363
+ # Cancel tasks matching the filter.
364
+ #
365
+ # This route is meant to be used with options, please see the API reference.
366
+ #
367
+ # Operations in Meilisearch are done asynchronously using "tasks".
368
+ # Tasks report their progress and status.
369
+ #
370
+ # Warning: This does not return instances of {Models::Task}. This is a raw
371
+ # call to the Meilisearch API and the return is not modified.
372
+ #
373
+ # @see https://www.meilisearch.com/docs/reference/api/tasks#task-object The Task Object
374
+ # @see https://www.meilisearch.com/docs/reference/api/tasks#cancel-tasks Meilisearch API reference
375
+ # @param options [Hash{Symbol => Object}] task search options as snake cased symbols, see the API reference
376
+ # @return [Hash{String => Object}] a Meilisearch task that is canceling other tasks
140
377
  def cancel_tasks(options = {})
141
378
  task_endpoint.cancel_tasks(options)
142
379
  end
143
380
 
381
+ # Cancel tasks matching the filter.
382
+ #
383
+ # This route is meant to be used with options, please see the API reference.
384
+ #
385
+ # Operations in Meilisearch are done asynchronously using "tasks".
386
+ # Tasks report their progress and status.
387
+ #
388
+ # Warning: This does not return instances of {Models::Task}. This is a raw
389
+ # call to the Meilisearch API and the return is not modified.
390
+ #
391
+ # Tasks are run in batches, see {#batches}.
392
+ #
393
+ # @see https://www.meilisearch.com/docs/reference/api/tasks#task-object The Task Object
394
+ # @see https://www.meilisearch.com/docs/reference/api/tasks#cancel-tasks Meilisearch API reference
395
+ # @param options [Hash{Symbol => Object}] task search options as snake cased symbols, see the API reference
396
+ # @return [Hash{String => Object}] a Meilisearch task that is canceling other tasks
144
397
  def delete_tasks(options = {})
145
398
  task_endpoint.delete_tasks(options)
146
399
  end
147
400
 
401
+ # Get Meilisearch tasks matching the filters.
402
+ #
403
+ # Operations in Meilisearch are done asynchronously using "tasks".
404
+ # Tasks report their progress and status.
405
+ #
406
+ # Warning: This does not return instances of {Models::Task}. This is a raw
407
+ # call to the Meilisearch API and the return is not modified.
408
+ #
409
+ # @see https://www.meilisearch.com/docs/reference/api/tasks#task-object The Task Object
410
+ # @see https://www.meilisearch.com/docs/reference/api/tasks#get-tasks Meilisearch API reference
411
+ # @param options [Hash{Symbol => Object}] task search options as snake cased symbols, see the API reference
412
+ # @return [Hash{String => Object}] results of the task search, see API reference
148
413
  def tasks(options = {})
149
414
  task_endpoint.task_list(options)
150
415
  end
151
416
 
417
+ # Get one task.
418
+ #
419
+ # Operations in Meilisearch are done asynchronously using "tasks".
420
+ # Tasks report their progress and status.
421
+ #
422
+ # Warning: This does not return instances of {Models::Task}. This is a raw
423
+ # call to the Meilisearch API and the return is not modified.
424
+ #
425
+ # @see https://www.meilisearch.com/docs/reference/api/tasks#task-object The Task Object
426
+ # @see https://www.meilisearch.com/docs/reference/api/tasks#get-one-task Meilisearch API reference
427
+ # @param task_uid [String] uid of the requested task
428
+ # @return [Hash{String => Object}] a Meilisearch task object (see above)
152
429
  def task(task_uid)
153
430
  task_endpoint.task(task_uid)
154
431
  end
155
432
 
156
- def wait_for_task(task_uid, timeout_in_ms = 5000, interval_in_ms = 50)
433
+ # Wait for a task in a busy loop.
434
+ #
435
+ # Try to avoid using it. Wrapper around {Task#wait_for_task}.
436
+ # @see Task#wait_for_task
437
+ def wait_for_task(
438
+ task_uid,
439
+ timeout_in_ms = Models::Task.default_timeout_ms,
440
+ interval_in_ms = Models::Task.default_interval_ms
441
+ )
157
442
  task_endpoint.wait_for_task(task_uid, timeout_in_ms, interval_in_ms)
158
443
  end
159
444
 
160
445
  ### BATCHES
161
446
 
447
+ # Get Meilisearch task batches matching the filters.
448
+ #
449
+ # Operations in Meilisearch are done asynchronously using "tasks".
450
+ # Tasks are run in batches.
451
+ #
452
+ # @see https://www.meilisearch.com/docs/reference/api/batches#batch-object The Batch Object
453
+ # @see https://www.meilisearch.com/docs/reference/api/batches#get-batches Meilisearch API reference
454
+ # @param options [Hash{Symbol => Object}] task search options as snake cased symbols, see the API reference
455
+ # @return [Hash{String => Object}] results of the batches search, see API reference
162
456
  def batches(options = {})
163
457
  http_get '/batches', options
164
458
  end
165
459
 
460
+ # Get a single Meilisearch task batch matching +batch_uid+.
461
+ #
462
+ # Operations in Meilisearch are done asynchronously using "tasks".
463
+ # Tasks are run in batches.
464
+ #
465
+ # @see https://www.meilisearch.com/docs/reference/api/batches#batch-object The Batch Object
466
+ # @see https://www.meilisearch.com/docs/reference/api/batches#get-one-batch Meilisearch API reference
467
+ # @param batch_uid [String] the uid of the request batch
468
+ # @return [Hash{String => Object}] a batch object, see above
166
469
  def batch(batch_uid)
167
470
  http_get "/batches/#{batch_uid}"
168
471
  end
169
472
 
473
+ ### EXPERIMENTAL FEATURES
474
+
475
+ def experimental_features
476
+ http_get '/experimental-features'
477
+ end
478
+
479
+ def update_experimental_features(expe_feat_changes)
480
+ expe_feat_changes = Utils.transform_attributes(expe_feat_changes)
481
+ http_patch '/experimental-features', expe_feat_changes
482
+ end
483
+
170
484
  private
171
485
 
172
486
  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,27 @@ 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 URI::InvalidURIError => e
122
+ raise CommunicationError, "Client URL missing scheme/protocol. Did you mean https://#{@base_url}" unless @base_url =~ %r{^\w+://}
123
+
124
+ raise CommunicationError, e
125
+ rescue Net::OpenTimeout, Net::ReadTimeout => e
126
+ attempts += 1
127
+ raise TimeoutError, e.message unless attempts <= max_retries && safe_to_retry?(config[:method_type], e)
128
+
129
+ sleep(retry_multiplier**attempts)
130
+
131
+ retry
114
132
  end
115
133
 
116
134
  validate(response)
@@ -132,5 +150,10 @@ module Meilisearch
132
150
 
133
151
  response.parsed_response
134
152
  end
153
+
154
+ # Ensures the only retryable error is a timeout didn't reached the server
155
+ def safe_to_retry?(method_type, error)
156
+ method_type == :get || ([:post, :put, :patch, :delete].include?(method_type) && error.is_a?(Net::OpenTimeout))
157
+ end
135
158
  end
136
159
  end