Skip to content
Snippets Groups Projects
Commit 8f44986a authored by Tim Diels's avatar Tim Diels Committed by Mohammad Fayoumi
Browse files

Issue #3522879 by tim-diels, mohammad-fayoumi: Support Elasticsearch Connector 8.x

parent e41c3cb6
Branches 2.0.x
Tags 2.0.0
1 merge request!1Provide support for elasticsearch connector 8.x
elastic_searchkit_proxy.search:
path: '/api/search/_msearch'
path: '/api/search/_search'
defaults:
_title: 'Elasticsearch connector'
_controller: '\Drupal\search_api_elasticsearchkit_proxy\Controller\ElasticSearchkitProxyController::search'
requirements:
_permission: 'access content'
methods: ['POST']
elastic_searchkit_proxy.msearch:
path: '/api/search/_msearch'
defaults:
_title: 'Elasticsearch connector'
_controller: '\Drupal\search_api_elasticsearchkit_proxy\Controller\ElasticSearchkitProxyController::msearch'
requirements:
_permission: 'access content'
methods: ['POST']
elastic_searchkit_proxy.elasticsearch_server_selection:
path: '/admin/config/search/elasticsearch-proxy-server'
......
......@@ -2,47 +2,38 @@
namespace Drupal\search_api_elasticsearchkit_proxy\Controller;
use Drupal\elasticsearch_connector\Entity\Cluster;
use Drupal\elasticsearch_connector\Plugin\search_api\backend\ElasticSearchBackend;
use Drupal\search_api\ServerInterface;
use Elastic\Elasticsearch\Client;
use Elastic\Elasticsearch\Response\Elasticsearch;
use Http\Promise\Promise;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\Controller\ControllerBase;
use Elasticsearch\ClientBuilder;
use Drupal\search_api\Entity\Server;
/**
* Class ElasticsearchProxyController.
*
* A controller for proxying Elasticsearch search requests through Drupal.
* This class is designed to be extensible and customizable by other modules.
* This class is designed to be extensible and customisable by other modules.
*/
class ElasticSearchkitProxyController extends ControllerBase {
/**
* Handles the Elasticsearch search request.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object containing the Elasticsearch query.
* Initialize the client.
*
* @return \Symfony\Component\HttpFoundation\JsonResponse
* The response object containing the Elasticsearch response or error.
* @throws \Drupal\search_api\SearchApiException
*/
public function search(Request $request) {
private function initialize(): JsonResponse|Client {
$server = $this->loadSearchApiServer();
if (!$server) {
if (!$server instanceof ServerInterface) {
return $this->handleServerNotFound();
}
$client = $this->initializeElasticsearchClient($server);
return $this->initializeElasticsearchClient($server);
try {
$body = $request->getContent();
$response = $this->performElasticsearchQuery($client, $body);
return $this->handleSuccessResponse($response);
}
catch (\Exception $e) {
return $this->handleErrorResponse($e);
}
}
/**
......@@ -51,7 +42,7 @@ class ElasticSearchkitProxyController extends ControllerBase {
* @return \Drupal\search_api\ServerInterface|null
* The search API server or null if not found.
*/
protected function loadSearchApiServer() {
protected function loadSearchApiServer(): ?ServerInterface {
$config = $this->config('search_api_elasticsearchkit_proxy.settings');
$server_id = $config->get('search_api_server');
return Server::load($server_id);
......@@ -63,7 +54,7 @@ class ElasticSearchkitProxyController extends ControllerBase {
* @return \Symfony\Component\HttpFoundation\JsonResponse
* The error response.
*/
protected function handleServerNotFound() {
protected function handleServerNotFound(): JsonResponse {
return new JsonResponse(['error' => 'Search API server not found.'], 404);
}
......@@ -73,67 +64,82 @@ class ElasticSearchkitProxyController extends ControllerBase {
* @param \Drupal\search_api\ServerInterface $server
* The search API server.
*
* @return \Elasticsearch\Client
* @return \Elastic\Elasticsearch\Client
* The Elasticsearch client.
*
* @throws \Drupal\search_api\SearchApiException
*/
protected function initializeElasticsearchClient($server) {
$cluster = $this->loadElasticsearchCluster($server);
$clusterOptions = $cluster->get('options');
$host = $cluster->get('url');
$clientBuilder = ClientBuilder::create()->setHosts([$host]);
if ($this->isBasicAuthentication($clusterOptions)) {
$username = $clusterOptions['username'];
$password = $clusterOptions['password'];
$clientBuilder->setBasicAuthentication($username, $password);
protected function initializeElasticsearchClient(ServerInterface $server): Client {
$serverBackend = $server->getBackend();
if (!$serverBackend instanceof ElasticSearchBackend) {
throw new \RuntimeException('Cannot connect to a Search API server that is not an ElasticSearchBackend.');
}
return $clientBuilder->build();
return $serverBackend->getClient();
}
/**
* Loads the Elasticsearch cluster entity.
* Handles the Elasticsearch search request.
*
* @param \Drupal\search_api\ServerInterface $server
* The search API server.
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object containing the Elasticsearch query.
*
* @return \Drupal\elasticsearch_connector\Entity\Cluster
* The Elasticsearch cluster entity.
* @return \Symfony\Component\HttpFoundation\JsonResponse
* The response object containing the Elasticsearch response or error.
*/
protected function loadElasticsearchCluster($server) {
$clusterName = $server->get('backend_config')['cluster_settings']['cluster'];
return Cluster::load($clusterName);
public function search(Request $request): JsonResponse {
$client = $this->initialize();
try {
$body = $request->getContent();
$response = $this->performElasticsearchQuery($client, $body);
return $this->handleSuccessResponse($response->asArray());
}
catch (\Exception $e) {
return $this->handleErrorResponse($e);
}
}
/**
* Checks if the cluster uses Basic Authentication.
* Handles the Elasticsearch msearch request.
*
* @param array $clusterOptions
* The cluster options.
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object containing the Elasticsearch query.
*
* @return bool
* TRUE if Basic Authentication is used, FALSE otherwise.
* @return \Symfony\Component\HttpFoundation\JsonResponse
* The response object containing the Elasticsearch response or error.
*/
protected function isBasicAuthentication(array $clusterOptions) {
return $clusterOptions['authentication_type'] === 'Basic'
&& isset($clusterOptions['username'])
&& isset($clusterOptions['password']);
public function msearch(Request $request): JsonResponse {
$client = $this->initialize();
try {
$body = $request->getContent();
$response = $this->performElasticsearchQuery($client, $body, 'msearch');
return $this->handleSuccessResponse($response->asArray());
}
catch (\Exception $e) {
return $this->handleErrorResponse($e);
}
}
/**
* Performs the Elasticsearch query.
*
* @param \Elasticsearch\Client $client
* @param \Elastic\Elasticsearch\Client $client
* The Elasticsearch client.
* @param string $body
* The request body containing the Elasticsearch query.
* @param string|null $type
* The type of search.
*
* @return array
* @return \Elastic\Elasticsearch\Response\Elasticsearch|\Http\Promise\Promise
* The Elasticsearch response.
*/
protected function performElasticsearchQuery($client, $body) {
return $client->msearch(['body' => $body]);
protected function performElasticsearchQuery(Client $client, string $body, ?string $type = NULL): Elasticsearch|Promise {
return match ($type) {
'msearch' => $client->msearch(['body' => $body]),
default => $client->search(['body' => $body]),
};
}
/**
......@@ -145,7 +151,7 @@ class ElasticSearchkitProxyController extends ControllerBase {
* @return \Symfony\Component\HttpFoundation\JsonResponse
* The JSON response containing the Elasticsearch results.
*/
protected function handleSuccessResponse(array $response) {
protected function handleSuccessResponse(array $response): JsonResponse {
$jsonResponse = new JsonResponse($response);
$jsonResponse->headers->set('Access-Control-Allow-Origin', '*');
$jsonResponse->headers->set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
......@@ -163,7 +169,7 @@ class ElasticSearchkitProxyController extends ControllerBase {
* @return \Symfony\Component\HttpFoundation\JsonResponse
* The error response containing the exception message.
*/
protected function handleErrorResponse(\Exception $e) {
protected function handleErrorResponse(\Exception $e): JsonResponse {
$errorResponse = new JsonResponse(['error' => $e->getMessage()], 500);
$errorResponse->headers->set('Access-Control-Allow-Origin', '*');
$errorResponse->headers->set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment