diff --git a/.env.example b/.env.example index 7445c4ea3..53597282e 100644 --- a/.env.example +++ b/.env.example @@ -59,6 +59,16 @@ REDIS_SENTINEL_MASTER_NAME= # ref: https://github.com/chatwoot/chatwoot/issues/2420 # REDIS_OPENSSL_VERIFY_MODE=none +# OpenSearch / Elasticsearch-compatible search config +# Set OPENSEARCH_URL to enable advanced search. Elastic Cloud API keys can be configured with either key name below. +# API key values must be provided without the ApiKey authorization scheme prefix. +# OPENSEARCH_URL= +# OPENSEARCH_API_KEY= +# ELASTICSEARCH_API_KEY= +# OPENSEARCH_AWS_ACCESS_KEY_ID= +# OPENSEARCH_AWS_SECRET_ACCESS_KEY= +# OPENSEARCH_AWS_REGION=us-east-1 + # Postgres Database config variables # You can leave POSTGRES_DATABASE blank. The default name of # the database in the production environment is chatwoot_production diff --git a/config/initializers/searchkick.rb b/config/initializers/searchkick.rb index e07f9022c..d1356fd78 100644 --- a/config/initializers/searchkick.rb +++ b/config/initializers/searchkick.rb @@ -1,9 +1,18 @@ Searchkick.queue_name = :async_database_migration if ENV.fetch('OPENSEARCH_URL', '').present? +api_key = ENV.fetch('OPENSEARCH_API_KEY', '').presence || ENV.fetch('ELASTICSEARCH_API_KEY', '').presence access_key_id = ENV.fetch('OPENSEARCH_AWS_ACCESS_KEY_ID', '') secret_access_key = ENV.fetch('OPENSEARCH_AWS_SECRET_ACCESS_KEY', '') -if access_key_id.present? && secret_access_key.present? +if api_key.present? + Searchkick.client_options = Searchkick.client_options.deep_merge( + transport_options: { + headers: { + 'Authorization' => "ApiKey #{api_key}" + } + } + ) +elsif access_key_id.present? && secret_access_key.present? region = ENV.fetch('OPENSEARCH_AWS_REGION', 'us-east-1') Searchkick.aws_credentials = { diff --git a/spec/config/searchkick_spec.rb b/spec/config/searchkick_spec.rb new file mode 100644 index 000000000..c4b71d0b1 --- /dev/null +++ b/spec/config/searchkick_spec.rb @@ -0,0 +1,53 @@ +require 'rails_helper' + +RSpec.describe Searchkick do + let(:initializer_path) { Rails.root.join('config/initializers/searchkick.rb') } + + around do |example| + original_client_options = described_class.client_options.deep_dup + original_queue_name = described_class.queue_name + original_aws_credentials = described_class.aws_credentials + original_client = described_class.instance_variable_get(:@client) + + example.run + ensure + described_class.client_options = original_client_options + described_class.queue_name = original_queue_name + described_class.instance_variable_set(:@aws_credentials, original_aws_credentials) + described_class.instance_variable_set(:@client, original_client) + end + + it 'configures API key authorization from OPENSEARCH_API_KEY' do + described_class.client_options = {} + + with_modified_env OPENSEARCH_API_KEY: 'opensearch-api-key', ELASTICSEARCH_API_KEY: nil, + OPENSEARCH_AWS_ACCESS_KEY_ID: nil, OPENSEARCH_AWS_SECRET_ACCESS_KEY: nil do + load initializer_path + end + + expect(described_class.client_options).to eq( + transport_options: { + headers: { + 'Authorization' => 'ApiKey opensearch-api-key' + } + } + ) + end + + it 'supports ELASTICSEARCH_API_KEY for Elastic Cloud credentials' do + described_class.client_options = {} + + with_modified_env OPENSEARCH_API_KEY: nil, ELASTICSEARCH_API_KEY: 'elastic-api-key', + OPENSEARCH_AWS_ACCESS_KEY_ID: nil, OPENSEARCH_AWS_SECRET_ACCESS_KEY: nil do + load initializer_path + end + + expect(described_class.client_options).to eq( + transport_options: { + headers: { + 'Authorization' => 'ApiKey elastic-api-key' + } + } + ) + end +end