diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index d99a2d5b..e199ec6e 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2025-03-31 12:39:31 UTC using RuboCop version 1.71.2. +# on 2025-04-02 09:32:03 UTC using RuboCop version 1.75.1. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -30,3 +30,16 @@ Metrics/ParameterLists: Naming/AccessorMethodName: Exclude: - 'lib/meilisearch/index.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +Style/IfUnlessModifier: + Exclude: + - 'lib/meilisearch/index.rb' + +# Offense count: 1 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. +# URISchemes: http, https +Layout/LineLength: + Max: 155 diff --git a/lib/meilisearch/client.rb b/lib/meilisearch/client.rb index cbce86ac..f410e639 100644 --- a/lib/meilisearch/client.rb +++ b/lib/meilisearch/client.rb @@ -1,18 +1,51 @@ # frozen_string_literal: true module Meilisearch + # Manages a connection to a Meilisearch server. + # client = Meilisearch::Client.new(MEILISEARCH_URL, MASTER_KEY, options) + # + # @see #indexes Managing search indexes + # @see #keys Managing API keys + # @see #stats View usage statistics + # @see #tasks Managing ongoing tasks + # @see #health Health checking + # @see #create_dump + # @see #create_snapshot class Client < HTTPRequest include Meilisearch::TenantToken include Meilisearch::MultiSearch ### INDEXES + # Fetch indexes in instance, returning the raw server response. + # + # Unless you have a good reason to, {#indexes} should be used instead. + # + # @see #indexes + # @see https://www.meilisearch.com/docs/reference/api/indexes#list-all-indexes Meilisearch API reference + # @param options [Hash] limit and offset options + # @return [Hash] + # {index response object}[https://www.meilisearch.com/docs/reference/api/indexes#response] def raw_indexes(options = {}) body = Utils.transform_attributes(options.transform_keys(&:to_sym).slice(:limit, :offset)) http_get('/indexes', body) end + # Swap two indexes. + # + # Can be used as a convenient way to rebuild an index while keeping it operational. + # client.index('a_swap').add_documents({}) + # client.swap_indexes(['a', 'a_swap']) + # + # Multiple swaps may be done with one request: + # client.swap_indexes(['a', 'a_swap'], ['b', 'b_swap']) + # + # @see https://www.meilisearch.com/docs/reference/api/indexes#swap-indexes Meilisearch API reference + # + # @param options [Array] the indexes to swap + # @return [Models::Task] the async task that swaps the indexes + # @raise [ApiError] def swap_indexes(*options) mapped_array = options.map { |arr| { indexes: arr } } @@ -20,6 +53,13 @@ def swap_indexes(*options) Models::Task.new(response, task_endpoint) end + # Fetch indexes in instance. + # + # @see https://www.meilisearch.com/docs/reference/api/indexes#list-all-indexes Meilisearch API reference + # @param options [Hash] limit and offset options + # @return [Hash] + # {index response object}[https://www.meilisearch.com/docs/reference/api/indexes#response] + # with results mapped to instances of {Index} def indexes(options = {}) response = raw_indexes(options) @@ -30,9 +70,23 @@ def indexes(options = {}) response end - # Usage: - # client.create_index('indexUID') - # client.create_index('indexUID', primary_key: 'id') + # Create a new empty index. + # + # client.create_index('indexUID') + # client.create_index('indexUID', primary_key: 'id') + # + # Indexes are also created when accessed: + # + # client.index('new_index').add_documents({}) + # + # @see #index + # @see https://www.meilisearch.com/docs/reference/api/indexes#create-an-index Meilisearch API reference + # + # @param index_uid [String] the uid of the new index + # @param options [Hash, nil] snake_cased options of {the endpoint}[https://www.meilisearch.com/docs/reference/api/indexes#create-an-index] + # + # @raise [ApiError] + # @return [Models::Task] the async task that creates the index def create_index(index_uid, options = {}) body = Utils.transform_attributes(options.merge(uid: index_uid)) @@ -41,8 +95,14 @@ def create_index(index_uid, options = {}) Models::Task.new(response, task_endpoint) end - # Synchronous version of create_index. - # Waits for the task to be achieved, be careful when using it. + # Synchronous version of {#create_index}. + # + # @deprecated + # use {Models::Task#await} on task returned from {#create_index} + # + # client.create_index('foo').await + # + # Waits for the task to be achieved with a busy loop, be careful when using it. def create_index!(index_uid, options = {}) Utils.soft_deprecate( 'Client#create_index!', @@ -52,42 +112,131 @@ def create_index!(index_uid, options = {}) create_index(index_uid, options).await end + # Delete an index. + # + # @param index_uid [String] the uid of the index to be deleted + # @return [Models::Task] the async task deleting the index def delete_index(index_uid) index_object(index_uid).delete end - # Usage: - # client.index('indexUID') + # Get index with given uid. + # + # Indexes that don't exist are lazily created by Meilisearch. + # index = client.index('index_uid') + # index.add_documents({}) # index is created here if it did not exist + # + # @see Index + # @param index_uid [String] the uid of the index to get + # @return [Index] def index(index_uid) index_object(index_uid) end + # Shorthand for + # client.index(index_uid).fetch_info + # + # @see Index#fetch_info + # @param index_uid [String] uid of the index def fetch_index(index_uid) index_object(index_uid).fetch_info end + # Shorthand for + # client.index(index_uid).fetch_raw_info + # + # @see Index#fetch_raw_info + # @param index_uid [String] uid of the index def fetch_raw_index(index_uid) index_object(index_uid).fetch_raw_info end ### KEYS + # Get all API keys + # + # This and other key methods require that the Meilisearch instance have a + # {master key}[https://www.meilisearch.com/docs/learn/security/differences_master_api_keys#master-key] + # set. + # + # @see #create_key #create_key to create keys and set their scope + # @see #key #key to fetch one key + # @see https://www.meilisearch.com/docs/reference/api/keys#get-all-keys Meilisearch API reference + # @param limit [String, Integer, nil] limit the number of returned keys + # @param offset [String, Integer, nil] skip the first +offset+ keys, + # useful for paging. + # + # @return [Hash] a {keys response}[https://www.meilisearch.com/docs/reference/api/keys#response] def keys(limit: nil, offset: nil) body = { limit: limit, offset: offset }.compact http_get '/keys', body end + # Get a specific API key. + # + # # obviously this example uid will not correspond to a key on your server + # # please replace it with your own key's uid + # uid = '6062abda-a5aa-4414-ac91-ecd7944c0f8d' + # client.key(uid) + # + # This and other key methods require that the Meilisearch instance have a + # {master key}[https://www.meilisearch.com/docs/learn/security/differences_master_api_keys#master-key] + # set. + # + # @see #keys #keys to get all keys in the instance + # @see #create_key #create_key to create keys and set their scope + # @see https://www.meilisearch.com/docs/reference/api/keys#get-one-key Meilisearch API reference + # @param uid_or_key [String] either the uuidv4 that is the key's + # {uid}[https://www.meilisearch.com/docs/reference/api/keys#uid] or + # a hash of the uid and the master key that is the key's + # {key}[https://www.meilisearch.com/docs/reference/api/keys#key] field + # + # @return [Hash] a {key object}[https://www.meilisearch.com/docs/reference/api/keys#key-object] def key(uid_or_key) http_get "/keys/#{uid_or_key}" end + # Create a new API key. + # + # require 'date_core' + # ten_days_later = (DateTime.now + 10).rfc3339 + # client.create_key(actions: ['*'], indexes: ['*'], expires_at: ten_days_later) + # + # This and other key methods require that the Meilisearch instance have a + # {master key}[https://www.meilisearch.com/docs/learn/security/differences_master_api_keys#master-key] + # set. + # + # @see #update_key #update_key to edit an existing key + # @see #keys #keys to get all keys in the instance + # @see #key #key to fetch one key + # @see https://www.meilisearch.com/docs/reference/api/keys#create-a-key Meilisearch API reference + # @param key_options [Hash] the key options of which the required are + # - +:actions+ +Array+ of API actions allowed for key, +["*"]+ for all + # - +:indexes+ +Array+ of indexes key can act on, +["*"]+ for all + # - +:expires_at+ expiration datetime in + # {RFC 3339}[https://www.ietf.org/rfc/rfc3339.txt] format, nil if the key never expires + # + # @return [Hash] a {key object}[https://www.meilisearch.com/docs/reference/api/keys#key-object] def create_key(key_options) body = Utils.transform_attributes(key_options) http_post '/keys', body end + # Update an existing API key. + # + # This and other key methods require that the Meilisearch instance have a + # {master key}[https://www.meilisearch.com/docs/learn/security/differences_master_api_keys#master-key] + # set. + # + # @see #create_key #create_key to create a new key + # @see #keys #keys to get all keys in the instance + # @see #key #key to fetch one key + # @see https://www.meilisearch.com/docs/reference/api/keys#update-a-key Meilisearch API reference + # @param key_options [Hash] see {#create_key} + # + # @return [Hash] a {key object}[https://www.meilisearch.com/docs/reference/api/keys#key-object] def update_key(uid_or_key, key_options) body = Utils.transform_attributes(key_options) body = body.slice('description', 'name') @@ -95,12 +244,34 @@ def update_key(uid_or_key, key_options) http_patch "/keys/#{uid_or_key}", body end + # Delete an API key. + # + # # obviously this example uid will not correspond to a key on your server + # # please replace it with your own key's uid + # uid = '6062abda-a5aa-4414-ac91-ecd7944c0f8d' + # client.delete_key(uid) + # + # This and other key methods require that the Meilisearch instance have a + # {master key}[https://www.meilisearch.com/docs/learn/security/differences_master_api_keys#master-key] + # set. + # + # @see #keys #keys to get all keys in the instance + # @see #create_key #create_key to create keys and set their scope + # @see https://www.meilisearch.com/docs/reference/api/keys#delete-a-key Meilisearch API reference + # @param uid_or_key [String] either the uuidv4 that is the key's + # {uid}[https://www.meilisearch.com/docs/reference/api/keys#uid] or + # a hash of the uid and the master key that is the key's + # {key}[https://www.meilisearch.com/docs/reference/api/keys#key] field def delete_key(uid_or_key) http_delete "/keys/#{uid_or_key}" end ### HEALTH + # Check if Meilisearch instance is healthy. + # + # @see #health + # @return [bool] whether or not the +/health+ endpoint raises any errors def healthy? http_get '/health' true @@ -108,22 +279,51 @@ def healthy? false end + # Check health of Meilisearch instance. + # + # @see https://www.meilisearch.com/docs/reference/api/health#get-health Meilisearch API reference + # @return [Hash] the health report from the Meilisearch instance def health http_get '/health' end ### STATS + # Check version of Meilisearch server + # + # @see https://www.meilisearch.com/docs/reference/api/version#get-version-of-meilisearch Meilisearch API reference + # @return [Hash] package version and last commit of Meilisearch server, see + # {version object}[https://www.meilisearch.com/docs/reference/api/version#version-object] def version http_get '/version' end + # Get stats of all indexes in instance. + # + # @see Index#stats + # @see https://www.meilisearch.com/docs/reference/api/stats#get-stats-of-all-indexes Meilisearch API reference + # @return [Hash] see {stats object}[https://www.meilisearch.com/docs/reference/api/stats#stats-object] def stats http_get '/stats' end ### DUMPS + # Create a database dump. + # + # Dumps are "blueprints" which can be used to restore your database. Restoring + # a dump requires reindexing all documents and is therefore inefficient. + # + # Dumps are created by the Meilisearch server in the directory where the server is started + # under +dumps/+ by default. + # + # @see https://www.meilisearch.com/docs/learn/advanced/snapshots_vs_dumps + # The difference between snapshots and dumps + # @see https://www.meilisearch.com/docs/learn/advanced/dumps + # Meilisearch documentation on how to use dumps + # @see https://www.meilisearch.com/docs/reference/api/dump#create-a-dump + # Meilisearch API reference + # @return [Models::Task] the async task that is creating the dump def create_dump response = http_post '/dumps' Models::Task.new(response, task_endpoint) @@ -131,28 +331,104 @@ def create_dump ### SNAPSHOTS + # Create a database snapshot. + # + # Snapshots are exact copies of the Meilisearch database. As such they are pre-indexed + # and restoring one is a very efficient operation. + # + # Snapshots are not compatible between Meilisearch versions. Snapshot creation takes priority + # over other tasks. + # + # Snapshots are created by the Meilisearch server in the directory where the server is started + # under +snapshots/+ by default. + # + # @see https://www.meilisearch.com/docs/learn/advanced/snapshots_vs_dumps + # The difference between snapshots and dumps + # @see https://www.meilisearch.com/docs/learn/advanced/snapshots + # Meilisearch documentation on how to use snapshots + # @see https://www.meilisearch.com/docs/reference/api/snapshots#create-a-snapshot + # Meilisearch API reference + # @return [Models::Task] the async task that is creating the snapshot def create_snapshot http_post '/snapshots' end ### TASKS + # Cancel tasks matching the filter. + # + # This route is meant to be used with options, please see the API reference. + # + # Operations in Meilisearch are done asynchronously using "tasks". + # Tasks report their progress and status. + # + # Warning: This does not return instances of {Models::Task}. This is a raw + # call to the Meilisearch API and the return is not modified. + # + # @see https://www.meilisearch.com/docs/reference/api/tasks#task-object The Task Object + # @see https://www.meilisearch.com/docs/reference/api/tasks#cancel-tasks Meilisearch API reference + # @param options [Hash] task search options as snake cased symbols, see the API reference + # @return [Hash] a Meilisearch task that is canceling other tasks def cancel_tasks(options = {}) task_endpoint.cancel_tasks(options) end + # Cancel tasks matching the filter. + # + # This route is meant to be used with options, please see the API reference. + # + # Operations in Meilisearch are done asynchronously using "tasks". + # Tasks report their progress and status. + # + # Warning: This does not return instances of {Models::Task}. This is a raw + # call to the Meilisearch API and the return is not modified. + # + # Tasks are run in batches, see {#batches}. + # + # @see https://www.meilisearch.com/docs/reference/api/tasks#task-object The Task Object + # @see https://www.meilisearch.com/docs/reference/api/tasks#cancel-tasks Meilisearch API reference + # @param options [Hash] task search options as snake cased symbols, see the API reference + # @return [Hash] a Meilisearch task that is canceling other tasks def delete_tasks(options = {}) task_endpoint.delete_tasks(options) end + # Get Meilisearch tasks matching the filters. + # + # Operations in Meilisearch are done asynchronously using "tasks". + # Tasks report their progress and status. + # + # Warning: This does not return instances of {Models::Task}. This is a raw + # call to the Meilisearch API and the return is not modified. + # + # @see https://www.meilisearch.com/docs/reference/api/tasks#task-object The Task Object + # @see https://www.meilisearch.com/docs/reference/api/tasks#get-tasks Meilisearch API reference + # @param options [Hash] task search options as snake cased symbols, see the API reference + # @return [Hash] results of the task search, see API reference def tasks(options = {}) task_endpoint.task_list(options) end + # Get one task. + # + # Operations in Meilisearch are done asynchronously using "tasks". + # Tasks report their progress and status. + # + # Warning: This does not return instances of {Models::Task}. This is a raw + # call to the Meilisearch API and the return is not modified. + # + # @see https://www.meilisearch.com/docs/reference/api/tasks#task-object The Task Object + # @see https://www.meilisearch.com/docs/reference/api/tasks#get-one-task Meilisearch API reference + # @param task_uid [String] uid of the requested task + # @return [Hash] a Meilisearch task object (see above) def task(task_uid) task_endpoint.task(task_uid) end + # Wait for a task in a busy loop. + # + # Try to avoid using it. Wrapper around {Task#wait_for_task}. + # @see Task#wait_for_task def wait_for_task( task_uid, timeout_in_ms = Models::Task.default_timeout_ms, @@ -163,10 +439,28 @@ def wait_for_task( ### BATCHES + # Get Meilisearch task batches matching the filters. + # + # Operations in Meilisearch are done asynchronously using "tasks". + # Tasks are run in batches. + # + # @see https://www.meilisearch.com/docs/reference/api/batches#batch-object The Batch Object + # @see https://www.meilisearch.com/docs/reference/api/batches#get-batches Meilisearch API reference + # @param options [Hash] task search options as snake cased symbols, see the API reference + # @return [Hash] results of the batches search, see API reference def batches(options = {}) http_get '/batches', options end + # Get a single Meilisearch task batch matching +batch_uid+. + # + # Operations in Meilisearch are done asynchronously using "tasks". + # Tasks are run in batches. + # + # @see https://www.meilisearch.com/docs/reference/api/batches#batch-object The Batch Object + # @see https://www.meilisearch.com/docs/reference/api/batches#get-one-batch Meilisearch API reference + # @param batch_uid [String] the uid of the request batch + # @return [Hash] a batch object, see above def batch(batch_uid) http_get "/batches/#{batch_uid}" end diff --git a/lib/meilisearch/task.rb b/lib/meilisearch/task.rb index cfdf6366..b9ec9892 100644 --- a/lib/meilisearch/task.rb +++ b/lib/meilisearch/task.rb @@ -36,6 +36,14 @@ def delete_tasks(options) http_delete '/tasks', Utils.parse_query(options, ALLOWED_CANCELATION_PARAMS) end + # Wait for a task with a busy loop. + # + # Not recommended, try to avoid interacting with Meilisearch synchronously. + # @param task_uid [String] uid of the task to wait on + # @param timeout_in_ms [Integer] the maximum amount of time to wait for a task + # in milliseconds + # @param interval_in_ms [Integer] how long to stay parked in the busy loop + # in milliseconds def wait_for_task( task_uid, timeout_in_ms = Models::Task.default_timeout_ms,