chore(search): support Elastic Cloud API keys (#15231)

# Pull Request Template

## Description

Adds API-key authorization support for Searchkick/OpenSearch so Elastic
Cloud deployments can configure advanced search with an Elastic API key
instead of embedding basic auth in the URL.

The initializer now accepts `OPENSEARCH_API_KEY` or
`ELASTICSEARCH_API_KEY` and forwards it as an `Authorization: ApiKey
...` header. `.env.example` also documents the
OpenSearch/Elasticsearch-compatible search variables.

Refs
https://linear.app/chatwoot/issue/CW-7511/populate-test-data-set-and-run-experiments

## Type of change

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality not to work as expected)
- [x] This change requires a documentation update

## How Has This Been Tested?

- `bundle exec ruby -c config/initializers/searchkick.rb`
- `bundle exec ruby -c spec/config/searchkick_spec.rb`
- `bundle exec rspec spec/config/searchkick_spec.rb`
- `bundle exec rubocop config/initializers/searchkick.rb
spec/config/searchkick_spec.rb`
- `git diff --check`

## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
This commit is contained in:
Sony Mathew
2026-07-29 16:42:44 +05:30
committed by GitHub
parent 039a311327
commit 8fcd32f442
3 changed files with 73 additions and 1 deletions

View File

@@ -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

View File

@@ -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 = {

View File

@@ -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