From ad5452ab8ee937b1a520c48e6f71a4cbc2926cbc Mon Sep 17 00:00:00 2001 From: pawelkeska Date: Wed, 17 Jan 2024 10:35:58 +0100 Subject: [PATCH] Up to elasticsearch 8 (#2181) This is a major (breaking) change to Elastica to make it compatible with Elasticsearch 8.0. More work and details will follow tracked in https://github.com/ruflin/Elastica/issues/2185 The goal of this PR is to get it to an alpha version. --- composer.json | 10 +- docker/docker-compose.es.yml | 4 +- docker/docker-compose.proxy.yml | 1 + docker/docker-compose.yml | 6 +- docker/php/Dockerfile | 2 + phpstan-baseline.neon | 15 - src/Bulk.php | 23 +- src/Bulk/ResponseSet.php | 3 - src/Client.php | 418 +++++++++++++---- src/ClientConfiguration.php | 30 +- src/Cluster.php | 13 +- src/Cluster/Health.php | 17 +- src/Cluster/Settings.php | 14 +- src/Connection.php | 104 +--- src/Exception/Connection/GuzzleException.php | 42 -- src/Exception/Connection/HttpException.php | 67 --- src/Exception/ConnectionException.php | 55 --- .../PartialShardFailureException.php | 26 - src/Exception/ResponseException.php | 54 --- src/Index.php | 443 +++++++++--------- src/Index/Recovery.php | 8 +- src/Index/Settings.php | 160 +++---- src/Index/Stats.php | 11 +- src/IndexTemplate.php | 75 ++- src/Mapping.php | 13 +- src/Multi/Search.php | 11 +- src/Node/Info.php | 24 +- src/Node/Stats.php | 24 +- src/Pipeline.php | 49 +- src/Reindex.php | 23 +- src/Request.php | 179 ------- src/Response.php | 49 -- src/ResponseConverter.php | 21 + src/Scroll.php | 44 +- src/Search.php | 49 +- src/SearchableInterface.php | 17 +- src/Snapshot.php | 177 ++++--- src/Status.php | 39 +- src/Task.php | 30 +- src/Transport/AbstractTransport.php | 133 ------ src/Transport/AwsAuthV4.php | 112 ----- src/Transport/Guzzle.php | 212 --------- src/Transport/Http.php | 235 ---------- src/Transport/Https.php | 18 - src/Transport/NullTransport.php | 90 ---- src/Util.php | 26 - tests/Aggregation/DateRangeTest.php | 6 +- tests/Aggregation/PercentilesTest.php | 13 +- tests/Base.php | 15 +- tests/Bulk/ResponseSetTest.php | 12 +- tests/BulkTest.php | 4 +- tests/ClientConfigurationTest.php | 49 +- tests/ClientFunctionalTest.php | 208 ++++---- tests/ClientTest.php | 86 +++- tests/Cluster/SettingsTest.php | 59 +-- tests/ClusterTest.php | 2 +- .../Strategy/CallbackStrategyTest.php | 2 +- tests/Connection/Strategy/RoundRobinTest.php | 117 ++++- tests/Connection/Strategy/SimpleTest.php | 117 ++++- tests/ConnectionTest.php | 43 +- tests/DocumentTest.php | 2 +- .../Connection/GuzzleExceptionTest.php | 18 - .../Connection/HttpExceptionTest.php | 12 - tests/Exception/ConnectionExceptionTest.php | 10 - .../PartialShardFailureExceptionTest.php | 69 --- tests/Exception/ResponseExceptionTest.php | 78 --- tests/Index/SettingsTest.php | 19 +- tests/IndexTemplateTest.php | 68 +-- tests/IndexTest.php | 34 +- tests/MappingTest.php | 10 +- tests/Multi/MultiBuilderTest.php | 2 + tests/Multi/SearchTest.php | 53 +-- tests/Node/InfoTest.php | 2 +- tests/NodeTest.php | 2 +- tests/PipelineTest.php | 8 +- tests/Query/MultiMatchTest.php | 15 +- tests/Query/QueryStringTest.php | 10 +- tests/Query/WildcardTest.php | 15 +- tests/ReindexTest.php | 15 +- tests/RequestTest.php | 99 ---- tests/ResponseFunctionalTest.php | 31 -- tests/SearchTest.php | 42 +- tests/SnapshotTest.php | 49 +- tests/StatusTest.php | 4 +- tests/Transport/AbstractTransportTest.php | 149 ------ tests/Transport/AwsAuthV4Test.php | 240 ---------- tests/Transport/DummyTransport.php | 15 - tests/Transport/GuzzleTest.php | 139 ------ tests/Transport/HttpTest.php | 214 --------- tests/Transport/NullTransportTest.php | 114 ----- tests/Transport/TransportBenchmarkTest.php | 246 ---------- tests/UtilTest.php | 50 +- 92 files changed, 1592 insertions(+), 4151 deletions(-) delete mode 100644 src/Exception/Connection/GuzzleException.php delete mode 100644 src/Exception/Connection/HttpException.php delete mode 100644 src/Exception/ConnectionException.php delete mode 100644 src/Exception/PartialShardFailureException.php delete mode 100644 src/Exception/ResponseException.php create mode 100644 src/ResponseConverter.php delete mode 100644 src/Transport/AbstractTransport.php delete mode 100644 src/Transport/AwsAuthV4.php delete mode 100755 src/Transport/Guzzle.php delete mode 100644 src/Transport/Http.php delete mode 100644 src/Transport/Https.php delete mode 100644 src/Transport/NullTransport.php delete mode 100644 tests/Exception/Connection/GuzzleExceptionTest.php delete mode 100644 tests/Exception/Connection/HttpExceptionTest.php delete mode 100644 tests/Exception/ConnectionExceptionTest.php delete mode 100644 tests/Exception/PartialShardFailureExceptionTest.php delete mode 100644 tests/Exception/ResponseExceptionTest.php delete mode 100644 tests/RequestTest.php delete mode 100644 tests/Transport/AbstractTransportTest.php delete mode 100644 tests/Transport/AwsAuthV4Test.php delete mode 100644 tests/Transport/DummyTransport.php delete mode 100644 tests/Transport/GuzzleTest.php delete mode 100644 tests/Transport/HttpTest.php delete mode 100644 tests/Transport/NullTransportTest.php delete mode 100644 tests/Transport/TransportBenchmarkTest.php diff --git a/composer.json b/composer.json index 7abd91a88c..dcd721e00f 100644 --- a/composer.json +++ b/composer.json @@ -17,13 +17,14 @@ "require": { "php": "~8.0.0 || ~8.1.0 || ~8.2.0", "ext-json": "*", - "elasticsearch/elasticsearch": "^7.10", + "elastic/transport": "^8.8", + "elasticsearch/elasticsearch": "^8.11", + "guzzlehttp/psr7": "^2.0", "nyholm/dsn": "^2.0.0", "psr/log": "^1.0 || ^2.0 || ^3.0", "symfony/deprecation-contracts": "^3.0" }, "require-dev": { - "aws/aws-sdk-php": "^3.155", "guzzlehttp/guzzle": "^7.2", "phpstan/phpstan": "^1.5", "phpstan/phpstan-phpunit": "^1.1", @@ -45,6 +46,11 @@ "Elastica\\Test\\": "tests/" } }, + "config": { + "allow-plugins": { + "php-http/discovery": true + } + }, "extra": { "branch-alias": { "dev-master": "7.0.x-dev" diff --git a/docker/docker-compose.es.yml b/docker/docker-compose.es.yml index 33598d255a..4340b036a8 100644 --- a/docker/docker-compose.es.yml +++ b/docker/docker-compose.es.yml @@ -2,7 +2,8 @@ version: '3.8' services: es01: - image: &image docker.elastic.co/elasticsearch/elasticsearch:${ES_VERSION:-7.15.2} + container_name: es01 + image: &image docker.elastic.co/elasticsearch/elasticsearch:${ES_VERSION:-8.11.1} command: &command > /bin/sh -c "(./bin/elasticsearch-plugin list | grep -q ingest-attachment || ./bin/elasticsearch-plugin install --batch ingest-attachment) && /usr/local/bin/docker-entrypoint.sh" environment: &environment @@ -28,6 +29,7 @@ services: networks: &networks - elastic es02: + container_name: es02 image: *image command: *command environment: diff --git a/docker/docker-compose.proxy.yml b/docker/docker-compose.proxy.yml index 8993a68db1..6d57fa269e 100644 --- a/docker/docker-compose.proxy.yml +++ b/docker/docker-compose.proxy.yml @@ -2,6 +2,7 @@ version: '3.4' services: proxy: + container_name: proxy image: nginx:1.17-alpine volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 2bf1395756..2f2e505ed5 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -6,7 +6,9 @@ version: '3.4' services: php: - build: php/ + container_name: php + build: + dockerfile: php/Dockerfile volumes: - ../:/var/www/html networks: @@ -15,6 +17,8 @@ services: - ES_HOST=es01 - PROXY_HOST=proxy - ES_VERSION=${ES_VERSION} + stdin_open: true + tty: true networks: elastic: diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile index f2a36730fb..5b092afa63 100644 --- a/docker/php/Dockerfile +++ b/docker/php/Dockerfile @@ -1,5 +1,7 @@ FROM php:8.1-alpine +WORKDIR /var/www/html + COPY --from=composer /usr/bin/composer /usr/bin/composer # Install requried packages for running Make and Phive diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 6c417f593d..274eb46148 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -20,16 +20,6 @@ parameters: count: 1 path: src/QueryBuilder.php - - - message: "#^Function GuzzleHttp\\\\Psr7\\\\modify_request not found\\.$#" - count: 1 - path: src/Transport/AwsAuthV4.php - - - - message: "#^Function GuzzleHttp\\\\Psr7\\\\stream_for not found\\.$#" - count: 1 - path: src/Transport/Guzzle.php - - message: "#^Parameter \\#1 \\$precision of method Elastica\\\\Aggregation\\\\GeohashGrid\\:\\:setPrecision\\(\\) expects int\\|string, float given\\.$#" count: 1 @@ -180,11 +170,6 @@ parameters: count: 1 path: tests/SearchTest.php - - - message: "#^Unreachable statement \\- code above always terminates\\.$#" - count: 1 - path: tests/SnapshotTest.php - - message: "#^Parameter \\#1 \\$suggestion of static method Elastica\\\\Suggest\\:\\:create\\(\\) expects Elastica\\\\Suggest\\|Elastica\\\\Suggest\\\\AbstractSuggest, Elastica\\\\Query\\\\BoolQuery given\\.$#" count: 1 diff --git a/src/Bulk.php b/src/Bulk.php index 28ec464571..313254d30f 100644 --- a/src/Bulk.php +++ b/src/Bulk.php @@ -2,16 +2,18 @@ namespace Elastica; +use Elastic\Elasticsearch\Exception\ClientResponseException; +use Elastic\Elasticsearch\Exception\MissingParameterException; +use Elastic\Elasticsearch\Exception\ServerResponseException; +use Elastic\Transport\Exception\NoNodeAvailableException; use Elastica\Bulk\Action; use Elastica\Bulk\Action\AbstractDocument as AbstractDocumentAction; use Elastica\Bulk\Response as BulkResponse; use Elastica\Bulk\ResponseSet; use Elastica\Exception\Bulk\ResponseException as BulkResponseException; use Elastica\Exception\ClientException; -use Elastica\Exception\ConnectionException; use Elastica\Exception\InvalidException; use Elastica\Exception\RequestEntityTooLargeException; -use Elastica\Exception\ResponseException; use Elastica\Script\AbstractScript; class Bulk @@ -277,15 +279,22 @@ public function toArray(): array } /** - * @throws ClientException - * @throws ConnectionException - * @throws ResponseException + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws BulkResponseException - * @throws InvalidException + * @throws ClientException */ public function send(): ResponseSet { - $response = $this->_client->request($this->getPath(), Request::POST, (string) $this, $this->_requestParams, Request::NDJSON_CONTENT_TYPE); + $params = ['body' => (string) $this]; + + if ($this->hasIndex()) { + $params['index'] = $this->getIndex(); + } + + $response = $this->_client->baseBulk(\array_merge($params, $this->_requestParams)); return $this->_processResponse($response); } diff --git a/src/Bulk/ResponseSet.php b/src/Bulk/ResponseSet.php index 7781550e86..5f2589494f 100644 --- a/src/Bulk/ResponseSet.php +++ b/src/Bulk/ResponseSet.php @@ -23,9 +23,6 @@ public function __construct(BaseResponse $response, array $bulkResponses) { parent::__construct($response->getData(), $response->getStatus()); - $this->setQueryTime($response->getQueryTime()); - $this->setTransferInfo($response->getTransferInfo()); - $this->_bulkResponses = $bulkResponses; } diff --git a/src/Client.php b/src/Client.php index 9d552a0ea5..5e4b12096e 100644 --- a/src/Client.php +++ b/src/Client.php @@ -2,19 +2,32 @@ namespace Elastica; +use Elastic\Elasticsearch\ClientInterface; +use Elastic\Elasticsearch\Exception\ClientResponseException; +use Elastic\Elasticsearch\Exception\HttpClientException; +use Elastic\Elasticsearch\Exception\MissingParameterException; +use Elastic\Elasticsearch\Exception\ServerResponseException; +use Elastic\Elasticsearch\Response\Elasticsearch; +use Elastic\Elasticsearch\Traits\ClientEndpointsTrait; +use Elastic\Elasticsearch\Traits\EndpointTrait; +use Elastic\Elasticsearch\Traits\NamespaceTrait; +use Elastic\Elasticsearch\Transport\Adapter\AdapterInterface; +use Elastic\Elasticsearch\Transport\Adapter\AdapterOptions; +use Elastic\Transport\Exception\NoAsyncClientException; +use Elastic\Transport\Exception\NoNodeAvailableException; +use Elastic\Transport\Transport; +use Elastic\Transport\TransportBuilder; use Elastica\Bulk\Action; use Elastica\Bulk\ResponseSet; use Elastica\Exception\Bulk\ResponseException as BulkResponseException; use Elastica\Exception\ClientException; -use Elastica\Exception\ConnectionException; use Elastica\Exception\InvalidException; -use Elastica\Exception\ResponseException; use Elastica\Script\AbstractScript; -use Elasticsearch\Endpoints\AbstractEndpoint; -use Elasticsearch\Endpoints\ClosePointInTime; -use Elasticsearch\Endpoints\Indices\ForceMerge; -use Elasticsearch\Endpoints\Indices\Refresh; -use Elasticsearch\Endpoints\Update; +use GuzzleHttp\Psr7\Uri; +use Http\Promise\Promise; +use Psr\Http\Client\ClientInterface as HttpClientInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; @@ -23,8 +36,17 @@ * * @author Nicolas Ruflin */ -class Client +class Client implements ClientInterface { + use EndpointTrait; + use NamespaceTrait; + use ClientEndpointsTrait { + closePointInTime as protected elasticClientClosePointInTime; + bulk as protected elasticClientBulk; + } + + private bool $elasticMetaHeader = true; + /** * @var ClientConfiguration */ @@ -41,12 +63,12 @@ class Client protected $_connectionPool; /** - * @var Request|null + * @var RequestInterface|null */ protected $_lastRequest; /** - * @var Response|null + * @var Elasticsearch|null */ protected $_lastResponse; @@ -60,6 +82,11 @@ class Client */ protected $_version; + /** + * The endpoint namespace storage. + */ + protected array $namespace; + /** * Creates a new Elastica client. * @@ -85,12 +112,80 @@ public function __construct($config = [], ?callable $callback = null, ?LoggerInt $this->_initConnections(); } + /** + * {@inheritdoc} + */ + public function getLogger(): LoggerInterface + { + return $this->_logger; + } + + /** + * {@inheritdoc} + */ + public function getTransport(): Transport + { + throw new \Exception('Not supported'); + } + + /** + * {@inheritdoc} + */ + public function setAsync(bool $async): self + { + throw new \Exception('Not supported'); + } + + /** + * {@inheritdoc} + */ + public function getAsync(): bool + { + throw new \Exception('Not supported'); + } + + /** + * {@inheritdoc} + */ + public function setElasticMetaHeader(bool $active): self + { + $this->elasticMetaHeader = $active; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getElasticMetaHeader(): bool + { + return $this->elasticMetaHeader; + } + + /** + * {@inheritdoc} + */ + public function setResponseException(bool $active): self + { + throw new \Exception('Not supported'); + } + + /** + * {@inheritdoc} + */ + public function getResponseException(): bool + { + throw new \Exception('Not supported'); + } + /** * Get current version. * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function getVersion(): string { @@ -98,7 +193,7 @@ public function getVersion(): string return $this->_version; } - $data = $this->request('/')->getData(); + $data = $this->info()->asArray(); return $this->_version = $data['version']['number']; } @@ -208,11 +303,13 @@ public function removeHeader(string $header): self * * @param array|Document[] $docs Array of Elastica\Document * - * @throws InvalidException If docs is empty - * @throws ClientException - * @throws ConnectionException - * @throws ResponseException + * @throws InvalidException If docs is empty + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws BulkResponseException + * @throws ClientException */ public function updateDocuments(array $docs, array $requestParams = []): ResponseSet { @@ -241,11 +338,13 @@ public function updateDocuments(array $docs, array $requestParams = []): Respons * * @param array|Document[] $docs Array of Elastica\Document * - * @throws InvalidException If docs is empty - * @throws ClientException - * @throws ConnectionException - * @throws ResponseException + * @throws InvalidException If docs is empty + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws BulkResponseException + * @throws ClientException */ public function addDocuments(array $docs, array $requestParams = []): ResponseSet { @@ -274,15 +373,18 @@ public function addDocuments(array $docs, array $requestParams = []): ResponseSe * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function updateDocument($id, $data, $index, array $options = []): Response { - $endpoint = new Update(); - $endpoint->setId($id); - $endpoint->setIndex($index); + $params = [ + 'id' => $id, + 'index' => $index, + ]; if ($data instanceof AbstractScript) { $requestData = $data->toArray(); @@ -317,19 +419,18 @@ public function updateDocument($id, $data, $index, array $options = []): Respons } } - $endpoint->setBody($requestData); - $endpoint->setParams($options); + $params['body'] = $requestData; - $response = $this->requestEndpoint($endpoint); + $response = $this->update(\array_merge($params, $options)); - if ($response->isOk() + if ($response->getStatusCode() >= 200 && $response->getStatusCode() < 300 && $data instanceof Document && ($data->isAutoPopulate() || $this->getConfigValue(['document', 'autoPopulate'], false)) ) { - $data->setVersionParams($response->getData()); + $data->setVersionParams($response->asArray()); } - return $response; + return $this->toElasticaResponse($response); } /** @@ -338,10 +439,12 @@ public function updateDocument($id, $data, $index, array $options = []): Respons * @param array|Document[] $docs * * @throws InvalidException - * @throws ClientException - * @throws ConnectionException - * @throws ResponseException + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws BulkResponseException + * @throws ClientException */ public function deleteDocuments(array $docs, array $requestParams = []): ResponseSet { @@ -455,10 +558,12 @@ public function setConnections(array $connections) * @param bool|string $routing Optional routing key for all ids * * @throws InvalidException - * @throws ClientException - * @throws ConnectionException - * @throws ResponseException + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws BulkResponseException + * @throws ClientException */ public function deleteIds(array $ids, $index, $routing = false): ResponseSet { @@ -501,11 +606,13 @@ public function deleteIds(array $ids, $index, $routing = false): ResponseSet * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html * - * @throws ResponseException * @throws InvalidException - * @throws ClientException - * @throws ConnectionException + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws BulkResponseException + * @throws ClientException */ public function bulk(array $params): ResponseSet { @@ -520,34 +627,32 @@ public function bulk(array $params): ResponseSet return $bulk->send(); } - /** - * Makes calls to the elasticsearch server based on this index. - * - * It's possible to make any REST query directly over this method - * - * @param string $path Path to call - * @param string $method Rest method to use (GET, POST, DELETE, PUT) - * @param array|string $data OPTIONAL Arguments as array or pre-encoded string - * @param array $query OPTIONAL Query params - * @param string $contentType Content-Type sent with this request - * - * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - */ - public function request(string $path, string $method = Request::GET, $data = [], array $query = [], string $contentType = Request::DEFAULT_CONTENT_TYPE): Response + public function baseBulk(array $params) + { + return $this->toElasticaResponse($this->elasticClientBulk($params)); + } + + public function sendRequest(RequestInterface $sentRequest): Elasticsearch|Promise { $connection = $this->getConnection(); - $request = $this->_lastRequest = new Request($path, $method, $data, $query, $connection, $contentType); + $transport = $connection->getTransportObject(); + + $this->_lastRequest = $sentRequest; $this->_lastResponse = null; try { - $response = $this->_lastResponse = $request->send(); - } catch (ConnectionException $e) { + $response = $transport->sendRequest($sentRequest); + + $result = new Elasticsearch(); + $result->setResponse($response, 'HEAD' === $sentRequest->getMethod() ? false : true); + + $this->_lastResponse = $result; + } catch (ServerResponseException|NoNodeAvailableException $e) { $this->_connectionPool->onFail($connection, $e, $this); $this->_logger->error('Elastica Request Failure', [ 'exception' => $e, - 'request' => $e->getRequest()->toArray(), + 'request' => $sentRequest, + 'request_content' => \json_decode($sentRequest->getBody()->__toString(), true), 'retry' => $this->hasConnection(), ]); @@ -556,33 +661,16 @@ public function request(string $path, string $method = Request::GET, $data = [], throw $e; } - return $this->request($path, $method, $data, $query); + return $this->sendRequest($sentRequest); } $this->_logger->debug('Elastica Request', [ - 'request' => $request->toArray(), - 'response' => $response->getData(), - 'responseStatus' => $response->getStatus(), + 'request' => \json_decode($sentRequest->getBody()->__toString(), true), + 'response' => 'HEAD' !== $sentRequest->getMethod() ? $result->asArray() : $result->asString(), + 'responseStatus' => $response->getStatusCode(), ]); - return $response; - } - - /** - * Makes calls to the elasticsearch server with usage official client Endpoint. - * - * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - */ - public function requestEndpoint(AbstractEndpoint $endpoint): Response - { - return $this->request( - \ltrim($endpoint->getURI(), '/'), - $endpoint->getMethod(), - $endpoint->getBody() ?? [], - $endpoint->getParams() - ); + return $result; } /** @@ -592,16 +680,15 @@ public function requestEndpoint(AbstractEndpoint $endpoint): Response * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-forcemerge.html * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function forcemergeAll($args = []): Response { - $endpoint = new ForceMerge(); - $endpoint->setParams($args); - - return $this->requestEndpoint($endpoint); + return $this->toElasticaResponse($this->indices()->forcemerge($args)); } /** @@ -609,16 +696,14 @@ public function forcemergeAll($args = []): Response * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/point-in-time-api.html#close-point-in-time-api * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function closePointInTime(string $pointInTimeId): Response { - $endpoint = new ClosePointInTime(); - $endpoint->setBody(['id' => $pointInTimeId]); - - return $this->requestEndpoint($endpoint); + return $this->toElasticaResponse($this->elasticClientClosePointInTime(['body' => ['id' => $pointInTimeId]])); } /** @@ -626,21 +711,22 @@ public function closePointInTime(string $pointInTimeId): Response * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-refresh.html * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function refreshAll(): Response { - return $this->requestEndpoint(new Refresh()); + return $this->toElasticaResponse($this->indices()->refresh()); } - public function getLastRequest(): ?Request + public function getLastRequest(): ?RequestInterface { return $this->_lastRequest; } - public function getLastResponse(): ?Response + public function getLastResponse(): ?Elasticsearch { return $this->_lastResponse; } @@ -657,6 +743,11 @@ public function setLogger(LoggerInterface $logger) return $this; } + public function toElasticaResponse(Elasticsearch|ResponseInterface $elasticsearchResponse): Response + { + return ResponseConverter::toElastica($elasticsearchResponse); + } + /** * Inits the client connections. */ @@ -708,6 +799,133 @@ protected function _prepareConnectionParams(array $config): array } } + $params['transport'] = $this->_buildTransport($config); + return $params; } + + protected function _buildTransport(array $config): Transport + { + $transportConfig = $config['transport_config'] ?? []; + $hosts = []; + + if (isset($config['url'])) { + $hosts = [$config['url']]; + } else { + if (isset($config['hosts'])) { + $hosts = $config['hosts']; + } else { + $hosts = [(string) Uri::fromParts([ + 'scheme' => $config['schema'] ?? 'http', + 'host' => $config['host'] ?? Connection::DEFAULT_HOST, + 'port' => $config['port'] ?? Connection::DEFAULT_PORT, + 'path' => isset($config['path']) ? \ltrim($config['path'], '/') : '', + ]), + ]; + } + } + + // Transport builder + $builder = TransportBuilder::create(); + + $builder->setHosts($hosts); + + // Logger + if (null !== $this->_logger) { + $builder->setLogger($this->_logger); + } + + // Http client + if (isset($transportConfig['http_client'])) { + $builder->setClient($config['http_client']); + } + + // Set HTTP client options + $builder->setClient( + $this->setTransportClientOptions( + $builder->getClient(), + $transportConfig['http_client_config'] ?? [], + $transportConfig['http_client_options'] ?? [] + ) + ); + + // Cloud id + if (isset($config['cloud_id'])) { + $builder->setCloudId($config['cloud_id']); + } + + // Node Pool + if (isset($transportConfig['node_pool'])) { + $builder->setNodePool($config['node_pool']); + } + + $transport = $builder->build(); + + // The default retries is equal to the number of hosts + if (isset($config['retries']) && (int) $config['retries'] > 0) { + $transport->setRetries($config['retries']); + } else { + $transport->setRetries(\count($hosts)); + } + + // Basic authentication + if (isset($config['username'], $config['password'])) { + $transport->setUserInfo($config['username'], $config['password']); + } + + // API key + if (isset($config['api_key']) && !empty($config['api_key'])) { + if (isset($config['username']) && !empty($config['username'])) { + throw new InvalidException('You cannot use APIKey and Basic Authenication together'); + } + $transport->setHeader('Authorization', \sprintf('ApiKey %s', $config['api_key'])); + } + + /* + * Elastic cloud optimized with gzip + * @see https://github.com/elastic/elasticsearch-php/issues/1241 omit for Symfony HTTP Client + */ + if (isset($config['cloud_id']) && !$this->isSymfonyHttpClient($transport)) { + $transport->setHeader('Accept-Encoding', 'gzip'); + } + + return $transport; + } + + /** + * Returns true if the transport HTTP client is Symfony. + */ + protected function isSymfonyHttpClient(Transport $transport): bool + { + if (false !== \strpos(\get_class($transport->getClient()), 'Symfony\Component\HttpClient')) { + return true; + } + try { + if (false !== \strpos(\get_class($transport->getAsyncClient()), 'Symfony\Component\HttpClient')) { + return true; + } + } catch (NoAsyncClientException $e) { + return false; + } + + return false; + } + + protected function setTransportClientOptions(HttpClientInterface $client, array $config, array $clientOptions = []): HttpClientInterface + { + if (empty($config) && empty($clientOptions)) { + return $client; + } + $class = \get_class($client); + if (!isset(AdapterOptions::HTTP_ADAPTERS[$class])) { + throw new HttpClientException(\sprintf('The HTTP client %s is not supported for custom options', $class)); + } + $adapterClass = AdapterOptions::HTTP_ADAPTERS[$class]; + if (!\class_exists($adapterClass) || !\in_array(AdapterInterface::class, \class_implements($adapterClass))) { + throw new HttpClientException(\sprintf('The class %s does not exists or does not implement %s', $adapterClass, AdapterInterface::class)); + } + $adapter = new $adapterClass(); + + return $adapter->setConfig($client, $config, $clientOptions); + } } diff --git a/src/ClientConfiguration.php b/src/ClientConfiguration.php index e777492dad..61020e5d05 100644 --- a/src/ClientConfiguration.php +++ b/src/ClientConfiguration.php @@ -21,24 +21,30 @@ class ClientConfiguration * retryOnConflict: Use in \Elastica\Client::updateDocument * bigintConversion: Set to true to enable the JSON bigint to string conversion option (see issue #717) * - * @var array + * @var array{ + * host: string|null, + * port: string|null, + * path: string|null, + * url:string|null, + * connections: array, + * roundRobin: bool, + * retryOnConflict: int, + * username: string|null, + * password: string|null, + * transport_config: array, + * } */ protected $configuration = [ 'host' => null, 'port' => null, 'path' => null, 'url' => null, - 'proxy' => null, - 'transport' => null, - 'persistent' => true, - 'timeout' => null, - 'connections' => [], // host, port, path, transport, compression, persistent, timeout, username, password, auth_type, config -> (curl, headers, url) + 'connections' => [], // host, port, path, transport_config -> [http_client, http_client_config, http_client_options, node_pool], username, password 'roundRobin' => false, 'retryOnConflict' => 0, - 'bigintConversion' => false, 'username' => null, 'password' => null, - 'auth_type' => null, // basic, digest, gssnegotiate, ntlm + 'transport_config' => [], // http_client, http_client_config, http_client_options, node_pool ]; /** @@ -169,10 +175,6 @@ private static function parseDsn(Url $dsn): array { $data = ['host' => $dsn->getHost()]; - if (null !== $dsn->getScheme()) { - $data['transport'] = $dsn->getScheme(); - } - if (null !== $dsn->getUser()) { $data['username'] = $dsn->getUser(); } @@ -181,10 +183,6 @@ private static function parseDsn(Url $dsn): array $data['password'] = $dsn->getPassword(); } - if (null !== $dsn->getUser() && null !== $dsn->getPassword()) { - $data['auth_type'] = 'basic'; - } - if (null !== $dsn->getPort()) { $data['port'] = $dsn->getPort(); } diff --git a/src/Cluster.php b/src/Cluster.php index d36cf147b4..8b79780a88 100644 --- a/src/Cluster.php +++ b/src/Cluster.php @@ -2,12 +2,12 @@ namespace Elastica; +use Elastic\Elasticsearch\Exception\ClientResponseException; +use Elastic\Elasticsearch\Exception\ServerResponseException; +use Elastic\Transport\Exception\NoNodeAvailableException; use Elastica\Cluster\Health; use Elastica\Cluster\Settings; use Elastica\Exception\ClientException; -use Elastica\Exception\ConnectionException; -use Elastica\Exception\ResponseException; -use Elasticsearch\Endpoints\Cluster\State; /** * Cluster information for elasticsearch. @@ -51,13 +51,14 @@ public function __construct(Client $client) /** * Refreshes all cluster information (state). * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function refresh(): void { - $this->_response = $this->_client->requestEndpoint(new State()); + $this->_response = $this->_client->toElasticaResponse($this->_client->cluster()->state()); $this->_data = $this->getResponse()->getData(); } diff --git a/src/Cluster/Health.php b/src/Cluster/Health.php index 244cf5fc7f..fa6e067f15 100644 --- a/src/Cluster/Health.php +++ b/src/Cluster/Health.php @@ -2,11 +2,12 @@ namespace Elastica\Cluster; +use Elastic\Elasticsearch\Exception\ClientResponseException; +use Elastic\Elasticsearch\Exception\ServerResponseException; +use Elastic\Transport\Exception\NoNodeAvailableException; use Elastica\Client; use Elastica\Cluster\Health\Index; use Elastica\Exception\ClientException; -use Elastica\Exception\ConnectionException; -use Elastica\Exception\ResponseException; /** * Elastic cluster health. @@ -179,17 +180,15 @@ public function getIndices(): array /** * Retrieves the health data from the cluster. * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ protected function _retrieveHealthData(): array { - $endpoint = new \Elasticsearch\Endpoints\Cluster\Health(); - $endpoint->setParams(['level' => 'shards']); + $response = $this->_client->cluster()->health(['level' => 'shards']); - $response = $this->_client->requestEndpoint($endpoint); - - return $response->getData(); + return $response->asArray(); } } diff --git a/src/Cluster/Settings.php b/src/Cluster/Settings.php index 73c924818e..b733b3ab9f 100644 --- a/src/Cluster/Settings.php +++ b/src/Cluster/Settings.php @@ -3,7 +3,6 @@ namespace Elastica\Cluster; use Elastica\Client; -use Elastica\Request; use Elastica\Response; /** @@ -37,7 +36,7 @@ public function __construct(Client $client) */ public function get(): array { - return $this->request()->getData(); + return $this->_client->cluster()->getSettings()->asArray(); } /** @@ -157,7 +156,9 @@ public function setReadOnly(bool $readOnly = true, bool $persistent = false): Re */ public function set(array $settings): Response { - return $this->request($settings, Request::PUT); + return $this->_client->toElasticaResponse( + $this->_client->cluster()->putSettings(['body' => $settings]) + ); } /** @@ -167,11 +168,4 @@ public function getClient(): Client { return $this->_client; } - - public function request(array $data = [], string $method = Request::GET): Response - { - $path = '_cluster/settings'; - - return $this->getClient()->request($path, $method, $data); - } } diff --git a/src/Connection.php b/src/Connection.php index 4c0a8d396a..8c243d9ec0 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -2,8 +2,8 @@ namespace Elastica; +use Elastic\Transport\Transport; use Elastica\Exception\InvalidException; -use Elastica\Transport\AbstractTransport; /** * Elastica connection instance to an elasticasearch node. @@ -36,23 +36,10 @@ class Connection extends Param */ public const DEFAULT_COMPRESSION = false; - /** - * Number of seconds after a timeout occurs for every request - * If using indexing of file large value necessary. - */ - public const TIMEOUT = 300; - - /** - * Number of seconds after a connection timeout occurs for every request during the connection phase. - * - * @see Connection::setConnectTimeout(); - */ - public const CONNECT_TIMEOUT = 0; - /** * Creates a new connection object. A connection is enabled by default. * - * @param array $params OPTIONAL Connection params: host, port, transport, timeout. All are optional + * @param array $params OPTIONAL Connection params: host, port. All are optional */ public function __construct(array $params = []) { @@ -101,43 +88,15 @@ public function setHost($host) return $this->setParam('host', $host); } - /** - * @return string|null Host - */ - public function getProxy() + public function getTransport(): Transport { - return $this->hasParam('proxy') ? $this->getParam('proxy') : null; + return $this->getParam('transport'); } /** - * Set proxy for http connections. Null is for environmental proxy, - * empty string to disable proxy and proxy string to set actual http proxy. - * - * @see http://curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY - * - * @param string|null $proxy - * * @return $this */ - public function setProxy($proxy) - { - return $this->setParam('proxy', $proxy); - } - - /** - * @return array|string - */ - public function getTransport() - { - return $this->hasParam('transport') ? $this->getParam('transport') : self::DEFAULT_TRANSPORT; - } - - /** - * @param array|string $transport - * - * @return $this - */ - public function setTransport($transport) + public function setTransport(Transport $transport) { return $this->setParam('transport', $transport); } @@ -178,49 +137,6 @@ public function setPath($path) return $this->setParam('path', $path); } - /** - * @param int $timeout Timeout in seconds - * - * @return $this - */ - public function setTimeout($timeout) - { - return $this->setParam('timeout', $timeout); - } - - /** - * @return int Connection timeout in seconds - */ - public function getTimeout() - { - return (int) $this->hasParam('timeout') ? $this->getParam('timeout') : self::TIMEOUT; - } - - /** - * Number of seconds after a connection timeout occurs for every request during the connection phase. - * Use a small value if you need a fast fail in case of dead, unresponsive or unreachable servers (~5 sec). - * - * Set to zero to switch to the default built-in connection timeout (300 seconds in curl). - * - * @see http://curl.haxx.se/libcurl/c/CURLOPT_CONNECTTIMEOUT.html - * - * @param int $timeout Connect timeout in seconds - * - * @return $this - */ - public function setConnectTimeout($timeout) - { - return $this->setParam('connectTimeout', $timeout); - } - - /** - * @return int Connection timeout in seconds - */ - public function getConnectTimeout() - { - return (int) $this->hasParam('connectTimeout') ? $this->getParam('connectTimeout') : self::CONNECT_TIMEOUT; - } - /** * Enables a connection. * @@ -244,15 +160,11 @@ public function isEnabled() /** * Returns an instance of the transport type. * - * @throws InvalidException If invalid transport type - * - * @return AbstractTransport Transport object + * @return Transport Transport object */ - public function getTransportObject() + public function getTransportObject(): Transport { - $transport = $this->getTransport(); - - return AbstractTransport::create($transport, $this); + return $this->getTransport(); } /** diff --git a/src/Exception/Connection/GuzzleException.php b/src/Exception/Connection/GuzzleException.php deleted file mode 100644 index da3fb6a3c6..0000000000 --- a/src/Exception/Connection/GuzzleException.php +++ /dev/null @@ -1,42 +0,0 @@ - - */ -class GuzzleException extends ConnectionException -{ - /** - * @var TransferException - */ - protected $_guzzleException; - - public function __construct(TransferException $guzzleException, ?Request $request = null, ?Response $response = null) - { - $this->_guzzleException = $guzzleException; - $message = $this->getErrorMessage($this->getGuzzleException()); - parent::__construct($message, $request, $response); - } - - public function getErrorMessage(TransferException $guzzleException): string - { - return $guzzleException->getMessage(); - } - - /** - * @return TransferException - */ - public function getGuzzleException(): BaseGuzzleException - { - return $this->_guzzleException; - } -} diff --git a/src/Exception/Connection/HttpException.php b/src/Exception/Connection/HttpException.php deleted file mode 100644 index 6620ce9003..0000000000 --- a/src/Exception/Connection/HttpException.php +++ /dev/null @@ -1,67 +0,0 @@ - - */ -class HttpException extends ConnectionException -{ - /** - * Error code / message. - * - * @var int Error code / message - */ - protected $_error = 0; - - /** - * Construct Exception. - * - * @param int $error - */ - public function __construct($error, ?Request $request = null, ?Response $response = null) - { - $this->_error = $error; - - $message = $this->getErrorMessage($this->getError()); - parent::__construct($message, $request, $response); - } - - /** - * Returns the error message corresponding to the error code - * cUrl error code reference can be found here {@link http://curl.haxx.se/libcurl/c/libcurl-errors.html}. - * - * @param int $error Error code - * - * @return string Error message - */ - public function getErrorMessage(int $error): string - { - return match ($error) { - \CURLE_UNSUPPORTED_PROTOCOL => 'Unsupported protocol', - \CURLE_FAILED_INIT => 'Internal cUrl error?', - \CURLE_URL_MALFORMAT => 'Malformed URL', - \CURLE_COULDNT_RESOLVE_PROXY => "Couldn't resolve proxy", - \CURLE_COULDNT_RESOLVE_HOST => "Couldn't resolve host", - \CURLE_COULDNT_CONNECT => "Couldn't connect to host, Elasticsearch down?", - \CURLE_OPERATION_TIMEOUTED => 'Operation timed out', - default => 'Unknown error:'.$error, - }; - } - - /** - * Return Error code / message. - * - * @return int Error code / message - */ - public function getError(): int - { - return $this->_error; - } -} diff --git a/src/Exception/ConnectionException.php b/src/Exception/ConnectionException.php deleted file mode 100644 index 99f1680cd3..0000000000 --- a/src/Exception/ConnectionException.php +++ /dev/null @@ -1,55 +0,0 @@ - - */ -class ConnectionException extends \RuntimeException implements ExceptionInterface -{ - /** - * @var Request|null Request object - */ - protected $_request; - - /** - * @var Response|null Response object - */ - protected $_response; - - /** - * Construct Exception. - */ - public function __construct(string $message, ?Request $request = null, ?Response $response = null) - { - $this->_request = $request; - $this->_response = $response; - - parent::__construct($message); - } - - /** - * Returns request object. - * - * @return Request|null Request object - */ - public function getRequest() - { - return $this->_request; - } - - /** - * Returns response object. - * - * @return Response|null Response object - */ - public function getResponse() - { - return $this->_response; - } -} diff --git a/src/Exception/PartialShardFailureException.php b/src/Exception/PartialShardFailureException.php deleted file mode 100644 index 6a8b51a738..0000000000 --- a/src/Exception/PartialShardFailureException.php +++ /dev/null @@ -1,26 +0,0 @@ - - */ -class PartialShardFailureException extends ResponseException -{ - /** - * Construct Exception. - */ - public function __construct(Request $request, Response $response) - { - parent::__construct($request, $response); - - $shardsStatistics = $response->getShardsStatistics(); - $this->message = JSON::stringify($shardsStatistics); - } -} diff --git a/src/Exception/ResponseException.php b/src/Exception/ResponseException.php deleted file mode 100644 index d90d5d0ff3..0000000000 --- a/src/Exception/ResponseException.php +++ /dev/null @@ -1,54 +0,0 @@ - - */ -class ResponseException extends \RuntimeException implements ExceptionInterface -{ - /** - * @var Request Request object - */ - protected $_request; - - /** - * @var Response Response object - */ - protected $_response; - - /** - * Construct Exception. - */ - public function __construct(Request $request, Response $response) - { - $this->_request = $request; - $this->_response = $response; - parent::__construct($response->getErrorMessage()); - } - - /** - * Returns request object. - * - * @return Request Request object - */ - public function getRequest(): Request - { - return $this->_request; - } - - /** - * Returns response object. - * - * @return Response Response object - */ - public function getResponse(): Response - { - return $this->_response; - } -} diff --git a/src/Index.php b/src/Index.php index 161507c17c..534e820514 100644 --- a/src/Index.php +++ b/src/Index.php @@ -2,45 +2,21 @@ namespace Elastica; +use Elastic\Elasticsearch\Exception\ClientResponseException; +use Elastic\Elasticsearch\Exception\MissingParameterException; +use Elastic\Elasticsearch\Exception\ServerResponseException; +use Elastic\Transport\Exception\NoNodeAvailableException; use Elastica\Bulk\ResponseSet; use Elastica\Exception\Bulk\ResponseException as BulkResponseException; use Elastica\Exception\ClientException; -use Elastica\Exception\ConnectionException; use Elastica\Exception\InvalidException; use Elastica\Exception\NotFoundException; -use Elastica\Exception\ResponseException; use Elastica\Index\Recovery as IndexRecovery; use Elastica\Index\Settings as IndexSettings; use Elastica\Index\Stats as IndexStats; use Elastica\Query\AbstractQuery; use Elastica\ResultSet\BuilderInterface; use Elastica\Script\AbstractScript; -use Elasticsearch\Endpoints\AbstractEndpoint; -use Elasticsearch\Endpoints\DeleteByQuery; -use Elasticsearch\Endpoints\Get as DocumentGet; -use Elasticsearch\Endpoints\Index as IndexEndpoint; -use Elasticsearch\Endpoints\Indices\Alias; -use Elasticsearch\Endpoints\Indices\Aliases\Update; -use Elasticsearch\Endpoints\Indices\Analyze; -use Elasticsearch\Endpoints\Indices\Cache\Clear; -use Elasticsearch\Endpoints\Indices\ClearCache; -use Elasticsearch\Endpoints\Indices\Close; -use Elasticsearch\Endpoints\Indices\Create; -use Elasticsearch\Endpoints\Indices\Delete; -use Elasticsearch\Endpoints\Indices\DeleteAlias; -use Elasticsearch\Endpoints\Indices\Exists; -use Elasticsearch\Endpoints\Indices\Flush; -use Elasticsearch\Endpoints\Indices\ForceMerge; -use Elasticsearch\Endpoints\Indices\GetAlias; -use Elasticsearch\Endpoints\Indices\GetMapping; -use Elasticsearch\Endpoints\Indices\Mapping\Get as MappingGet; -use Elasticsearch\Endpoints\Indices\Open; -use Elasticsearch\Endpoints\Indices\PutSettings; -use Elasticsearch\Endpoints\Indices\Refresh; -use Elasticsearch\Endpoints\Indices\Settings\Put; -use Elasticsearch\Endpoints\Indices\UpdateAliases; -use Elasticsearch\Endpoints\OpenPointInTime; -use Elasticsearch\Endpoints\UpdateByQuery; /** * Elastica index object. @@ -115,17 +91,16 @@ public function setMapping(Mapping $mapping, array $query = []): Response /** * Gets all mappings for the current index. * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function getMapping(): array { - // TODO: Use only GetMapping when dropping support for elasticsearch/elasticsearch 7.x - $endpoint = \class_exists(GetMapping::class) ? new GetMapping() : new MappingGet(); - - $response = $this->requestEndpoint($endpoint); - $data = $response->getData(); + $response = $this->getClient()->indices()->getMapping(['index' => $this->getName()]); + $data = $response->asArray(); // Get first entry as if index is an Alias, the name of the mapping is the real name and not alias name $mapping = \array_shift($data); @@ -159,11 +134,12 @@ public function createDocument(string $id = '', $data = []) * @param Document[] $docs Array of Elastica\Document * @param array $options Array of query params to use for query. For possible options check es api * - * @throws ClientException - * @throws ConnectionException - * @throws ResponseException + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws BulkResponseException - * @throws InvalidException + * @throws ClientException * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html */ @@ -188,38 +164,43 @@ public function updateDocuments(array $docs, array $options = []): ResponseSet * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update-by-query.html * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function updateByQuery($query, AbstractScript $script, array $options = []): Response { - $endpoint = new UpdateByQuery(); $q = Query::create($query)->getQuery(); - $body = [ - 'query' => \is_array($q) ? $q : $q->toArray(), - 'script' => $script->toArray()['script'], + $params = [ + 'index' => $this->getName(), + 'body' => [ + 'query' => \is_array($q) ? $q : $q->toArray(), + 'script' => $script->toArray()['script'], + ], ]; - $endpoint->setBody($body); - $endpoint->setParams($options); - - return $this->requestEndpoint($endpoint); + return $this->_client->toElasticaResponse( + $this->_client->updateByQuery(\array_merge($params, $options)) + ); } /** * Adds the given document to the search index. * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function addDocument(Document $doc): Response { - $endpoint = new IndexEndpoint(); + $params = ['index' => $this->getName()]; if (null !== $doc->getId() && '' !== $doc->getId()) { - $endpoint->setId($doc->getId()); + $params['id'] = $doc->getId(); } $options = $doc->getOptions( @@ -237,10 +218,10 @@ public function addDocument(Document $doc): Response ] ); - $endpoint->setBody($doc->getData()); - $endpoint->setParams($options); + $params['body'] = $doc->getData(); + $params = \array_merge($params, $options); - $response = $this->requestEndpoint($endpoint); + $response = $this->_client->toElasticaResponse($this->_client->index($params)); $data = $response->getData(); // set autogenerated id to document @@ -262,11 +243,12 @@ public function addDocument(Document $doc): Response * @param array|Document[] $docs Array of Elastica\Document * @param array $options Array of query params to use for query. For possible options check es api * - * @throws ClientException - * @throws ConnectionException - * @throws ResponseException + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws BulkResponseException - * @throws InvalidException + * @throws ClientException * * @return ResponseSet * @@ -287,36 +269,44 @@ public function addDocuments(array $docs, array $options = []) * @param int|string $id Document id * @param array $options options for the get request * - * @throws NotFoundException + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException + * @throws NotFoundException */ public function getDocument($id, array $options = []): Document { - $endpoint = new DocumentGet(); - $endpoint->setId($id); - $endpoint->setParams($options); - - $response = $this->requestEndpoint($endpoint); - $result = $response->getData(); + $params = \array_merge([ + 'id' => $id, + 'index' => $this->getName(), + ], $options); + + try { + $response = $this->getClient()->get($params); + $result = $response->asArray(); + + if (isset($result['fields'])) { + $data = $result['fields']; + } elseif (isset($result['_source'])) { + $data = $result['_source']; + } else { + $data = []; + } - if (!isset($result['found']) || false === $result['found']) { - throw new NotFoundException('doc id '.$id.' not found'); - } + $doc = new Document($id, $data, $this->getName()); + $doc->setVersionParams($result); - if (isset($result['fields'])) { - $data = $result['fields']; - } elseif (isset($result['_source'])) { - $data = $result['_source']; - } else { - $data = []; + return $doc; + } catch (ClientResponseException $e) { + // 404 means the index alias doesn't exist which means no indexes have it. + if (404 === $e->getResponse()->getStatusCode()) { + throw new NotFoundException('doc id '.$id.' not found'); + } + // If we don't have a 404 then this is still unexpected so rethrow the exception. + throw $e; } - - $doc = new Document($id, $data, $this->getName()); - $doc->setVersionParams($result); - - return $doc; } /** @@ -324,9 +314,11 @@ public function getDocument($id, array $options = []): Document * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete.html * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function deleteById(string $id, array $options = []): Response { @@ -334,11 +326,14 @@ public function deleteById(string $id, array $options = []): Response throw new NotFoundException('Doc id "'.$id.'" not found and can not be deleted'); } - $endpoint = new \Elasticsearch\Endpoints\Delete(); - $endpoint->setId(\trim($id)); - $endpoint->setParams($options); + $params = [ + 'id' => \trim($id), + 'index' => $this->getName(), + ]; - return $this->requestEndpoint($endpoint); + return $this->_client->toElasticaResponse( + $this->_client->delete(\array_merge($params, $options)) + ); } /** @@ -352,19 +347,24 @@ public function deleteById(string $id, array $options = []): Response * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function deleteByQuery($query, array $options = []): Response { $query = Query::create($query)->getQuery(); - $endpoint = new DeleteByQuery(); - $endpoint->setBody(['query' => \is_array($query) ? $query : $query->toArray()]); - $endpoint->setParams($options); + $params = \array_merge([ + 'index' => $this->getName(), + 'body' => ['query' => \is_array($query) ? $query : $query->toArray()], + ], $options); - return $this->requestEndpoint($endpoint); + return $this->_client->toElasticaResponse( + $this->_client->deleteByQuery($params) + ); } /** @@ -372,28 +372,33 @@ public function deleteByQuery($query, array $options = []): Response * * @see: https://www.elastic.co/guide/en/elasticsearch/reference/current/point-in-time-api.html * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function openPointInTime(string $keepAlive): Response { - $endpoint = new OpenPointInTime(); - $endpoint->setParams(['keep_alive' => $keepAlive]); - - return $this->requestEndpoint($endpoint); + return $this->_client->toElasticaResponse( + $this->_client->openPointInTime(['index' => $this->getName(), 'keep_alive' => $keepAlive]) + ); } /** * Deletes the index. * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function delete(): Response { - return $this->requestEndpoint(new Delete()); + return $this->_client->toElasticaResponse( + $this->_client->indices()->delete(['index' => $this->getName()]) + ); } /** @@ -401,11 +406,12 @@ public function delete(): Response * * @param Document[] $docs Array of documents * - * @throws ClientException - * @throws ConnectionException - * @throws ResponseException + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws BulkResponseException - * @throws InvalidException + * @throws ClientException * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html */ @@ -427,16 +433,17 @@ public function deleteDocuments(array $docs): ResponseSet * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-forcemerge.html * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function forcemerge($args = []): Response { - $endpoint = new ForceMerge(); - $endpoint->setParams($args); - - return $this->requestEndpoint($endpoint); + return $this->_client->toElasticaResponse( + $this->_client->indices()->forcemerge(\array_merge(['index' => $this->getName(), $args])) + ); } /** @@ -444,13 +451,17 @@ public function forcemerge($args = []): Response * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-refresh.html * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function refresh(): Response { - return $this->requestEndpoint(new Refresh()); + return $this->_client->toElasticaResponse( + $this->_client->indices()->refresh() + ); } /** @@ -461,56 +472,48 @@ public function refresh(): Response * @param array $args Additional arguments to pass to the Create endpoint * @param array $options Associative array of options (option=>value) * - * @throws InvalidException + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - * - * @return Response Server response */ public function create(array $args = [], array $options = []): Response { - $endpoint = new Create(); - $invalidOptions = \array_diff(\array_keys($options), $allowedOptions = \array_merge($endpoint->getParamWhitelist(), [ - 'recreate', - ])); - - if (1 === $invalidOptionCount = \count($invalidOptions)) { - throw new InvalidException(\sprintf('"%s" is not a valid option. Allowed options are "%s".', \implode('", "', $invalidOptions), \implode('", "', $allowedOptions))); - } - - if ($invalidOptionCount > 1) { - throw new InvalidException(\sprintf('"%s" are not valid options. Allowed options are "%s".', \implode('", "', $invalidOptions), \implode('", "', $allowedOptions))); - } - if ($options['recreate'] ?? false) { try { $this->delete(); - } catch (ResponseException $e) { + } catch (ClientResponseException $e) { // Index can't be deleted, because it doesn't exist } } unset($options['recreate']); - $endpoint->setParams($options); - $endpoint->setBody($args); + $params = \array_merge([ + 'index' => $this->getName(), + 'body' => $args, + ], $options); - return $this->requestEndpoint($endpoint); + return $this->_client->toElasticaResponse( + $this->_client->indices()->create($params) + ); } /** * Checks if the given index exists ans is created. * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function exists(): bool { - $response = $this->requestEndpoint(new Exists()); + $response = $this->getClient()->indices()->exists(['index' => $this->getName()]); - return 200 === $response->getStatus(); + return 200 === $response->getStatusCode(); } /** @@ -550,13 +553,17 @@ public function count($query = '', string $method = Request::POST): int * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-open-close.html * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function open(): Response { - return $this->requestEndpoint(new Open()); + return $this->_client->toElasticaResponse( + $this->_client->indices()->open(['index' => $this->getName()]) + ); } /** @@ -564,13 +571,17 @@ public function open(): Response * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-open-close.html * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function close(): Response { - return $this->requestEndpoint(new Close()); + return $this->_client->toElasticaResponse( + $this->_client->indices()->close(['index' => $this->getName()]) + ); } /** @@ -596,9 +607,11 @@ public function getClient(): Client * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function addAlias(string $name, bool $replace = false): Response { @@ -613,11 +626,13 @@ public function addAlias(string $name, bool $replace = false): Response $data['actions'][] = ['add' => ['index' => $this->getName(), 'alias' => $name]]; - // TODO: Use only UpdateAliases when dropping support for elasticsearch/elasticsearch 7.x - $endpoint = \class_exists(UpdateAliases::class) ? new UpdateAliases() : new Update(); - $endpoint->setBody($data); + // // TODO: Use only UpdateAliases when dropping support for elasticsearch/elasticsearch 7.x + // $endpoint = \class_exists(UpdateAliases::class) ? new UpdateAliases() : new Update(); + // $endpoint->setBody($data); - return $this->getClient()->requestEndpoint($endpoint); + return $this->_client->toElasticaResponse( + $this->_client->indices()->updateAliases(['index' => $this->getName(), 'body' => $data]) + ); } /** @@ -625,35 +640,34 @@ public function addAlias(string $name, bool $replace = false): Response * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-aliases.html * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function removeAlias(string $name): Response { - // TODO: Use only DeleteAlias when dropping support for elasticsearch/elasticsearch 7.x - $endpoint = \class_exists(DeleteAlias::class) ? new DeleteAlias() : new Alias\Delete(); - $endpoint->setName($name); - - return $this->requestEndpoint($endpoint); + return $this->_client->toElasticaResponse( + $this->_client->indices()->deleteAlias(['index' => $this->getName(), 'name' => $name]) + ); } /** * Returns all index aliases. * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException * * @return string[] */ public function getAliases(): array { - // TODO: Use only GetAlias when dropping support for elasticsearch/elasticsearch 7.x - $endpoint = \class_exists(GetAlias::class) ? new GetAlias() : new Alias\Get(); - $endpoint->setName('*'); - - $responseData = $this->requestEndpoint($endpoint)->getData(); + $response = $this->getClient()->indices()->getAlias(['name' => '*']); + $responseData = $response->asArray(); if (!isset($responseData[$this->getName()])) { return []; @@ -680,17 +694,18 @@ public function hasAlias(string $name): bool * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-clearcache.html * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function clearCache(): Response { - // TODO: Use only ClearCache when dropping support for elasticsearch/elasticsearch 7.x - $endpoint = \class_exists(ClearCache::class) ? new ClearCache() : new Clear(); - // TODO: add additional cache clean arguments - return $this->requestEndpoint($endpoint); + return $this->_client->toElasticaResponse( + $this->_client->indices()->clearCache() + ); } /** @@ -698,16 +713,17 @@ public function clearCache(): Response * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-flush.html * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function flush(array $options = []): Response { - $endpoint = new Flush(); - $endpoint->setParams($options); - - return $this->requestEndpoint($endpoint); + return $this->_client->toElasticaResponse( + $this->_client->indices()->flush($options) + ); } /** @@ -717,50 +733,17 @@ public function flush(array $options = []): Response * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function setSettings(array $data): Response { - // TODO: Use only PutSettings when dropping support for elasticsearch/elasticsearch 7.x - $endpoint = \class_exists(PutSettings::class) ? new PutSettings() : new Put(); - $endpoint->setBody($data); - - return $this->requestEndpoint($endpoint); - } - - /** - * Makes calls to the elasticsearch server based on this index. - * - * @param string $path Path to call - * @param string $method Rest method to use (GET, POST, DELETE, PUT) - * @param array|string $data Arguments as array or encoded string - * - * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - */ - public function request(string $path, string $method, $data = [], array $queryParameters = []): Response - { - $path = $this->getName().'/'.$path; - - return $this->getClient()->request($path, $method, $data, $queryParameters); - } - - /** - * Makes calls to the elasticsearch server with usage official client Endpoint based on this index. - * - * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - */ - public function requestEndpoint(AbstractEndpoint $endpoint): Response - { - $cloned = clone $endpoint; - $cloned->setIndex($this->getName()); - - return $this->getClient()->requestEndpoint($cloned); + return $this->_client->toElasticaResponse( + $this->_client->indices()->putSettings(['index' => $this->getName(), 'body' => $data]) + ); } /** @@ -771,17 +754,21 @@ public function requestEndpoint(AbstractEndpoint $endpoint): Response * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-analyze.html * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function analyze(array $body, $args = []): array { - $endpoint = new Analyze(); - $endpoint->setBody($body); - $endpoint->setParams($args); + $params = \array_merge([ + 'index' => $this->getName(), + 'body' => $body, + ], $args); - $data = $this->requestEndpoint($endpoint)->getData(); + $response = $this->getClient()->indices()->analyze($params); + $data = $response->asArray(); // Support for "Explain" parameter, that returns a different response structure from Elastic // @see: https://www.elastic.co/guide/en/elasticsearch/reference/current/_explain_analyze.html @@ -810,6 +797,6 @@ public function updateDocument($data, array $options = []): Response throw new InvalidException('Document or Script id is not set'); } - return $this->getClient()->updateDocument($data->getId(), $data, $this->getName(), $options); + return $this->_client->updateDocument($data->getId(), $data, $this->getName(), $options); } } diff --git a/src/Index/Recovery.php b/src/Index/Recovery.php index 2378fd2bcc..3869c3fc72 100644 --- a/src/Index/Recovery.php +++ b/src/Index/Recovery.php @@ -4,7 +4,6 @@ use Elastica\Index as BaseIndex; use Elastica\Response; -use Elasticsearch\Endpoints\Indices\Recovery as RecoveryEndpoint; /** * Elastica index recovery object. @@ -59,8 +58,6 @@ public function getIndex(): BaseIndex /** * Returns response object. - * - * @return Response Response object */ public function getResponse(): Response { @@ -94,9 +91,8 @@ public function refresh(): self */ protected function getRecoveryData() { - $endpoint = new RecoveryEndpoint(); - - $this->_response = $this->getIndex()->requestEndpoint($endpoint); + $client = $this->getIndex()->getClient(); + $this->_response = $client->toElasticaResponse($client->indices()->recovery(['index' => $this->getIndex()->getName()])); return $this->getResponse()->getData(); } diff --git a/src/Index/Settings.php b/src/Index/Settings.php index d88d9d6a5c..df166e685f 100644 --- a/src/Index/Settings.php +++ b/src/Index/Settings.php @@ -2,13 +2,14 @@ namespace Elastica\Index; +use Elastic\Elasticsearch\Exception\ClientResponseException; +use Elastic\Elasticsearch\Exception\ServerResponseException; +use Elastic\Transport\Exception\NoNodeAvailableException; use Elastica\Exception\ClientException; -use Elastica\Exception\ConnectionException; use Elastica\Exception\NotFoundException; -use Elastica\Exception\ResponseException; use Elastica\Index as BaseIndex; -use Elastica\Request; use Elastica\Response; +use Elastica\ResponseConverter; /** * Elastica index settings object. @@ -69,9 +70,10 @@ public function __construct(BaseIndex $index) * * @param string $setting OPTIONAL Setting name to return * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException * * @return array|int|string|null Settings data * @@ -83,11 +85,14 @@ public function get(string $setting = '', bool $includeDefaults = false) 'include_defaults' => $includeDefaults, ]; - $requestData = $this->request([], Request::GET, $queryParameters)->getData(); + $requestData = $this->getIndex()->getClient()->indices()->getSettings( + \array_merge(['index' => $this->getIndex()->getName()], $queryParameters) + )->asArray(); + $data = \reset($requestData); if (empty($data['settings']) || empty($data['settings']['index'])) { - // should not append, the request should throw a ResponseException + // should not append, the request should throw a ClientResponseException throw new NotFoundException('Index '.$this->getIndex()->getName().' not found'); } @@ -132,9 +137,10 @@ public function get(string $setting = '', bool $includeDefaults = false) * * @param string $setting Setting name to return * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function getBool(string $setting): bool { @@ -148,11 +154,10 @@ public function getBool(string $setting): bool * * @param int $replicas Number of replicas * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - * - * @return Response Response object */ public function setNumberOfReplicas(int $replicas): Response { @@ -164,9 +169,10 @@ public function setNumberOfReplicas(int $replicas): Response * * If no number of replicas is set, the default number is returned * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException * * @return int The number of replicas */ @@ -180,9 +186,10 @@ public function getNumberOfReplicas(): int * * If no number of shards is set, the default number is returned * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException * * @return int The number of shards */ @@ -196,9 +203,10 @@ public function getNumberOfShards(): int * * @param bool $readOnly (default = true) * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function setReadOnly(bool $readOnly = true): Response { @@ -206,9 +214,10 @@ public function setReadOnly(bool $readOnly = true): Response } /** + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function getReadOnly(): bool { @@ -216,9 +225,10 @@ public function getReadOnly(): bool } /** + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function getBlocksRead(): bool { @@ -228,9 +238,10 @@ public function getBlocksRead(): bool /** * @param bool $state OPTIONAL (default = true) * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function setBlocksRead(bool $state = true): Response { @@ -238,9 +249,10 @@ public function setBlocksRead(bool $state = true): Response } /** + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function getBlocksWrite(): bool { @@ -250,9 +262,10 @@ public function getBlocksWrite(): bool /** * @param bool $state OPTIONAL (default = true) * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function setBlocksWrite(bool $state = true): Response { @@ -260,9 +273,10 @@ public function setBlocksWrite(bool $state = true): Response } /** + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function getBlocksMetadata(): bool { @@ -270,8 +284,9 @@ public function getBlocksMetadata(): bool // So when a cluster_block_exception happened it must be enabled. try { return $this->getBool('blocks.metadata'); - } catch (ResponseException $e) { - if ('cluster_block_exception' === $e->getResponse()->getFullError()['type']) { + } catch (ClientResponseException $e) { + $response = ResponseConverter::toElastica($e->getResponse()); + if ('cluster_block_exception' === $response->getFullError()['type']) { return true; } @@ -284,9 +299,10 @@ public function getBlocksMetadata(): bool * * @param bool $state OPTIONAL (default = true) * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function setBlocksMetadata(bool $state = true): Response { @@ -301,11 +317,10 @@ public function setBlocksMetadata(bool $state = true): Response * * @param string $interval Duration of the refresh interval * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - * - * @return Response Response object */ public function setRefreshInterval(string $interval): Response { @@ -317,9 +332,10 @@ public function setRefreshInterval(string $interval): Response * * If no interval is set, the default interval is returned * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException * * @return string Refresh interval */ @@ -336,9 +352,10 @@ public function getRefreshInterval(): string * @param string $key Merge policy key (for ex. expunge_deletes_allowed) * @param int|string $value * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException * * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-merge.html */ @@ -356,9 +373,10 @@ public function setMergePolicy(string $key, $value): Response * * @param string $key Merge policy key (for ex. expunge_deletes_allowed) * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException * * @return int|string * @@ -376,15 +394,24 @@ public function getMergePolicy(string $key) * * @param array $data Arguments * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - * - * @return Response Response object */ public function set(array $data): Response { - return $this->request($data, Request::PUT); + $client = $this->getIndex()->getClient(); + + if ($data) { + $data = ['index' => $data]; + } + + return $client->toElasticaResponse( + $client->indices()->putSettings( + \array_merge(['index' => $this->getIndex()->getName(), 'body' => $data]) + ) + ); } /** @@ -396,37 +423,4 @@ public function getIndex(): BaseIndex { return $this->_index; } - - /** - * Updates the given settings for the index. - * - * With elasticsearch 0.16 the following settings are supported - * - index.term_index_interval - * - index.term_index_divisor - * - index.translog.flush_threshold_ops - * - index.translog.flush_threshold_size - * - index.translog.flush_threshold_period - * - index.refresh_interval - * - index.merge.policy - * - index.auto_expand_replicas - * - * @param array $data OPTIONAL Data array - * @param string $method OPTIONAL Transfer method (default = \Elastica\Request::GET) - * - * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - * - * @return Response Response object - */ - public function request(array $data = [], string $method = Request::GET, array $queryParameters = []): Response - { - $path = '_settings'; - - if ($data) { - $data = ['index' => $data]; - } - - return $this->getIndex()->request($path, $method, $data, $queryParameters); - } } diff --git a/src/Index/Stats.php b/src/Index/Stats.php index 840d85ff17..e986500355 100644 --- a/src/Index/Stats.php +++ b/src/Index/Stats.php @@ -85,8 +85,6 @@ public function getIndex(): Index /** * Returns response object. - * - * @return Response Response object */ public function getResponse(): Response { @@ -98,7 +96,14 @@ public function getResponse(): Response */ public function refresh(): void { - $this->_response = $this->getIndex()->requestEndpoint(new \Elasticsearch\Endpoints\Indices\Stats()); + $client = $this->getIndex()->getClient(); + + $this->_response = $client->toElasticaResponse( + $client->indices()->stats( + ['index' => $this->getIndex()->getName()] + ) + ); + $this->_data = $this->getResponse()->getData(); } } diff --git a/src/IndexTemplate.php b/src/IndexTemplate.php index 58ea468ea1..9283c41aa6 100644 --- a/src/IndexTemplate.php +++ b/src/IndexTemplate.php @@ -2,10 +2,12 @@ namespace Elastica; +use Elastic\Elasticsearch\Exception\ClientResponseException; +use Elastic\Elasticsearch\Exception\MissingParameterException; +use Elastic\Elasticsearch\Exception\ServerResponseException; +use Elastic\Transport\Exception\NoNodeAvailableException; use Elastica\Exception\ClientException; -use Elastica\Exception\ConnectionException; use Elastica\Exception\InvalidException; -use Elastica\Exception\ResponseException; /** * Elastica index template object. @@ -48,15 +50,17 @@ public function __construct(Client $client, $name) /** * Deletes the index template. * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - * - * @return Response */ - public function delete() + public function delete(): Response { - return $this->request(Request::DELETE); + return $this->_client->toElasticaResponse( + $this->_client->indices()->deleteTemplate(['name' => $this->getName()]) + ); } /** @@ -66,69 +70,48 @@ public function delete() * * @param array $args OPTIONAL Arguments to use * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - * - * @return Response */ - public function create(array $args = []) + public function create(array $args = []): Response { - return $this->request(Request::PUT, $args); + return $this->_client->toElasticaResponse( + $this->_client->indices()->putTemplate(['name' => $this->getName(), 'body' => $args]) + ); } /** * Checks if the given index template is already created. * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - * - * @return bool */ - public function exists() + public function exists(): bool { - $response = $this->request(Request::HEAD); + $response = $this->_client->indices()->existsTemplate(['name' => $this->getName()]); - return 200 === $response->getStatus(); + return 200 === $response->getStatusCode(); } /** * Returns the index template name. - * - * @return string Index name */ - public function getName() + public function getName(): string { return $this->_name; } /** * Returns index template client. - * - * @return Client */ - public function getClient() + public function getClient(): Client { return $this->_client; } - - /** - * Makes calls to the elasticsearch server based on this index template name. - * - * @param string $method Rest method to use (GET, POST, DELETE, PUT) - * @param array $data OPTIONAL Arguments as array - * - * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - * - * @return Response - */ - public function request($method, $data = []) - { - $path = '_template/'.$this->getName(); - - return $this->getClient()->request($path, $method, $data); - } } diff --git a/src/Mapping.php b/src/Mapping.php index c32cbade3e..37facc0337 100644 --- a/src/Mapping.php +++ b/src/Mapping.php @@ -3,8 +3,6 @@ namespace Elastica; use Elastica\Exception\InvalidException; -use Elasticsearch\Endpoints\Indices\Mapping\Put; -use Elasticsearch\Endpoints\Indices\PutMapping; /** * Elastica Mapping object. @@ -164,12 +162,11 @@ public function toArray(): array */ public function send(Index $index, array $query = []): Response { - // TODO: Use only PutMapping when dropping support for elasticsearch/elasticsearch 7.x - $endpoint = \class_exists(PutMapping::class) ? new PutMapping() : new Put(); - $endpoint->setBody($this->toArray()); - $endpoint->setParams($query); - - return $index->requestEndpoint($endpoint); + return $index->getClient()->toElasticaResponse( + $index->getClient()->indices()->putMapping( + \array_merge(['index' => $index->getName(), 'body' => $this->toArray()], $query) + ) + ); } /** diff --git a/src/Multi/Search.php b/src/Multi/Search.php index 8197967d8f..b82a5ad59b 100644 --- a/src/Multi/Search.php +++ b/src/Multi/Search.php @@ -4,7 +4,6 @@ use Elastica\Client; use Elastica\JSON; -use Elastica\Request; use Elastica\Search as BaseSearch; /** @@ -130,15 +129,9 @@ public function search(): ResultSet { $data = $this->_getData(); - $response = $this->getClient()->request( - '_msearch', - Request::POST, - $data, - $this->_options, - Request::NDJSON_CONTENT_TYPE - ); + $response = $this->getClient()->msearch(\array_merge(['body' => $data], $this->_options)); - return $this->_builder->buildMultiResultSet($response, $this->getSearches()); + return $this->_builder->buildMultiResultSet($this->_client->toElasticaResponse($response), $this->getSearches()); } protected function _getData(): string diff --git a/src/Node/Info.php b/src/Node/Info.php index 8989218a9d..462ead4e5c 100644 --- a/src/Node/Info.php +++ b/src/Node/Info.php @@ -2,12 +2,12 @@ namespace Elastica\Node; +use Elastic\Elasticsearch\Exception\ClientResponseException; +use Elastic\Elasticsearch\Exception\ServerResponseException; +use Elastic\Transport\Exception\NoNodeAvailableException; use Elastica\Exception\ClientException; -use Elastica\Exception\ConnectionException; -use Elastica\Exception\ResponseException; use Elastica\Node as BaseNode; use Elastica\Response; -use Elasticsearch\Endpoints\Nodes\Info as NodesInfo; /** * Elastica cluster node object. @@ -194,8 +194,6 @@ public function getName(): string /** * Returns response object. - * - * @return Response Response object */ public function getResponse(): Response { @@ -207,25 +205,23 @@ public function getResponse(): Response * * @param array $params Params to return (default none). Possible options: settings, os, process, jvm, thread_pool, network, transport, http, plugin * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - * - * @return Response Response object */ public function refresh(array $params = []): Response { $this->_params = $params; + $client = $this->getNode()->getClient(); - // TODO: Use only NodesInfo when dropping support for elasticsearch/elasticsearch 7.x - $endpoint = \class_exists(NodesInfo::class) ? new NodesInfo() : new \Elasticsearch\Endpoints\Cluster\Nodes\Info(); - $endpoint->setNodeId($this->getNode()->getId()); + $paramsRequest['node_id'] = $this->getNode()->getId(); if ($params) { - $endpoint->setMetric($params); + $paramsRequest['metric'] = $params; } - $this->_response = $this->getNode()->getClient()->requestEndpoint($endpoint); + $this->_response = $client->toElasticaResponse($client->nodes()->info($paramsRequest)); $data = $this->getResponse()->getData(); $this->_data = \reset($data['nodes']); diff --git a/src/Node/Stats.php b/src/Node/Stats.php index b975e616a5..a3f3050cda 100644 --- a/src/Node/Stats.php +++ b/src/Node/Stats.php @@ -2,12 +2,12 @@ namespace Elastica\Node; +use Elastic\Elasticsearch\Exception\ClientResponseException; +use Elastic\Elasticsearch\Exception\ServerResponseException; +use Elastic\Transport\Exception\NoNodeAvailableException; use Elastica\Exception\ClientException; -use Elastica\Exception\ConnectionException; -use Elastica\Exception\ResponseException; use Elastica\Node as BaseNode; use Elastica\Response; -use Elasticsearch\Endpoints\Nodes\Stats as NodesStats; /** * Elastica cluster node object. @@ -95,8 +95,6 @@ public function getNode(): BaseNode /** * Returns response object. - * - * @return Response Response object */ public function getResponse(): Response { @@ -106,19 +104,17 @@ public function getResponse(): Response /** * Reloads all nodes information. Has to be called if informations changed. * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - * - * @return Response Response object */ public function refresh(): Response { - // TODO: Use only NodesStats when dropping support for elasticsearch/elasticsearch 7.x - $endpoint = \class_exists(NodesStats::class) ? new NodesStats() : new \Elasticsearch\Endpoints\Cluster\Nodes\Stats(); - $endpoint->setNodeId($this->getNode()->getName()); - - $this->_response = $this->getNode()->getClient()->requestEndpoint($endpoint); + $client = $this->getNode()->getClient(); + $this->_response = $client->toElasticaResponse( + $client->nodes()->stats(['node_id' => $this->getNode()->getName()]) + ); $data = $this->getResponse()->getData(); $this->_data = \reset($data['nodes']); diff --git a/src/Pipeline.php b/src/Pipeline.php index 45697b590a..390db8b061 100644 --- a/src/Pipeline.php +++ b/src/Pipeline.php @@ -2,18 +2,8 @@ namespace Elastica; -use Elastica\Exception\ClientException; -use Elastica\Exception\ConnectionException; use Elastica\Exception\InvalidException; -use Elastica\Exception\ResponseException; use Elastica\Processor\AbstractProcessor; -use Elasticsearch\Endpoints\AbstractEndpoint; -use Elasticsearch\Endpoints\Ingest\DeletePipeline; -use Elasticsearch\Endpoints\Ingest\GetPipeline; -use Elasticsearch\Endpoints\Ingest\Pipeline\Delete; -use Elasticsearch\Endpoints\Ingest\Pipeline\Get; -use Elasticsearch\Endpoints\Ingest\Pipeline\Put; -use Elasticsearch\Endpoints\Ingest\PutPipeline; /** * Elastica Pipeline object. @@ -67,12 +57,9 @@ public function create(): Response throw new InvalidException('You should set a valid processor of type Elastica\Processor\AbstractProcessor.'); } - // TODO: Use only PutPipeline when dropping support for elasticsearch/elasticsearch 7.x - $endpoint = \class_exists(PutPipeline::class) ? new PutPipeline() : new Put(); - $endpoint->setId($this->id); - $endpoint->setBody($this->toArray()); - - return $this->requestEndpoint($endpoint); + return $this->_client->toElasticaResponse( + $this->_client->ingest()->putPipeline(['id' => $this->id, 'body' => $this->toArray()]) + ); } /** @@ -82,11 +69,9 @@ public function create(): Response */ public function getPipeline(string $id): Response { - // TODO: Use only GetPipeline when dropping support for elasticsearch/elasticsearch 7.x - $endpoint = \class_exists(GetPipeline::class) ? new GetPipeline() : new Get(); - $endpoint->setId($id); - - return $this->requestEndpoint($endpoint); + return $this->_client->toElasticaResponse( + $this->_client->ingest()->getPipeline(['id' => $id]) + ); } /** @@ -96,11 +81,9 @@ public function getPipeline(string $id): Response */ public function deletePipeline(string $id): Response { - // TODO: Use only DeletePipeline when dropping support for elasticsearch/elasticsearch 7.x - $endpoint = \class_exists(DeletePipeline::class) ? new DeletePipeline() : new Delete(); - $endpoint->setId($id); - - return $this->requestEndpoint($endpoint); + return $this->_client->toElasticaResponse( + $this->_client->ingest()->deletePipeline(['id' => $id]) + ); } /** @@ -172,18 +155,4 @@ public function getClient(): Client { return $this->_client; } - - /** - * Makes calls to the elasticsearch server with usage official client Endpoint based on this index. - * - * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - */ - public function requestEndpoint(AbstractEndpoint $endpoint): Response - { - $cloned = clone $endpoint; - - return $this->getClient()->requestEndpoint($cloned); - } } diff --git a/src/Reindex.php b/src/Reindex.php index b9d685d63e..a74b12ad42 100644 --- a/src/Reindex.php +++ b/src/Reindex.php @@ -2,9 +2,11 @@ namespace Elastica; +use Elastic\Elasticsearch\Exception\ClientResponseException; +use Elastic\Elasticsearch\Exception\MissingParameterException; +use Elastic\Elasticsearch\Exception\ServerResponseException; +use Elastic\Transport\Exception\NoNodeAvailableException; use Elastica\Exception\ClientException; -use Elastica\Exception\ConnectionException; -use Elastica\Exception\ResponseException; use Elastica\Query\AbstractQuery; use Elastica\Script\AbstractScript; use Elastica\Script\Script; @@ -16,7 +18,7 @@ class Reindex extends Param public const OPERATION_TYPE_CREATE = 'create'; public const CONFLICTS = 'conflicts'; public const CONFLICTS_PROCEED = 'proceed'; - public const SIZE = 'size'; + public const SIZE = 'max_docs'; public const QUERY = 'query'; public const SORT = 'sort'; public const SCRIPT = 'script'; @@ -61,20 +63,19 @@ public function __construct(Index $oldIndex, Index $newIndex, array $params = [] } /** + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function run(): Response { $body = $this->_getBody($this->_oldIndex, $this->_newIndex, $this->getParams()); - $reindexEndpoint = new \Elasticsearch\Endpoints\Reindex(); - $params = \array_intersect_key($this->getParams(), \array_fill_keys($reindexEndpoint->getParamWhitelist(), null)); - $reindexEndpoint->setParams($params); - $reindexEndpoint->setBody($body); - - $this->_lastResponse = $this->_oldIndex->getClient()->requestEndpoint($reindexEndpoint); + $this->_lastResponse = $this->_oldIndex->getClient()->toElasticaResponse( + $this->_oldIndex->getClient()->reindex(\array_merge(['body' => $body], $this->getParams())) + ); return $this->_lastResponse; } diff --git a/src/Request.php b/src/Request.php index 679446f2d1..b221a93d6e 100644 --- a/src/Request.php +++ b/src/Request.php @@ -2,10 +2,6 @@ namespace Elastica; -use Elastica\Exception\ConnectionException; -use Elastica\Exception\InvalidException; -use Elastica\Exception\ResponseException; - /** * Elastica Request object. * @@ -20,179 +16,4 @@ class Request extends Param public const DELETE = 'DELETE'; public const DEFAULT_CONTENT_TYPE = 'application/json'; public const NDJSON_CONTENT_TYPE = 'application/x-ndjson'; - - /** - * @var Connection|null - */ - protected $_connection; - - /** - * Construct. - * - * @param string $path Request path - * @param string $method OPTIONAL Request method (use const's) (default = self::GET) - * @param array|string $data OPTIONAL Data array - * @param array $query OPTIONAL Query params - * @param string $contentType Content-Type sent with this request - */ - public function __construct(string $path, string $method = self::GET, $data = [], array $query = [], ?Connection $connection = null, string $contentType = self::DEFAULT_CONTENT_TYPE) - { - $this->setPath($path); - $this->setMethod($method); - $this->setData($data); - $this->setQuery($query); - - if ($connection) { - $this->setConnection($connection); - } - $this->setContentType($contentType); - } - - public function __toString(): string - { - return JSON::stringify($this->toArray()); - } - - /** - * Sets the request method. Use one of the for consts. - * - * @return $this - */ - public function setMethod(string $method) - { - return $this->setParam('method', $method); - } - - /** - * Get request method. - */ - public function getMethod(): string - { - return $this->getParam('method'); - } - - /** - * Sets the request data. - * - * @param array|string $data Request data - * - * @return $this - */ - public function setData($data) - { - return $this->setParam('data', $data); - } - - /** - * Return request data. - * - * @return array|string Request data - */ - public function getData() - { - return $this->getParam('data'); - } - - /** - * Sets the request path. - * - * @return $this - */ - public function setPath(string $path) - { - return $this->setParam('path', $path); - } - - /** - * Return request path. - */ - public function getPath(): string - { - return $this->getParam('path'); - } - - /** - * Return query params. - * - * @return array Query params - */ - public function getQuery(): array - { - return $this->getParam('query'); - } - - /** - * @return $this - */ - public function setQuery(array $query = []) - { - return $this->setParam('query', $query); - } - - /** - * @return $this - */ - public function setConnection(Connection $connection) - { - $this->_connection = $connection; - - return $this; - } - - /** - * Return Connection Object. - * - * @throws Exception\InvalidException If no valid connection was set - */ - public function getConnection(): Connection - { - if (null === $this->_connection) { - throw new InvalidException('No valid connection object set'); - } - - return $this->_connection; - } - - /** - * Set the Content-Type of this request. - */ - public function setContentType(string $contentType) - { - return $this->setParam('contentType', $contentType); - } - - /** - * Get the Content-Type of this request. - */ - public function getContentType(): string - { - return $this->getParam('contentType'); - } - - /** - * Sends request to server. - * - * @throws ResponseException - * @throws ConnectionException - */ - public function send(): Response - { - $transport = $this->getConnection()->getTransportObject(); - - // Refactor: Not full toArray needed in exec? - return $transport->exec($this, $this->getConnection()->toArray()); - } - - /** - * @return array - */ - public function toArray() - { - $data = $this->getParams(); - if ($this->_connection) { - $data['connection'] = $this->_connection->getParams(); - } - - return $data; - } } diff --git a/src/Response.php b/src/Response.php index e6fdd53e71..d0b188b9b9 100644 --- a/src/Response.php +++ b/src/Response.php @@ -216,55 +216,6 @@ public function getData() return $this->_response; } - /** - * Gets the transfer information. - * - * @return array information about the curl request - */ - public function getTransferInfo() - { - return $this->_transferInfo; - } - - /** - * Sets the transfer info of the curl request. This function is called - * from the \Elastica\Client::_callService . - * - * @param array $transferInfo the curl transfer information - * - * @return $this - */ - public function setTransferInfo(array $transferInfo) - { - $this->_transferInfo = $transferInfo; - - return $this; - } - - /** - * Returns query execution time. - * - * @return float Query time - */ - public function getQueryTime() - { - return $this->_queryTime; - } - - /** - * Sets the query time. - * - * @param float $queryTime Query time - * - * @return $this - */ - public function setQueryTime($queryTime) - { - $this->_queryTime = $queryTime; - - return $this; - } - /** * Time request took. * diff --git a/src/ResponseConverter.php b/src/ResponseConverter.php new file mode 100644 index 0000000000..f360bca582 --- /dev/null +++ b/src/ResponseConverter.php @@ -0,0 +1,21 @@ + + */ +class ResponseConverter +{ + public static function toElastica(Elasticsearch|ResponseInterface $response): Response + { + if ($response instanceof Elasticsearch) { + return new Response($response->asString(), $response->getStatusCode()); + } + + return new Response($response->getBody(), $response->getStatusCode()); + } +} diff --git a/src/Scroll.php b/src/Scroll.php index 297602e327..e5e985ec7c 100644 --- a/src/Scroll.php +++ b/src/Scroll.php @@ -2,10 +2,12 @@ namespace Elastica; +use Elastic\Elasticsearch\Exception\ClientResponseException; +use Elastic\Elasticsearch\Exception\ServerResponseException; +use Elastic\Transport\Exception\NoNodeAvailableException; use Elastica\Exception\ClientException; -use Elastica\Exception\ConnectionException; use Elastica\Exception\InvalidException; -use Elastica\Exception\ResponseException; +use Elastica\Exception\NotFoundException; /** * Scroll Iterator. @@ -73,9 +75,10 @@ public function current(): ResultSet * * @see http://php.net/manual/en/iterator.next.php * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function next(): void { @@ -120,9 +123,10 @@ public function valid(): bool * * @see http://php.net/manual/en/iterator.rewind.php * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function rewind(): void { @@ -144,16 +148,15 @@ public function rewind(): void /** * Cleares the search context on ES and marks this Scroll instance as finished. * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function clear(): void { if (null !== $this->_nextScrollId) { - $this->_search->getClient()->request( - '_search/scroll', - Request::DELETE, + $this->_search->getClient()->clearScroll( [Search::OPTION_SCROLL_ID => [$this->_nextScrollId]] ); @@ -165,9 +168,11 @@ public function clear(): void /** * Prepares Scroll for next request. * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx + * @throws NotFoundException * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ protected function _setScrollId(ResultSet $resultSet): void { @@ -178,8 +183,17 @@ protected function _setScrollId(ResultSet $resultSet): void $this->_currentResultSet = $resultSet; ++$this->currentPage; $this->_nextScrollId = null; - if ($resultSet->getResponse()->isOk()) { - $this->_nextScrollId = $resultSet->getResponse()->getScrollId(); + $response = $resultSet->getResponse(); + + if ($response->isOk()) { + $data = $response->getData(); + + if (!isset($data['_scroll_id'])) { + throw new NotFoundException('Unable to find the field [_scroll_id] from the response'); + } + + $this->_nextScrollId = $data['_scroll_id']; + if (0 === $resultSet->count()) { $this->clear(); } diff --git a/src/Search.php b/src/Search.php index 826f179657..ff69997217 100644 --- a/src/Search.php +++ b/src/Search.php @@ -2,10 +2,11 @@ namespace Elastica; +use Elastic\Elasticsearch\Exception\ClientResponseException; +use Elastic\Elasticsearch\Exception\ServerResponseException; +use Elastic\Transport\Exception\NoNodeAvailableException; use Elastica\Exception\ClientException; -use Elastica\Exception\ConnectionException; use Elastica\Exception\InvalidException; -use Elastica\Exception\ResponseException; use Elastica\Query\AbstractQuery; use Elastica\Query\MatchAll; use Elastica\ResultSet\BuilderInterface; @@ -283,9 +284,10 @@ public function getPath(): string * @param array|null $options associative array of options (option=>value) * * @throws InvalidException + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function search($query = '', ?array $options = null, string $method = Request::POST): ResultSet { @@ -298,24 +300,30 @@ public function search($query = '', ?array $options = null, string $method = Req // Send scroll_id via raw HTTP body to handle cases of very large (> 4kb) ids. if ('_search/scroll' === $path) { - $data = [self::OPTION_SCROLL_ID => $params[self::OPTION_SCROLL_ID]]; + $params['body'] = [self::OPTION_SCROLL_ID => $params[self::OPTION_SCROLL_ID]]; unset($params[self::OPTION_SCROLL_ID]); + + $response = $this->getClient()->scroll($params); } else { - $data = $query->toArray(); - } + if ($indices = $this->getIndices()) { + $params['index'] = \implode(',', $indices); + } + $params['body'] = $query->toArray(); - $response = $this->getClient()->request($path, $method, $data, $params); + $response = $this->getClient()->search($params); + } - return $this->builder->buildResultSet($response, $query); + return $this->builder->buildResultSet(new Response($response->asArray(), $response->getStatusCode()), $query); } /** * @param array|Query|Query\AbstractQuery|string $query * @param bool $fullResult By default only the total hit count is returned. If set to true, the full ResultSet including aggregations is returned * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException * * @return int|ResultSet */ @@ -328,15 +336,18 @@ public function count($query = '', bool $fullResult = false, string $method = Re $query->setSize(0); $query->setTrackTotalHits(true); - $path = $this->getPath(); + $params = [ + 'body' => $query->toArray(), + [self::OPTION_SEARCH_TYPE => self::OPTION_SEARCH_TYPE_QUERY_THEN_FETCH], + ]; + + if ($indices = $this->getIndices()) { + $params['index'] = \implode(',', $indices); + } + + $response = $this->getClient()->search($params); - $response = $this->getClient()->request( - $path, - $method, - $query->toArray(), - [self::OPTION_SEARCH_TYPE => self::OPTION_SEARCH_TYPE_QUERY_THEN_FETCH] - ); - $resultSet = $this->builder->buildResultSet($response, $query); + $resultSet = $this->builder->buildResultSet(new Response($response->asArray(), $response->getStatusCode()), $query); return $fullResult ? $resultSet : $resultSet->getTotalHits(); } diff --git a/src/SearchableInterface.php b/src/SearchableInterface.php index 4e94427fc0..3126495823 100644 --- a/src/SearchableInterface.php +++ b/src/SearchableInterface.php @@ -2,10 +2,11 @@ namespace Elastica; +use Elastic\Elasticsearch\Exception\ClientResponseException; +use Elastic\Elasticsearch\Exception\ServerResponseException; +use Elastic\Transport\Exception\NoNodeAvailableException; use Elastica\Exception\ClientException; -use Elastica\Exception\ConnectionException; use Elastica\Exception\InvalidException; -use Elastica\Exception\ResponseException; use Elastica\Query\AbstractQuery; use Elastica\Suggest\AbstractSuggest; @@ -42,10 +43,11 @@ interface SearchableInterface * @param array|null $options associative array of options (option=>value) * @param string $method Request method, see Request's constants * - * @throws ClientException - * @throws ConnectionException + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws InvalidException - * @throws ResponseException + * @throws ClientException */ public function search($query = '', ?array $options = null, string $method = Request::POST): ResultSet; @@ -60,9 +62,10 @@ public function search($query = '', ?array $options = null, string $method = Req * * @param string $method Request method, see Request's constants * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException * * @return int number of documents matching the query */ diff --git a/src/Snapshot.php b/src/Snapshot.php index f030e5e305..5a5da70e34 100644 --- a/src/Snapshot.php +++ b/src/Snapshot.php @@ -2,11 +2,12 @@ namespace Elastica; +use Elastic\Elasticsearch\Exception\ClientResponseException; +use Elastic\Elasticsearch\Exception\MissingParameterException; +use Elastic\Elasticsearch\Exception\ServerResponseException; +use Elastic\Transport\Exception\NoNodeAvailableException; use Elastica\Exception\ClientException; -use Elastica\Exception\ConnectionException; use Elastica\Exception\NotFoundException; -use Elastica\Exception\ResponseException; -use Elasticsearch\Endpoints\Snapshot\Restore; /** * Class Snapshot. @@ -32,20 +33,25 @@ public function __construct(Client $client) * @param string $type the repository type ("fs" for file system) * @param array $settings Additional repository settings. If type "fs" is used, the "location" setting must be provided. * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - * - * @return Response */ - public function registerRepository($name, $type, $settings = []) + public function registerRepository($name, $type, $settings = []): Response { - $data = [ - 'type' => $type, - 'settings' => $settings, + $params = [ + 'repository' => $name, + 'body' => [ + 'type' => $type, + 'settings' => $settings, + ], ]; - return $this->request($name, Request::PUT, $data); + return $this->_client->toElasticaResponse( + $this->_client->snapshot()->createRepository($params) + ); } /** @@ -53,24 +59,25 @@ public function registerRepository($name, $type, $settings = []) * * @param string $name the name of the desired repository * - * @throws ClientException - * @throws ConnectionException + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws NotFoundException - * @throws ResponseException + * @throws ClientException * * @return array */ public function getRepository($name) { try { - $response = $this->request($name); - } catch (ResponseException $e) { - if (404 === $e->getResponse()->getStatus()) { + $response = $this->_client->snapshot()->getRepository(['repository' => $name]); + } catch (ClientResponseException $e) { + if (404 === $e->getResponse()->getStatusCode()) { throw new NotFoundException("Repository '".$name."' does not exist."); } throw $e; } - $data = $response->getData(); + $data = $response->asArray(); return $data[$name]; } @@ -78,15 +85,16 @@ public function getRepository($name) /** * Retrieve all repository records. * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException * * @return array */ public function getAllRepositories() { - return $this->request('_all')->getData(); + return $this->_client->snapshot()->getRepository()->asArray(); } /** @@ -97,15 +105,23 @@ public function getAllRepositories() * @param array $options optional settings for this snapshot * @param bool $waitForCompletion if true, the request will not return until the snapshot operation is complete * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - * - * @return Response */ - public function createSnapshot($repository, $name, $options = [], $waitForCompletion = false) + public function createSnapshot($repository, $name, $options = [], $waitForCompletion = false): Response { - return $this->request($repository.'/'.$name, Request::PUT, $options, ['wait_for_completion' => $waitForCompletion]); + $params = [ + 'repository' => $repository, + 'snapshot' => $name, + 'wait_for_completion' => $waitForCompletion, + ]; + + return $this->_client->toElasticaResponse( + $this->_client->snapshot()->create(\array_merge($params, $options)) + ); } /** @@ -114,42 +130,66 @@ public function createSnapshot($repository, $name, $options = [], $waitForComple * @param string $repository the name of the repository from which to retrieve the snapshot * @param string $name the name of the desired snapshot * - * @throws ClientException - * @throws ConnectionException + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws NotFoundException - * @throws ResponseException + * @throws ClientException * * @return array */ public function getSnapshot($repository, $name) { try { - $response = $this->request($repository.'/'.$name); - } catch (ResponseException $e) { - if (404 === $e->getResponse()->getStatus()) { + $response = $this->_client->snapshot()->get(['repository' => $repository, 'snapshot' => $name]); + } catch (ClientResponseException $e) { + if (404 === $e->getResponse()->getStatusCode()) { throw new NotFoundException("Snapshot '".$name."' does not exist in repository '".$repository."'."); } throw $e; } - $data = $response->getData(); + $data = $response->asArray(); return $data['snapshots'][0]; } + /** + * Delete a repository. + * + * @param string $repository the name of the repository from which to retrieve the snapshot + * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx + * @throws NotFoundException + * @throws ClientException + */ + public function deleteRepository($repository): Response + { + return $this->_client->toElasticaResponse( + $this->_client->snapshot()->deleteRepository(['repository' => $repository]) + ); + } + /** * Retrieve data regarding all snapshots in the given repository. * * @param string $repository the repository name * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException * * @return array */ public function getAllSnapshots($repository) { - return $this->request($repository.'/_all')->getData(); + $data = $this->_client->snapshot()->get(['repository' => $repository, 'snapshot' => '*'])->asArray(); + + return $data['snapshots'] ?? []; } /** @@ -158,15 +198,17 @@ public function getAllSnapshots($repository) * @param string $repository the repository in which the snapshot resides * @param string $name the name of the snapshot to be deleted * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - * - * @return Response */ - public function deleteSnapshot($repository, $name) + public function deleteSnapshot($repository, $name): Response { - return $this->request($repository.'/'.$name, Request::DELETE); + return $this->_client->toElasticaResponse( + $this->_client->snapshot()->delete(['repository' => $repository, 'snapshot' => $name]) + ); } /** @@ -177,42 +219,23 @@ public function deleteSnapshot($repository, $name) * @param array $options options for the restore operation * @param bool $waitForCompletion if true, the request will not return until the restore operation is complete * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - * - * @return Response */ - public function restoreSnapshot($repository, $name, $options = [], $waitForCompletion = false) + public function restoreSnapshot($repository, $name, $options = [], $waitForCompletion = false): Response { - $endpoint = (new Restore()) - ->setRepository($repository) - ->setSnapshot($name) - ->setBody($options) - ->setParams([ - 'wait_for_completion' => $waitForCompletion ? 'true' : 'false', - ]) - ; - - return $this->_client->requestEndpoint($endpoint); - } + $params = [ + 'repository' => $repository, + 'snapshot' => $name, + 'body' => $options, + 'wait_for_completion' => $waitForCompletion ? 'true' : 'false', + ]; - /** - * Perform a snapshot request. - * - * @param string $path the URL - * @param string $method the HTTP method - * @param array $data request body data - * @param array $query query string parameters - * - * @throws ClientException - * @throws ConnectionException - * @throws ResponseException - * - * @return Response - */ - public function request($path, $method = Request::GET, $data = [], array $query = []) - { - return $this->_client->request('_snapshot/'.$path, $method, $data, $query); + return $this->_client->toElasticaResponse( + $this->_client->snapshot()->restore($params) + ); } } diff --git a/src/Status.php b/src/Status.php index f8f6371d5b..8ce9487c30 100644 --- a/src/Status.php +++ b/src/Status.php @@ -2,12 +2,10 @@ namespace Elastica; +use Elastic\Elasticsearch\Exception\ClientResponseException; +use Elastic\Elasticsearch\Exception\ServerResponseException; +use Elastic\Transport\Exception\NoNodeAvailableException; use Elastica\Exception\ClientException; -use Elastica\Exception\ConnectionException; -use Elastica\Exception\ResponseException; -use Elasticsearch\Endpoints\Indices\Alias\Get; -use Elasticsearch\Endpoints\Indices\GetAlias; -use Elasticsearch\Endpoints\Indices\Stats; /** * Elastica general status. @@ -93,32 +91,29 @@ public function aliasExists(string $name) /** * Returns an array with all indices that the given alias name points to. * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException * * @return Index[] */ public function getIndicesWithAlias(string $alias) { - // TODO: Use only GetAlias when dropping support for elasticsearch/elasticsearch 7.x - $endpoint = \class_exists(GetAlias::class) ? new GetAlias() : new Get(); - $endpoint->setName($alias); - $response = null; try { - $response = $this->_client->requestEndpoint($endpoint); - } catch (ResponseException $e) { + $response = $this->_client->indices()->getAlias(['name' => $alias]); + } catch (ClientResponseException $e) { // 404 means the index alias doesn't exist which means no indexes have it. - if (404 === $e->getResponse()->getStatus()) { + if (404 === $e->getResponse()->getStatusCode()) { return []; } // If we don't have a 404 then this is still unexpected so rethrow the exception. throw $e; } $indices = []; - foreach ($response->getData() as $name => $unused) { + foreach ($response->asArray() as $name => $unused) { $indices[] = new Index($this->_client, $name); } @@ -127,10 +122,8 @@ public function getIndicesWithAlias(string $alias) /** * Returns response object. - * - * @return Response Response object */ - public function getResponse() + public function getResponse(): Response { if (null === $this->_response) { $this->refresh(); @@ -154,13 +147,17 @@ public function getShards() /** * Refresh status object. * + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function refresh(): void { - $this->_response = $this->_client->requestEndpoint(new Stats()); + $this->_response = $this->_client->toElasticaResponse( + $this->_client->indices()->stats() + ); + $this->_data = $this->getResponse()->getData(); } } diff --git a/src/Task.php b/src/Task.php index 9546c24682..be02d89d71 100644 --- a/src/Task.php +++ b/src/Task.php @@ -2,10 +2,11 @@ namespace Elastica; +use Elastic\Elasticsearch\Exception\ClientResponseException; +use Elastic\Elasticsearch\Exception\MissingParameterException; +use Elastic\Elasticsearch\Exception\ServerResponseException; +use Elastic\Transport\Exception\NoNodeAvailableException; use Elastica\Exception\ClientException; -use Elastica\Exception\ConnectionException; -use Elastica\Exception\ResponseException; -use Elasticsearch\Endpoints\Tasks; /** * Represents elasticsearch task. @@ -87,18 +88,17 @@ public function getResponse(): Response * * @param array $options * + * @throws MissingParameterException if a required parameter is missing + * @throws NoNodeAvailableException if all the hosts are offline + * @throws ClientResponseException if the status code of response is 4xx + * @throws ServerResponseException if the status code of response is 5xx * @throws ClientException - * @throws ConnectionException - * @throws ResponseException */ public function refresh(array $options = []): void { - $endpoint = (new Tasks\Get()) - ->setTaskId($this->_id) - ->setParams($options) - ; - - $this->_response = $this->_client->requestEndpoint($endpoint); + $this->_response = $this->_client->toElasticaResponse( + $this->_client->tasks()->get(\array_merge(['task_id' => $this->_id], $options)) + ); $this->_data = $this->getResponse()->getData(); } @@ -118,10 +118,8 @@ public function cancel(): Response throw new \Exception('No task id given'); } - $endpoint = (new Tasks\Cancel()) - ->setTaskId($this->_id) - ; - - return $this->_client->requestEndpoint($endpoint); + return $this->_client->toElasticaResponse( + $this->_client->tasks()->cancel(['task_id' => $this->_id]) + ); } } diff --git a/src/Transport/AbstractTransport.php b/src/Transport/AbstractTransport.php deleted file mode 100644 index df1dd9b7be..0000000000 --- a/src/Transport/AbstractTransport.php +++ /dev/null @@ -1,133 +0,0 @@ - - */ -abstract class AbstractTransport extends Param -{ - /** - * @var Connection - */ - protected $_connection; - - /** - * Construct transport. - */ - public function __construct(?Connection $connection = null) - { - if ($connection) { - $this->setConnection($connection); - } - } - - public function getConnection(): Connection - { - return $this->_connection; - } - - /** - * @return $this - */ - public function setConnection(Connection $connection): AbstractTransport - { - $this->_connection = $connection; - - return $this; - } - - /** - * Executes the transport request. - * - * @param Request $request Request object - * @param array $params Hostname, port, path, ... - * - * @throws ResponseException - * @throws ConnectionException - */ - abstract public function exec(Request $request, array $params): Response; - - /** - * BOOL values true|false should be sanityzed and passed to Elasticsearch - * as string. - * - * @param array $query - * - * @return array - */ - public function sanityzeQueryStringBool(array $query) - { - foreach ($query as $key => $value) { - if (\is_bool($value)) { - $query[$key] = ($value) ? 'true' : 'false'; - } - } - - return $query; - } - - /** - * Create a transport. - * - * The $transport parameter can be one of the following values: - * - * * string: The short name of a transport. For instance "Http" - * * object: An already instantiated instance of a transport - * * array: An array with a "type" key which must be set to one of the two options. All other - * keys in the array will be set as parameters in the transport instance - * - * @param AbstractTransport|array|string $transport A transport definition - * @param Connection $connection A connection instance - * @param array $params Parameters for the transport class - * - * @throws InvalidException - */ - public static function create($transport, Connection $connection, array $params = []): AbstractTransport - { - if (\is_array($transport) && isset($transport['type'])) { - $transportParams = $transport; - unset($transportParams['type']); - - $params = \array_replace($params, $transportParams); - $transport = $transport['type']; - } - - if (\is_string($transport)) { - $specialTransports = [ - 'nulltransport' => 'NullTransport', - ]; - - $transport = $specialTransports[\strtolower($transport)] ?? \ucfirst($transport); - $classNames = ["Elastica\\Transport\\{$transport}", $transport]; - foreach ($classNames as $className) { - if (\class_exists($className)) { - $transport = new $className(); - break; - } - } - } - - if ($transport instanceof self) { - $transport->setConnection($connection); - - foreach ($params as $key => $value) { - $transport->setParam($key, $value); - } - } else { - throw new InvalidException('Invalid transport'); - } - - return $transport; - } -} diff --git a/src/Transport/AwsAuthV4.php b/src/Transport/AwsAuthV4.php deleted file mode 100644 index 04f8cdc017..0000000000 --- a/src/Transport/AwsAuthV4.php +++ /dev/null @@ -1,112 +0,0 @@ -push($this->getSigningMiddleware(), 'sign'); - - self::$_guzzleClientConnection = new Client([ - 'handler' => $stack, - ]); - } - - return self::$_guzzleClientConnection; - } - - protected function _getBaseUrl(Connection $connection): string - { - $this->initializePortAndScheme(); - - return parent::_getBaseUrl($connection); - } - - private function getSigningMiddleware(): callable - { - $region = $this->getConnection()->hasParam('aws_region') - ? $this->getConnection()->getParam('aws_region') - : \getenv('AWS_REGION'); - $signer = new SignatureV4('es', $region); - $credProvider = $this->getCredentialProvider(); - $transport = $this; - - return Middleware::mapRequest(static function (RequestInterface $req) use ( - $signer, - $credProvider, - $transport - ) { - return $signer->signRequest($transport->sanitizeRequest($req), $credProvider()->wait()); - }); - } - - private function sanitizeRequest(RequestInterface $request): RequestInterface - { - // Trailing dots are valid parts of DNS host names (see RFC 1034), - // but interferes with header signing where AWS expects a stripped host name. - if ('.' === \substr($request->getHeader('host')[0], -1)) { - $changes = ['set_headers' => ['host' => \rtrim($request->getHeader('host')[0], '.')]]; - if (\class_exists(Psr7\Utils::class)) { - $request = Psr7\Utils::modifyRequest($request, $changes); - } else { - $request = Psr7\modify_request($request, $changes); - } - } - - return $request; - } - - private function getCredentialProvider(): callable - { - $connection = $this->getConnection(); - - if ($connection->hasParam('aws_credential_provider')) { - return $connection->getParam('aws_credential_provider'); - } - - if ($connection->hasParam('aws_secret_access_key')) { - return CredentialProvider::fromCredentials(new Credentials( - $connection->getParam('aws_access_key_id'), - $connection->getParam('aws_secret_access_key'), - $connection->hasParam('aws_session_token') - ? $connection->getParam('aws_session_token') - : null - )); - } - - return CredentialProvider::defaultProvider(); - } - - private function initializePortAndScheme(): void - { - $connection = $this->getConnection(); - if (true === $this->isSslRequired($connection)) { - $this->_scheme = 'https'; - $connection->setPort(443); - } else { - $this->_scheme = 'http'; - $connection->setPort(80); - } - } - - private function isSslRequired(Connection $conn, bool $default = false): bool - { - return $conn->hasParam('ssl') - ? (bool) $conn->getParam('ssl') - : $default; - } -} diff --git a/src/Transport/Guzzle.php b/src/Transport/Guzzle.php deleted file mode 100755 index fd9d9d5ba9..0000000000 --- a/src/Transport/Guzzle.php +++ /dev/null @@ -1,212 +0,0 @@ - - */ -class Guzzle extends AbstractTransport -{ - /** - * Http scheme. - * - * @var string Http scheme - */ - protected $_scheme = 'http'; - - /** - * Curl resource to reuse. - * - * @var Client|null Guzzle client to reuse - */ - protected static $_guzzleClientConnection; - - /** - * Makes calls to the elasticsearch server. - * - * All calls that are made to the server are done through this function - * - * @throws \Elastica\Exception\ConnectionException - * @throws ResponseException - * @throws \Elastica\Exception\Connection\HttpException - */ - public function exec(Request $request, array $params): Response - { - $connection = $this->getConnection(); - - $client = $this->_getGuzzleClient($connection->isPersistent()); - - $options = [ - 'base_uri' => $this->_getBaseUrl($connection), - RequestOptions::HEADERS => [ - 'Content-Type' => $request->getContentType(), - ], - RequestOptions::HTTP_ERRORS => false, // 4xx and 5xx is expected and NOT an exceptions in this context - ]; - - if ($connection->getTimeout()) { - $options[RequestOptions::TIMEOUT] = $connection->getTimeout(); - } - - if (null !== $proxy = $connection->getProxy()) { - $options[RequestOptions::PROXY] = $proxy; - } - - $req = $this->_createPsr7Request($request, $connection); - - try { - $start = \microtime(true); - $res = $client->send($req, $options); - $end = \microtime(true); - } catch (TransferException $ex) { - throw new GuzzleException($ex, $request, new Response($ex->getMessage())); - } - - $responseBody = (string) $res->getBody(); - $response = new Response($responseBody, $res->getStatusCode()); - $response->setQueryTime($end - $start); - if ($connection->hasConfig('bigintConversion')) { - $response->setJsonBigintConversion($connection->getConfig('bigintConversion')); - } - - $response->setTransferInfo( - [ - 'request_header' => $request->getMethod(), - 'http_code' => $res->getStatusCode(), - ] - ); - - if ($response->hasError()) { - throw new ResponseException($request, $response); - } - - if ($response->hasFailedShards()) { - throw new PartialShardFailureException($request, $response); - } - - return $response; - } - - /** - * @return Psr7\Request - */ - protected function _createPsr7Request(Request $request, Connection $connection) - { - $req = new Psr7\Request( - $request->getMethod(), - $this->_getActionPath($request), - $connection->hasConfig('headers') && \is_array($connection->getConfig('headers')) - ? $connection->getConfig('headers') - : [] - ); - - $data = $request->getData(); - if (!empty($data)) { - if (Request::GET === $req->getMethod()) { - $req = $req->withMethod(Request::POST); - } - - if ($this->hasParam('postWithRequestBody') && true == $this->getParam('postWithRequestBody')) { - $request->setMethod(Request::POST); - $req = $req->withMethod(Request::POST); - } - - $req = $req->withBody($this->streamFor($data)); - } - - return $req; - } - - /** - * Return Guzzle resource. - * - * @param bool $persistent False if not persistent connection - */ - protected function _getGuzzleClient(bool $persistent = true): Client - { - if (!$persistent || !self::$_guzzleClientConnection) { - self::$_guzzleClientConnection = new Client(); - } - - return self::$_guzzleClientConnection; - } - - /** - * Builds the base url for the guzzle connection. - */ - protected function _getBaseUrl(Connection $connection): string - { - // If url is set, url is taken. Otherwise port, host and path - $url = $connection->hasConfig('url') ? $connection->getConfig('url') : ''; - - if (!empty($url)) { - $baseUri = $url; - } else { - $baseUri = (string) Uri::fromParts([ - 'scheme' => $this->_scheme, - 'host' => $connection->getHost(), - 'port' => $connection->getPort(), - 'path' => \ltrim($connection->getPath(), '/'), - ]); - } - - return \rtrim($baseUri, '/'); - } - - /** - * Builds the action path url for each request. - */ - protected function _getActionPath(Request $request): string - { - $action = $request->getPath(); - if ($action) { - $action = '/'.\ltrim($action, '/'); - } - - if (!Util::isDateMathEscaped($action)) { - $action = Util::escapeDateMath($action); - } - - $query = $request->getQuery(); - - if (!empty($query)) { - $action .= '?'.\http_build_query( - $this->sanityzeQueryStringBool($query) - ); - } - - return $action; - } - - /** - * @param mixed $data - */ - private function streamFor($data): StreamInterface - { - if (\is_array($data)) { - $data = JSON::stringify($data, \JSON_UNESCAPED_UNICODE); - } - - return \class_exists(Psr7\Utils::class) - ? Psr7\Utils::streamFor($data) - : Psr7\stream_for($data) - ; - } -} diff --git a/src/Transport/Http.php b/src/Transport/Http.php deleted file mode 100644 index aa99fae473..0000000000 --- a/src/Transport/Http.php +++ /dev/null @@ -1,235 +0,0 @@ - - */ -class Http extends AbstractTransport -{ - /** - * Http scheme. - * - * @var string Http scheme - */ - protected $_scheme = 'http'; - - /** - * Curl resource to reuse. - * - * @var \CurlHandle|resource|null Curl resource to reuse - */ - protected static $_curlConnection; - - /** - * Makes calls to the elasticsearch server. - * - * All calls that are made to the server are done through this function - * - * @param array $params Host, Port, ... - * - * @throws ConnectionException - * @throws ResponseException - * @throws HttpException - * - * @return Response Response object - */ - public function exec(Request $request, array $params): Response - { - $connection = $this->getConnection(); - - $conn = $this->_getConnection($connection->isPersistent()); - - // If url is set, url is taken. Otherwise port, host and path - $url = $connection->hasConfig('url') ? $connection->getConfig('url') : ''; - - if (!empty($url)) { - $baseUri = $url; - } else { - $baseUri = $this->_scheme.'://'.$connection->getHost().':'.$connection->getPort().'/'.$connection->getPath(); - } - - $requestPath = $request->getPath(); - if (!Util::isDateMathEscaped($requestPath)) { - $requestPath = Util::escapeDateMath($requestPath); - } - - $baseUri .= $requestPath; - - $query = $request->getQuery(); - - if (!empty($query)) { - $baseUri .= '?'.\http_build_query( - $this->sanityzeQueryStringBool($query) - ); - } - - \curl_setopt($conn, \CURLOPT_URL, $baseUri); - \curl_setopt($conn, \CURLOPT_TIMEOUT_MS, $connection->getTimeout() * 1000); - \curl_setopt($conn, \CURLOPT_FORBID_REUSE, 0); - - // Tell ES that we support the compressed responses - // An "Accept-Encoding" header containing all supported encoding types is sent - // curl will decode the response automatically if the response is encoded - \curl_setopt($conn, \CURLOPT_ENCODING, ''); - - /* @see Connection::setConnectTimeout() */ - $connectTimeoutMs = $connection->getConnectTimeout() * 1000; - - // Let's only apply this value if the number of ms is greater than or equal to "1". - // In case "0" is passed as an argument, the value is reset to its default (300 s) - if ($connectTimeoutMs >= 1) { - \curl_setopt($conn, \CURLOPT_CONNECTTIMEOUT_MS, $connectTimeoutMs); - } - - if (null !== $proxy = $connection->getProxy()) { - \curl_setopt($conn, \CURLOPT_PROXY, $proxy); - } - - $username = $connection->getUsername(); - $password = $connection->getPassword(); - if (null !== $username && null !== $password) { - \curl_setopt($conn, \CURLOPT_HTTPAUTH, $this->_getAuthType()); - \curl_setopt($conn, \CURLOPT_USERPWD, "{$username}:{$password}"); - } - - $this->_setupCurl($conn); - - $headersConfig = $connection->hasConfig('headers') ? $connection->getConfig('headers') : []; - - $headers = []; - - if (!empty($headersConfig)) { - foreach ($headersConfig as $header => $headerValue) { - $headers[] = $header.': '.$headerValue; - } - } - - // TODO: REFACTOR - $data = $request->getData(); - $httpMethod = $request->getMethod(); - - $headers[] = 'Content-Type: '.$request->getContentType(); - - if (!empty($data)) { - if ($this->hasParam('postWithRequestBody') && true == $this->getParam('postWithRequestBody')) { - $httpMethod = Request::POST; - } - - if (\is_array($data)) { - $content = JSON::stringify($data, \JSON_UNESCAPED_UNICODE | \JSON_UNESCAPED_SLASHES); - } else { - $content = $data; - - // Escaping of / not necessary. Causes problems in base64 encoding of files - $content = \str_replace('\/', '/', $content); - } - - if ($connection->hasCompression()) { - // Compress the body of the request ... - \curl_setopt($conn, \CURLOPT_POSTFIELDS, \gzencode($content)); - - // ... and tell ES that it is compressed - $headers[] = 'Content-Encoding: gzip'; - } else { - \curl_setopt($conn, \CURLOPT_POSTFIELDS, $content); - } - } else { - \curl_setopt($conn, \CURLOPT_POSTFIELDS, ''); - } - - \curl_setopt($conn, \CURLOPT_HTTPHEADER, $headers); - - \curl_setopt($conn, \CURLOPT_NOBODY, 'HEAD' === $httpMethod); - - \curl_setopt($conn, \CURLOPT_CUSTOMREQUEST, $httpMethod); - - $start = \microtime(true); - - // cURL opt returntransfer leaks memory, therefore OB instead. - \ob_start(); - \curl_exec($conn); - $responseString = \ob_get_clean(); - - $end = \microtime(true); - - // Checks if error exists - $errorNumber = \curl_errno($conn); - - $response = new Response($responseString, \curl_getinfo($conn, \CURLINFO_RESPONSE_CODE)); - $response->setQueryTime($end - $start); - $response->setTransferInfo(\curl_getinfo($conn)); - if ($connection->hasConfig('bigintConversion')) { - $response->setJsonBigintConversion($connection->getConfig('bigintConversion')); - } - - if ($response->hasError()) { - throw new ResponseException($request, $response); - } - - if ($response->hasFailedShards()) { - throw new PartialShardFailureException($request, $response); - } - - if ($errorNumber > 0) { - throw new HttpException($errorNumber, $request, $response); - } - - return $response; - } - - /** - * Called to add additional curl params. - * - * @param \CurlHandle|resource $curlConnection Curl connection - */ - protected function _setupCurl($curlConnection): void - { - if ($this->getConnection()->hasConfig('curl')) { - foreach ($this->getConnection()->getConfig('curl') as $key => $param) { - \curl_setopt($curlConnection, $key, $param); - } - } - } - - /** - * Return Curl resource. - * - * @param bool $persistent False if not persistent connection - * - * @return \CurlHandle|resource Connection resource - */ - protected function _getConnection(bool $persistent = true) - { - if (!$persistent || !self::$_curlConnection) { - self::$_curlConnection = \curl_init(); - } - - return self::$_curlConnection; - } - - /** - * @return int - */ - protected function _getAuthType() - { - return match ($this->_connection->getAuthType()) { - 'digest' => \CURLAUTH_DIGEST, - 'gssnegotiate' => \CURLAUTH_GSSNEGOTIATE, - 'ntlm' => \CURLAUTH_NTLM, - 'basic' => \CURLAUTH_BASIC, - default => \CURLAUTH_ANY, - }; - } -} diff --git a/src/Transport/Https.php b/src/Transport/Https.php deleted file mode 100644 index 599ae0d176..0000000000 --- a/src/Transport/Https.php +++ /dev/null @@ -1,18 +0,0 @@ - - */ -class Https extends Http -{ - /** - * Https scheme. - * - * @var string https scheme - */ - protected $_scheme = 'https'; -} diff --git a/src/Transport/NullTransport.php b/src/Transport/NullTransport.php deleted file mode 100644 index add545f666..0000000000 --- a/src/Transport/NullTransport.php +++ /dev/null @@ -1,90 +0,0 @@ - - * @author Jan Domanski - */ -class NullTransport extends AbstractTransport -{ - /** - * Response you want to get from the transport. - * - * @var Response|null Response - */ - protected $_response; - - /** - * Set response object the transport returns. - * - * @param array $params Hostname, port, path, ... - */ - public function getResponse(array $params = []): Response - { - return $this->_response ?? $this->generateDefaultResponse($params); - } - - /** - * Set response object the transport returns. - * - * @return $this - */ - public function setResponse(Response $response): NullTransport - { - $this->_response = $response; - - return $this; - } - - /** - * Generate an example response object. - * - * @param array $params Hostname, port, path, ... - * - * @return Response $response - */ - public function generateDefaultResponse(array $params): Response - { - $response = [ - 'took' => 0, - 'timed_out' => false, - '_shards' => [ - 'total' => 0, - 'successful' => 0, - 'failed' => 0, - ], - 'hits' => [ - 'total' => [ - 'value' => 0, - ], - 'max_score' => null, - 'hits' => [], - ], - 'params' => $params, - ]; - - return new Response(JSON::stringify($response)); - } - - /** - * Null transport. - * - * @param array $params Hostname, port, path, ... - * - * @return Response Response empty object - */ - public function exec(Request $request, array $params): Response - { - return $this->getResponse($params); - } -} diff --git a/src/Util.php b/src/Util.php index 81214b4a61..7e23ac5c28 100644 --- a/src/Util.php +++ b/src/Util.php @@ -201,30 +201,4 @@ public static function convertDateTimeObject(\DateTime $dateTime, bool $includeT return $dateTime->format($formatString); } - - /** - * Converts Request to Curl console command. - * - * @return string - */ - public static function convertRequestToCurlCommand(Request $request) - { - $message = 'curl -X'.\strtoupper($request->getMethod()).' '; - $message .= '\'http://'.$request->getConnection()->getHost().':'.$request->getConnection()->getPort().'/'; - $message .= $request->getPath(); - - $query = $request->getQuery(); - if (!empty($query)) { - $message .= '?'.\http_build_query($query); - } - - $message .= '\''; - - $data = $request->getData(); - if (!empty($data)) { - $message .= ' -d \''.JSON::stringify($data).'\''; - } - - return $message; - } } diff --git a/tests/Aggregation/DateRangeTest.php b/tests/Aggregation/DateRangeTest.php index e8755f5ade..846405061c 100644 --- a/tests/Aggregation/DateRangeTest.php +++ b/tests/Aggregation/DateRangeTest.php @@ -2,9 +2,9 @@ namespace Elastica\Test\Aggregation; +use Elastic\Elasticsearch\Exception\ClientResponseException; use Elastica\Aggregation\DateRange; use Elastica\Document; -use Elastica\Exception\ResponseException; use Elastica\Index; use Elastica\Mapping; use Elastica\Query; @@ -133,8 +133,8 @@ public function testDateRangeSetFormatAccordingToFormatTargetField(): void try { $this->_getIndexForTest()->search($query)->getAggregation('date'); $this->fail('Should throw exception to and from parameters in date_range aggregation are interpreted according of the target field'); - } catch (ResponseException $e) { - $error = $e->getResponse()->getFullError(); + } catch (ClientResponseException $e) { + $error = \json_decode($e->getResponse()->getBody(), true)['error'] ?? null; $this->assertSame('search_phase_execution_exception', $error['type']); $this->assertStringStartsWith('failed to parse date field', $error['root_cause'][0]['reason']); diff --git a/tests/Aggregation/PercentilesTest.php b/tests/Aggregation/PercentilesTest.php index 80c858cf4e..5fd7c0f242 100644 --- a/tests/Aggregation/PercentilesTest.php +++ b/tests/Aggregation/PercentilesTest.php @@ -144,9 +144,16 @@ public function testActualWork(): void ]); $index->refresh(); + // execute + $agg = (new Percentiles('price_percentile', 'price')); + + if (isset($_SERVER['ES_VERSION']) && \version_compare($_SERVER['ES_VERSION'], '8.9.0', '>=')) { + $agg->setParam('tdigest', ['execution_hint' => 'high_accuracy']); + } + // execute $query = new Query(); - $query->addAggregation(new Percentiles('price_percentile', 'price')); + $query->addAggregation($agg); $resultSet = $index->search($query); $aggResult = $resultSet->getAggregation('price_percentile'); @@ -219,6 +226,10 @@ public function testKeyed(): void ->setKeyed(false) ; + if (isset($_SERVER['ES_VERSION']) && \version_compare($_SERVER['ES_VERSION'], '8.9.0', '>=')) { + $agg->setParam('tdigest', ['execution_hint' => 'high_accuracy']); + } + $query = new Query(); $query->addAggregation($agg); diff --git a/tests/Base.php b/tests/Base.php index d0969af424..e9337ce157 100644 --- a/tests/Base.php +++ b/tests/Base.php @@ -5,8 +5,6 @@ use Elastica\Client; use Elastica\Connection; use Elastica\Index; -use Elasticsearch\Endpoints\Ingest\Pipeline\Put; -use Elasticsearch\Endpoints\Ingest\PutPipeline; use PHPUnit\Framework\TestCase; use PHPUnit\Util\Test as TestUtil; use Psr\Log\LoggerInterface; @@ -107,10 +105,7 @@ protected function _createRenamePipeline(): void { $client = $this->_getClient(); - // TODO: Use only PutPipeline when dropping support for elasticsearch/elasticsearch 7.x - $endpoint = \class_exists(PutPipeline::class) ? new PutPipeline() : new Put(); - $endpoint->setID('renaming'); - $endpoint->setBody([ + $body = [ 'description' => 'Rename field', 'processors' => [ [ @@ -120,9 +115,9 @@ protected function _createRenamePipeline(): void ], ], ], - ]); + ]; - $client->requestEndpoint($endpoint); + $client->ingest()->putPipeline(['id' => 'renaming', 'body' => $body]); } protected function _checkPlugin($plugin): void @@ -135,14 +130,14 @@ protected function _checkPlugin($plugin): void protected function _getVersion() { - $data = $this->_getClient()->request('/')->getData(); + $data = $this->_getClient()->info()->asArray(); return \substr($data['version']['number'], 0, 1); } protected function _checkVersion($version): void { - $data = $this->_getClient()->request('/')->getData(); + $data = $this->_getClient()->info()->asArray(); $installedVersion = $data['version']['number']; if (\version_compare($installedVersion, $version) < 0) { diff --git a/tests/Bulk/ResponseSetTest.php b/tests/Bulk/ResponseSetTest.php index f7310dddc4..56fba86240 100644 --- a/tests/Bulk/ResponseSetTest.php +++ b/tests/Bulk/ResponseSetTest.php @@ -26,11 +26,6 @@ public function testConstructor(): void $responseSet = $this->_createResponseSet($responseData, $actions); $this->assertEquals(200, $responseSet->getStatus()); - $this->assertEquals(12.3, $responseSet->getQueryTime()); - $this->assertEquals([ - 'url' => 'http://127.0.0.1:9200/_bulk', - 'http_code' => 200, - ], $responseSet->getTransferInfo()); } /** @@ -147,14 +142,9 @@ protected function _createResponseSet(array $responseData, array $actions): Resp $client = $this->createMock(Client::class); $response = new Response($responseData, 200); - $response->setQueryTime(12.3); - $response->setTransferInfo([ - 'url' => 'http://127.0.0.1:9200/_bulk', - 'http_code' => 200, - ]); $client->expects($this->once()) - ->method('request') + ->method('baseBulk') ->withAnyParameters() ->willReturn($response) ; diff --git a/tests/BulkTest.php b/tests/BulkTest.php index 67ffd50fbd..17cbab9cfe 100644 --- a/tests/BulkTest.php +++ b/tests/BulkTest.php @@ -755,7 +755,7 @@ public function testMemoryUsage(): void $endMemory = \memory_get_usage(); - $this->assertLessThan(1.31, $endMemory / $startMemory); + $this->assertLessThan(1.41, $endMemory / $startMemory); } /** @@ -781,7 +781,7 @@ public function testSendRequestEntityTooLargeExceptionIf413Response(): void /** @var Client|MockObject $clientMock */ $clientMock = $this->createMock(Client::class); $clientMock - ->method('request') + ->method('baseBulk') ->willReturn($response) ; diff --git a/tests/ClientConfigurationTest.php b/tests/ClientConfigurationTest.php index 6ef2b5841e..a648a80647 100644 --- a/tests/ClientConfigurationTest.php +++ b/tests/ClientConfigurationTest.php @@ -38,17 +38,12 @@ public function testFromSimpleDsn(): void 'port' => 9201, 'path' => null, 'url' => null, - 'proxy' => null, - 'transport' => null, - 'persistent' => true, - 'timeout' => null, 'connections' => [], 'roundRobin' => false, 'retryOnConflict' => 0, - 'bigintConversion' => false, 'username' => null, 'password' => null, - 'auth_type' => null, + 'transport_config' => [], ]; $this->assertEquals($expected, $configuration->getAll()); @@ -56,24 +51,19 @@ public function testFromSimpleDsn(): void public function testFromDsnWithParameters(): void { - $configuration = ClientConfiguration::fromDsn('https://user:p4ss@foo.com:9201/my-path?proxy=https://proxy.com&persistent=false&timeout=45&roundRobin=true&retryOnConflict=2&bigintConversion=true&extra=abc'); + $configuration = ClientConfiguration::fromDsn('https://user:p4ss@foo.com:9201/my-path?roundRobin=true&retryOnConflict=2&extra=abc'); $expected = [ 'host' => 'foo.com', 'port' => 9201, 'path' => '/my-path', 'url' => null, - 'proxy' => 'https://proxy.com', - 'transport' => 'https', - 'persistent' => false, - 'timeout' => 45, 'connections' => [], 'roundRobin' => true, 'retryOnConflict' => 2, - 'bigintConversion' => true, 'username' => 'user', 'password' => 'p4ss', - 'auth_type' => 'basic', 'extra' => 'abc', + 'transport_config' => [], ]; $this->assertEquals($expected, $configuration->getAll()); @@ -87,21 +77,16 @@ public function testFromDsnWithPool(): void 'port' => null, 'path' => null, 'url' => null, - 'proxy' => null, - 'transport' => null, - 'persistent' => true, - 'timeout' => null, 'connections' => [ - ['host' => '127.0.0.1', 'transport' => 'http', 'username' => 'nicolas'], - ['host' => '127.0.0.2', 'path' => '/bar', 'transport' => 'http', 'timeout' => 4], + ['host' => '127.0.0.1', 'username' => 'nicolas'], + ['host' => '127.0.0.2', 'path' => '/bar', 'timeout' => 4], ], 'roundRobin' => false, 'retryOnConflict' => 0, - 'bigintConversion' => false, 'username' => 'tobias', 'password' => null, - 'auth_type' => null, 'extra' => 'abc', + 'transport_config' => [], ]; $this->assertEquals($expected, $configuration->getAll()); @@ -116,17 +101,12 @@ public function testFromEmptyArray(): void 'port' => null, 'path' => null, 'url' => null, - 'proxy' => null, - 'transport' => null, - 'persistent' => true, - 'timeout' => null, 'connections' => [], // host, port, path, timeout, transport, compression, persistent, timeout, username, password, config -> (curl, headers, url) 'roundRobin' => false, 'retryOnConflict' => 0, - 'bigintConversion' => false, 'username' => null, 'password' => null, - 'auth_type' => null, + 'transport_config' => [], ]; $this->assertEquals($expected, $configuration->getAll()); @@ -144,17 +124,12 @@ public function testFromArray(): void 'port' => null, 'path' => null, 'url' => null, - 'proxy' => null, - 'transport' => null, - 'persistent' => true, - 'timeout' => null, 'connections' => [], // host, port, path, timeout, transport, compression, persistent, timeout, username, password, config -> (curl, headers, url) 'roundRobin' => false, 'retryOnConflict' => 0, - 'bigintConversion' => false, 'username' => 'Jdoe', 'password' => null, - 'auth_type' => null, + 'transport_config' => [], 'extra' => 'abc', ]; @@ -171,24 +146,18 @@ public function testHas(): void public function testGet(): void { $configuration = new ClientConfiguration(); - $this->assertTrue($configuration->get('persistent')); $expected = [ 'host' => null, 'port' => null, 'path' => null, 'url' => null, - 'proxy' => null, - 'transport' => null, - 'persistent' => true, - 'timeout' => null, 'connections' => [], 'roundRobin' => false, 'retryOnConflict' => 0, - 'bigintConversion' => false, 'username' => null, 'password' => null, - 'auth_type' => null, + 'transport_config' => [], ]; $this->assertEquals($expected, $configuration->get('')); diff --git a/tests/ClientFunctionalTest.php b/tests/ClientFunctionalTest.php index 712f67de7b..ddc2c82677 100644 --- a/tests/ClientFunctionalTest.php +++ b/tests/ClientFunctionalTest.php @@ -2,22 +2,21 @@ namespace Elastica\Test; +use Elastic\Elasticsearch\Response\Elasticsearch; +use Elastic\Elasticsearch\Transport\Adapter\AdapterOptions; +use Elastic\Transport\Exception\NoNodeAvailableException; +use Elastic\Transport\TransportBuilder; use Elastica\Bulk; use Elastica\Bulk\ResponseSet; use Elastica\Client; use Elastica\Connection; use Elastica\Document; -use Elastica\Exception\Connection\HttpException; -use Elastica\Exception\ConnectionException; use Elastica\Exception\NotFoundException; -use Elastica\Request; -use Elastica\Response; use Elastica\Script\Script; use Elastica\Test\Base as BaseTest; -use Elasticsearch\Endpoints\Indices\Stats; -use Elasticsearch\Endpoints\Search; -use PHPUnit\Framework\MockObject\MockObject; -use Psr\Log\LoggerInterface; +use GuzzleHttp\RequestOptions; +use Psr\Http\Client\ClientInterface as HttpClientInterface; +use Psr\Http\Message\RequestInterface; /** * @group functional @@ -28,7 +27,7 @@ class ClientFunctionalTest extends BaseTest { public function testConnectionErrors(): void { - $this->expectException(HttpException::class); + $this->expectException(NoNodeAvailableException::class); $client = $this->_getClient(['host' => 'foo.bar', 'port' => '9201']); $client->getVersion(); @@ -36,7 +35,7 @@ public function testConnectionErrors(): void public function testClientBadHost(): void { - $this->expectException(HttpException::class); + $this->expectException(NoNodeAvailableException::class); $client = $this->_getClient(['host' => 'localhost', 'port' => '9201']); $client->getVersion(); @@ -44,7 +43,7 @@ public function testClientBadHost(): void public function testClientBadHostWithTimeout(): void { - $this->expectException(HttpException::class); + $this->expectException(NoNodeAvailableException::class); $client = $this->_getClient(['host' => 'foo.bar', 'timeout' => 10]); $client->getVersion(); @@ -362,13 +361,30 @@ public function testOneInvalidConnection(): void { $client = $this->_getClient(); + $httpClientOptions = [ + RequestOptions::TIMEOUT => 1, + RequestOptions::CONNECT_TIMEOUT => 1, + ]; + + $transportConnectionBuilder1 = TransportBuilder::create(); + $transportConnectionBuilder1->setHosts([$this->_getHost().':9100']); + $transportConnectionBuilder1->setClient( + $this->setHttpClientOptions($transportConnectionBuilder1->getClient(), [], $httpClientOptions) + ); + + $transportConnectionBuilder2 = TransportBuilder::create(); + $transportConnectionBuilder2->setHosts([$this->_getHost().':9200']); + $transportConnectionBuilder2->setClient( + $this->setHttpClientOptions($transportConnectionBuilder1->getClient(), [], $httpClientOptions) + ); + // First connection work, second should not work - $connection1 = new Connection(['port' => '9100', 'timeout' => 2, 'host' => $this->_getHost()]); - $connection2 = new Connection(['port' => '9200', 'timeout' => 2, 'host' => $this->_getHost()]); + $connection1 = new Connection(['port' => '9100', 'timeout' => 2, 'host' => $this->_getHost(), 'transport' => $transportConnectionBuilder1->build()]); + $connection2 = new Connection(['port' => '9200', 'timeout' => 2, 'host' => $this->_getHost(), 'transport' => $transportConnectionBuilder2->build()]); $client->setConnections([$connection1, $connection2]); - $client->request('_stats'); + $client->indices()->stats(); $connections = $client->getConnections(); @@ -383,16 +399,33 @@ public function testTwoInvalidConnection(): void { $client = $this->_getClient(); + $httpClientOptions = [ + RequestOptions::TIMEOUT => 1, + RequestOptions::CONNECT_TIMEOUT => 1, + ]; + + $transportConnectionBuilder1 = TransportBuilder::create(); + $transportConnectionBuilder1->setHosts([$this->_getHost().':9101']); + $transportConnectionBuilder1->setClient( + $this->setHttpClientOptions($transportConnectionBuilder1->getClient(), [], $httpClientOptions) + ); + + $transportConnectionBuilder2 = TransportBuilder::create(); + $transportConnectionBuilder2->setHosts([$this->_getHost().':9102']); + $transportConnectionBuilder2->setClient( + $this->setHttpClientOptions($transportConnectionBuilder1->getClient(), [], $httpClientOptions) + ); + // First connection work, second should not work - $connection1 = new Connection(['port' => '9101', 'timeout' => 2]); - $connection2 = new Connection(['port' => '9102', 'timeout' => 2]); + $connection1 = new Connection(['host' => $this->_getHost(), 'port' => '9101', 'timeout' => 2, 'transport' => $transportConnectionBuilder1->build()]); + $connection2 = new Connection(['host' => $this->_getHost(), 'port' => '9102', 'timeout' => 2, 'transport' => $transportConnectionBuilder2->build()]); $client->setConnections([$connection1, $connection2]); try { - $client->request('_stats'); + $client->indices()->stats(); $this->fail('Should throw exception as no connection valid'); - } catch (HttpException $e) { + } catch (NoNodeAvailableException $e) { } $connections = $client->getConnections(); @@ -401,7 +434,7 @@ public function testTwoInvalidConnection(): void $this->assertCount(2, $connections); // One connection has to be disabled - $this->assertTrue(false === $connections[0]->isEnabled() || false === $connections[1]->isEnabled()); + $this->assertTrue(false === $connections[0]->isEnabled() && false === $connections[1]->isEnabled()); } /** @@ -411,10 +444,27 @@ public function testCallback(): void { $count = 0; + $httpClientOptions = [ + RequestOptions::TIMEOUT => 1, + RequestOptions::CONNECT_TIMEOUT => 1, + ]; + + $transportConnectionBuilder1 = TransportBuilder::create(); + $transportConnectionBuilder1->setHosts([$this->_getHost().':9101']); + $transportConnectionBuilder1->setClient( + $this->setHttpClientOptions($transportConnectionBuilder1->getClient(), [], $httpClientOptions) + ); + + $transportConnectionBuilder2 = TransportBuilder::create(); + $transportConnectionBuilder2->setHosts([$this->_getHost().':9102']); + $transportConnectionBuilder2->setClient( + $this->setHttpClientOptions($transportConnectionBuilder1->getClient(), [], $httpClientOptions) + ); + // Callback function which verifies that disabled connection objects are returned $callback = function (Connection $connection, \Exception $exception, Client $client) use (&$count): void { $this->assertInstanceOf(Connection::class, $connection); - $this->assertInstanceOf(ConnectionException::class, $exception); + $this->assertInstanceOf(NoNodeAvailableException::class, $exception); $this->assertInstanceOf(Client::class, $client); $this->assertFalse($connection->isEnabled()); ++$count; @@ -423,17 +473,17 @@ public function testCallback(): void $client = $this->_getClient([], $callback); // First connection work, second should not work - $connection1 = new Connection(['port' => '9101', 'timeout' => 2]); - $connection2 = new Connection(['port' => '9102', 'timeout' => 2]); + $connection1 = new Connection(['port' => '9101', 'timeout' => 2, 'transport' => $transportConnectionBuilder1->build()]); + $connection2 = new Connection(['port' => '9102', 'timeout' => 2, 'transport' => $transportConnectionBuilder2->build()]); $client->setConnections([$connection1, $connection2]); $this->assertEquals(0, $count); try { - $client->request('_stats'); + $client->indices()->stats(); $this->fail('Should throw exception as no connection valid'); - } catch (HttpException $e) { + } catch (NoNodeAvailableException $e) { $this->assertTrue(true); } @@ -448,7 +498,7 @@ public function testUrlConstructor(): void // Url should overwrite invalid host $client = $this->_getClient(['url' => $url, 'port' => '9101', 'timeout' => 2]); - $response = $client->request('_stats'); + $response = $client->toElasticaResponse($client->indices()->stats()); $this->assertTrue($response->isOk()); } @@ -719,15 +769,15 @@ public function testDeleteDocumentsWithRequestParameters(): void public function testLastRequestResponse(): void { $client = $this->_getClient(); - $response = $client->request('_stats'); + $response = $client->indices()->stats(); $lastRequest = $client->getLastRequest(); - $this->assertInstanceOf(Request::class, $lastRequest); - $this->assertEquals('_stats', $lastRequest->getPath()); + $this->assertInstanceOf(RequestInterface::class, $lastRequest); + $this->assertEquals('/_stats', $lastRequest->getUri()->getPath()); $lastResponse = $client->getLastResponse(); - $this->assertInstanceOf(Response::class, $lastResponse); + $this->assertInstanceOf(Elasticsearch::class, $lastResponse); $this->assertSame($response, $lastResponse); } @@ -849,10 +899,8 @@ public function testArrayQuery(): void ], ]; - $path = $index->getName().'/_search'; - - $response = $client->request($path, Request::GET, $query); - $responseArray = $response->getData(); + $response = $client->search(['body' => $query]); + $responseArray = $response->asArray(); $this->assertEquals(1, $responseArray['hits']['total']['value']); } @@ -872,59 +920,12 @@ public function testJSONQuery(): void $path = $index->getName().'/_search'; - $response = $client->request($path, Request::GET, $query); - $responseArray = $response->getData(); + $response = $client->search(['body' => $query]); + $responseArray = $response->asArray(); $this->assertEquals(1, $responseArray['hits']['total']['value']); } - public function testLogger(): void - { - /** @var LoggerInterface&MockObject $logger */ - $logger = $this->createMock(LoggerInterface::class); - $client = $this->_getClient([], null, $logger); - - $logger->expects($this->once()) - ->method('debug') - ->with( - 'Elastica Request', - $this->logicalAnd( - $this->arrayHasKey('request'), - $this->arrayHasKey('response'), - $this->arrayHasKey('responseStatus') - ) - ) - ; - - $client->request('_stats'); - } - - public function testLoggerOnFailure(): void - { - $this->expectException(HttpException::class); - - /** @var LoggerInterface&MockObject $logger */ - $logger = $this->createMock(LoggerInterface::class); - $client = $this->_getClient(['connections' => [ - ['host' => $this->_getHost(), 'port' => 9201], - ]], null, $logger); - - $logger->expects($this->once()) - ->method('error') - ->with( - 'Elastica Request Failure', - $this->logicalAnd( - $this->arrayHasKey('exception'), - $this->arrayHasKey('request'), - $this->arrayHasKey('retry'), - $this->logicalNot($this->arrayHasKey('response')) - ) - ) - ; - - $client->request('_stats'); - } - public function testDateMathEscapingWithMixedRequestTypes(): void { $client = $this->_getClient(); @@ -952,20 +953,6 @@ public function testDateMathEscapingWithMixedRequestTypes(): void $bulk->send(); } - public function testDateMathEscapingWithEscapedPath(): void - { - $client = $this->_getClient(); - - $now = new \DateTime(); - - // e.g. test-2018.01.01 - $staticIndex = $client->getIndex('test-'.$now->format('Y.m.d')); - $staticIndex->create(); - - // It should not double escape the index name, since it came already escaped. - $client->request('/_refresh'); - } - public function testEndpointParamsRequest(): void { $index = $this->_createIndex(); @@ -977,16 +964,13 @@ public function testEndpointParamsRequest(): void $index->refresh(); - $endpoint = new Stats(); - $endpoint->setIndex($index->getName()); - $endpoint->setMetric('indexing'); - $response = $client->requestEndpoint($endpoint); + $response = $client->indices()->stats(['index' => $index->getName(), 'metric' => ['indexing']]); - $this->assertArrayHasKey('index_total', $response->getData()['indices'][$index->getName()]['total']['indexing']); + $this->assertArrayHasKey('index_total', $response->asArray()['indices'][$index->getName()]['total']['indexing']); $this->assertSame( 2, - $response->getData()['indices'][$index->getName()]['total']['indexing']['index_total'] + $response->asArray()['indices'][$index->getName()]['total']['indexing']['index_total'] ); } @@ -1015,12 +999,9 @@ public function testEndpointQueryRequest($query, $totalHits): void ], ]; - $endpoint = new Search(); - $endpoint->setIndex($index->getName()); - $endpoint->setBody($query); + $response = $client->search(['index' => $index->getName(), 'body' => $query]); - $response = $client->requestEndpoint($endpoint); - $responseArray = $response->getData(); + $responseArray = $response->asArray(); $this->assertEquals($totalHits, $responseArray['hits']['total']['value']); } @@ -1032,4 +1013,17 @@ public function endpointQueryRequestDataProvider(): array ['ruflin2', 0], ]; } + + protected function setHttpClientOptions(HttpClientInterface $client, array $config, array $clientOptions = []): HttpClientInterface + { + if (empty($config) && empty($clientOptions)) { + return $client; + } + $class = \get_class($client); + $adapterClass = AdapterOptions::HTTP_ADAPTERS[$class]; + + $adapter = new $adapterClass(); + + return $adapter->setConfig($client, $config, $clientOptions); + } } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index fde5378394..2d2685335d 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -2,6 +2,7 @@ namespace Elastica\Test; +use Elastic\Transport\Transport; use Elastica\Client; use Elastica\Connection; use Elastica\Exception\InvalidException; @@ -22,7 +23,7 @@ public function testConstruct(): void public function testConstructWithDsn(): void { - $client = new Client('https://user:p4ss@foo.com:9200?persistent=false&retryOnConflict=2'); + $client = new Client('https://user:p4ss@foo.com:9200?retryOnConflict=2'); $this->assertCount(1, $client->getConnections()); $expected = [ @@ -30,18 +31,13 @@ public function testConstructWithDsn(): void 'port' => 9200, 'path' => null, 'url' => null, - 'proxy' => null, - 'transport' => 'https', - 'persistent' => false, - 'timeout' => null, 'connections' => [], 'roundRobin' => false, 'retryOnConflict' => 2, - 'bigintConversion' => false, 'username' => 'user', 'password' => 'p4ss', - 'auth_type' => 'basic', 'connectionStrategy' => 'Simple', + 'transport_config' => [], ]; $this->assertEquals($expected, $client->getConfig()); @@ -148,4 +144,80 @@ public function testClientConnectWithConfigSetByMethod(): void $this->assertEquals($this->_getHost(), $connection->getHost()); $this->assertEquals($this->_getPort(), $connection->getPort()); } + + public function testGetAsync(): void + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Not supported'); + + $client = $this->_getClient(); + $client->getAsync(); + } + + public function testSetElasticMetaHeader(): void + { + $client = $this->_getClient(); + $client->setElasticMetaHeader(true); + + $this->assertTrue($client->getElasticMetaHeader()); + } + + public function testGetTransport(): void + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Not supported'); + + $client = $this->_getClient(); + $client->getTransport(); + } + + public function testSetAsync(): void + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Not supported'); + + $client = $this->_getClient(); + $client->setAsync(true); + } + + public function testSetResponseException(): void + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Not supported'); + + $client = $this->_getClient(); + $client->setResponseException(true); + } + + public function testGetResponseException(): void + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Not supported'); + + $client = $this->_getClient(); + $client->getResponseException(); + } + + public function testClientConnectionWithCloudId(): void + { + $client = new Client([ + 'host' => 'foo.com', + 'port' => 9200, + 'path' => null, + 'url' => null, + 'cloud_id' => 'Test:ZXUtY2VudHJhbC0xLmF3cy5jbG91ZC5lcy5pbyQ0ZGU0NmNlZDhkOGQ0NTk2OTZlNTQ0ZmU1ZjMyYjk5OSRlY2I0YTJlZmY0OTA0ZDliOTE5NzMzMmQwOWNjOTY5Ng==', + 'connections' => [], + 'roundRobin' => false, + 'retryOnConflict' => 2, + 'username' => 'user', + 'password' => 'p4ss', + 'connectionStrategy' => 'Simple', + 'transport_config' => [], + ]); + $transport = $client->getConnection()->getTransportObject(); + $node = $transport->getNodePool()->nextNode(); + + $this->assertInstanceOf(Transport::class, $transport); + $this->assertEquals('4de46ced8d8d459696e544fe5f32b999.eu-central-1.aws.cloud.es.io', $node->getUri()->getHost()); + } } diff --git a/tests/Cluster/SettingsTest.php b/tests/Cluster/SettingsTest.php index 622ce2bdd0..a9c00f4b0b 100644 --- a/tests/Cluster/SettingsTest.php +++ b/tests/Cluster/SettingsTest.php @@ -2,9 +2,9 @@ namespace Elastica\Test\Cluster; +use Elastic\Elasticsearch\Exception\ClientResponseException; use Elastica\Cluster\Settings; use Elastica\Document; -use Elastica\Exception\ResponseException; use Elastica\Test\Base as BaseTest; /** @@ -12,58 +12,6 @@ */ class SettingsTest extends BaseTest { - /** - * @group functional - */ - public function testSetTransient(): void - { - if (\version_compare($_SERVER['ES_VERSION'], '7.0.0', '>=')) { - $this->markTestSkipped('discovery.zen.minimum_master_nodes is deprecated, ignored in 7.x and removed in 8.x, see: https://www.elastic.co/guide/en/elasticsearch/reference/master/migrating-8.0.html#breaking-changes-8.0'); - } - - $index = $this->_createIndex(); - - if (\count($index->getClient()->getCluster()->getNodes()) < 2) { - $this->markTestSkipped('At least two master nodes have to be running for this test'); - } - - $settings = new Settings($index->getClient()); - - $settings->setTransient('discovery.zen.minimum_master_nodes', 2); - $data = $settings->get(); - $this->assertEquals(2, $data['transient']['discovery']['zen']['minimum_master_nodes']); - - $settings->setTransient('discovery.zen.minimum_master_nodes', 1); - $data = $settings->get(); - $this->assertEquals(1, $data['transient']['discovery']['zen']['minimum_master_nodes']); - } - - /** - * @group functional - */ - public function testSetPersistent(): void - { - if (\version_compare($_SERVER['ES_VERSION'], '7.0.0', '>=')) { - $this->markTestSkipped('discovery.zen.minimum_master_nodes is deprecated, ignored in 7.x and removed in 8.x, see: https://www.elastic.co/guide/en/elasticsearch/reference/master/migrating-8.0.html#breaking-changes-8.0'); - } - - $index = $this->_createIndex(); - - if (\count($index->getClient()->getCluster()->getNodes()) < 2) { - $this->markTestSkipped('At least two master nodes have to be running for this test'); - } - - $settings = new Settings($index->getClient()); - - $settings->setPersistent('discovery.zen.minimum_master_nodes', 2); - $data = $settings->get(); - $this->assertEquals(2, $data['persistent']['discovery']['zen']['minimum_master_nodes']); - - $settings->setPersistent('discovery.zen.minimum_master_nodes', 1); - $data = $settings->get(); - $this->assertEquals(1, $data['persistent']['discovery']['zen']['minimum_master_nodes']); - } - /** * @group functional */ @@ -89,8 +37,9 @@ public function testSetReadOnly(): void try { $index->addDocument($doc2); $this->fail('should throw read only exception'); - } catch (ResponseException $e) { - $error = $e->getResponse()->getFullError(); + } catch (ClientResponseException $e) { + $error = \json_decode($e->getResponse()->getBody(), true)['error'] ?? null; + $this->assertSame('cluster_block_exception', $error['type']); $this->assertStringContainsString('cluster read-only', $error['reason']); } diff --git a/tests/ClusterTest.php b/tests/ClusterTest.php index 66cdd528be..88d6beabed 100644 --- a/tests/ClusterTest.php +++ b/tests/ClusterTest.php @@ -18,7 +18,7 @@ public function testGetNodeNames(): void $client = $this->_getClient(); $cluster = new Cluster($client); - $data = $client->request('_nodes')->getData(); + $data = $client->nodes()->info()->asArray(); $rawNodes = $data['nodes']; $expectedNodeNames = []; diff --git a/tests/Connection/Strategy/CallbackStrategyTest.php b/tests/Connection/Strategy/CallbackStrategyTest.php index 43c77bbf05..3690bb7f47 100644 --- a/tests/Connection/Strategy/CallbackStrategyTest.php +++ b/tests/Connection/Strategy/CallbackStrategyTest.php @@ -49,7 +49,7 @@ public function testConnection(): void }]; $client = $this->_getClient($config); - $response = $client->request('_aliases'); + $response = $client->toElasticaResponse($client->indices()->getAlias()); $this->assertEquals(1, $count); diff --git a/tests/Connection/Strategy/RoundRobinTest.php b/tests/Connection/Strategy/RoundRobinTest.php index 49ee106ed7..64e7c0f61f 100644 --- a/tests/Connection/Strategy/RoundRobinTest.php +++ b/tests/Connection/Strategy/RoundRobinTest.php @@ -2,12 +2,17 @@ namespace Elastica\Test\Connection\Strategy; +use Elastic\Elasticsearch\Response\Elasticsearch; +use Elastic\Elasticsearch\Transport\Adapter\AdapterOptions; +use Elastic\Transport\Exception\NoNodeAvailableException; +use Elastic\Transport\TransportBuilder; use Elastica\Client; use Elastica\Connection; use Elastica\Connection\Strategy\RoundRobin; -use Elastica\Exception\ConnectionException; -use Elastica\Response; +use Elastica\ResponseConverter; use Elastica\Test\Base; +use GuzzleHttp\RequestOptions; +use Psr\Http\Client\ClientInterface as HttpClientInterface; /** * Description of RoundRobinTest. @@ -30,7 +35,7 @@ public function testConnection(): void { $config = ['connectionStrategy' => 'RoundRobin']; $client = $this->_getClient($config); - $response = $client->request('_aliases'); + $response = $client->indices()->getAlias(); $this->_checkResponse($response); @@ -53,14 +58,24 @@ public function testOldStrategySet(): void */ public function testFailConnection(): void { - $this->expectException(ConnectionException::class); - - $config = ['connectionStrategy' => 'RoundRobin', 'host' => '255.255.255.0', 'timeout' => $this->_timeout]; + $this->expectException(NoNodeAvailableException::class); + + $config = [ + 'connectionStrategy' => 'RoundRobin', + 'host' => '255.255.255.0', + 'timeout' => $this->_timeout, + 'transport_config' => [ + 'http_client_options' => [ + RequestOptions::TIMEOUT => 1, + RequestOptions::CONNECT_TIMEOUT => 1, + ], + ], + ]; $client = $this->_getClient($config); $this->_checkStrategy($client); - $client->request('_aliases'); + $client->indices()->getAlias(); } /** @@ -68,9 +83,26 @@ public function testFailConnection(): void */ public function testWithOneFailConnection(): void { + $httpClientOptions = [ + RequestOptions::TIMEOUT => 1, + RequestOptions::CONNECT_TIMEOUT => 1, + ]; + + $transportConnectionBuilder1 = TransportBuilder::create(); + $transportConnectionBuilder1->setHosts(['255.255.255.0']); + $transportConnectionBuilder1->setClient( + $this->setHttpClientOptions($transportConnectionBuilder1->getClient(), [], $httpClientOptions) + ); + + $transportConnectionBuilder2 = TransportBuilder::create(); + $transportConnectionBuilder2->setHosts([$this->_getHost().':'.$this->_getPort()]); + $transportConnectionBuilder2->setClient( + $this->setHttpClientOptions($transportConnectionBuilder1->getClient(), [], $httpClientOptions) + ); + $connections = [ - new Connection(['host' => '255.255.255.0', 'timeout' => $this->_timeout]), - new Connection(['host' => $this->_getHost(), 'timeout' => $this->_timeout]), + new Connection(['host' => '255.255.255.0', 'timeout' => $this->_timeout, 'transport' => $transportConnectionBuilder1->build()]), + new Connection(['host' => $this->_getHost(), 'timeout' => $this->_timeout, 'transport' => $transportConnectionBuilder2->build()]), ]; $count = 0; @@ -81,7 +113,7 @@ public function testWithOneFailConnection(): void $client = $this->_getClient(['connectionStrategy' => 'RoundRobin'], $callback); $client->setConnections($connections); - $response = $client->request('_aliases'); + $response = $client->indices()->getAlias(); $this->_checkResponse($response); @@ -95,10 +127,46 @@ public function testWithOneFailConnection(): void */ public function testWithNoValidConnection(): void { + $httpClientOptions = [ + RequestOptions::TIMEOUT => 1, + RequestOptions::CONNECT_TIMEOUT => 1, + ]; + + $transportConnectionBuilder1 = TransportBuilder::create(); + $transportConnectionBuilder1->setHosts(['255.255.255.0']); + $transportConnectionBuilder1->setClient( + $this->setHttpClientOptions($transportConnectionBuilder1->getClient(), [], $httpClientOptions) + ); + + $transportConnectionBuilder2 = TransportBuilder::create(); + $transportConnectionBuilder2->setHosts(['45.45.45.45:80']); + $transportConnectionBuilder2->setClient( + $this->setHttpClientOptions($transportConnectionBuilder1->getClient(), [], $httpClientOptions) + ); + + $transportConnectionBuilder3 = TransportBuilder::create(); + $transportConnectionBuilder3->setHosts(['10.123.213.123']); + $transportConnectionBuilder3->setClient( + $this->setHttpClientOptions($transportConnectionBuilder1->getClient(), [], $httpClientOptions) + ); + $connections = [ - new Connection(['host' => '255.255.255.0', 'timeout' => $this->_timeout]), - new Connection(['host' => '45.45.45.45', 'port' => '80', 'timeout' => $this->_timeout]), - new Connection(['host' => '10.123.213.123', 'timeout' => $this->_timeout]), + new Connection([ + 'host' => '255.255.255.0', + 'timeout' => $this->_timeout, + 'transport' => $transportConnectionBuilder1->build(), + ]), + new Connection([ + 'host' => '45.45.45.45', + 'port' => '80', + 'timeout' => $this->_timeout, + 'transport' => $transportConnectionBuilder2->build(), + ]), + new Connection([ + 'host' => '10.123.213.123', + 'timeout' => $this->_timeout, + 'transport' => $transportConnectionBuilder3->build(), + ]), ]; $count = 0; @@ -109,9 +177,9 @@ public function testWithNoValidConnection(): void $client->setConnections($connections); try { - $client->request('_aliases'); + $client->indices()->getAlias(); $this->fail('Should throw exception as no connection valid'); - } catch (ConnectionException $e) { + } catch (NoNodeAvailableException $e) { $this->assertEquals(\count($connections), $count); $this->_checkStrategy($client); } @@ -124,8 +192,23 @@ protected function _checkStrategy(Client $client): void $this->assertInstanceOf(RoundRobin::class, $strategy); } - protected function _checkResponse(Response $response): void + protected function _checkResponse(Elasticsearch $response): void { - $this->assertTrue($response->isOk()); + $responseElastica = ResponseConverter::toElastica($response); + + $this->assertTrue($responseElastica->isOk()); + } + + protected function setHttpClientOptions(HttpClientInterface $client, array $config, array $clientOptions = []): HttpClientInterface + { + if (empty($config) && empty($clientOptions)) { + return $client; + } + $class = \get_class($client); + $adapterClass = AdapterOptions::HTTP_ADAPTERS[$class]; + + $adapter = new $adapterClass(); + + return $adapter->setConfig($client, $config, $clientOptions); } } diff --git a/tests/Connection/Strategy/SimpleTest.php b/tests/Connection/Strategy/SimpleTest.php index c260f4a208..c93e89adbe 100644 --- a/tests/Connection/Strategy/SimpleTest.php +++ b/tests/Connection/Strategy/SimpleTest.php @@ -2,12 +2,17 @@ namespace Elastica\Test\Connection\Strategy; +use Elastic\Elasticsearch\Response\Elasticsearch; +use Elastic\Elasticsearch\Transport\Adapter\AdapterOptions; +use Elastic\Transport\Exception\NoNodeAvailableException; +use Elastic\Transport\TransportBuilder; use Elastica\Client; use Elastica\Connection; use Elastica\Connection\Strategy\Simple; -use Elastica\Exception\ConnectionException; -use Elastica\Response; +use Elastica\ResponseConverter; use Elastica\Test\Base; +use GuzzleHttp\RequestOptions; +use Psr\Http\Client\ClientInterface as HttpClientInterface; /** * Description of SimplyTest. @@ -29,7 +34,7 @@ class SimpleTest extends Base public function testConnection(): void { $client = $this->_getClient(); - $response = $client->request('_aliases'); + $response = $client->indices()->getAlias(); $this->_checkResponse($response); @@ -41,14 +46,24 @@ public function testConnection(): void */ public function testFailConnection(): void { - $this->expectException(ConnectionException::class); - - $config = ['host' => '255.255.255.0', 'timeout' => $this->_timeout]; + $this->expectException(NoNodeAvailableException::class); + + $config = [ + 'connectionStrategy' => 'RoundRobin', + 'host' => '255.255.255.0', + 'timeout' => $this->_timeout, + 'transport_config' => [ + 'http_client_options' => [ + RequestOptions::TIMEOUT => 1, + RequestOptions::CONNECT_TIMEOUT => 1, + ], + ], + ]; $client = $this->_getClient($config); $this->_checkStrategy($client); - $client->request('_aliases'); + $client->indices()->getAlias(); } /** @@ -56,9 +71,26 @@ public function testFailConnection(): void */ public function testWithOneFailConnection(): void { + $httpClientOptions = [ + RequestOptions::TIMEOUT => 1, + RequestOptions::CONNECT_TIMEOUT => 1, + ]; + + $transportConnectionBuilder1 = TransportBuilder::create(); + $transportConnectionBuilder1->setHosts(['255.255.255.0']); + $transportConnectionBuilder1->setClient( + $this->setHttpClientOptions($transportConnectionBuilder1->getClient(), [], $httpClientOptions) + ); + + $transportConnectionBuilder2 = TransportBuilder::create(); + $transportConnectionBuilder2->setHosts([$this->_getHost().':'.$this->_getPort()]); + $transportConnectionBuilder2->setClient( + $this->setHttpClientOptions($transportConnectionBuilder1->getClient(), [], $httpClientOptions) + ); + $connections = [ - new Connection(['host' => '255.255.255.0', 'timeout' => $this->_timeout]), - new Connection(['host' => $this->_getHost(), 'timeout' => $this->_timeout]), + new Connection(['host' => '255.255.255.0', 'timeout' => $this->_timeout, 'transport' => $transportConnectionBuilder1->build()]), + new Connection(['host' => $this->_getHost(), 'timeout' => $this->_timeout, 'transport' => $transportConnectionBuilder2->build()]), ]; $count = 0; @@ -69,7 +101,7 @@ public function testWithOneFailConnection(): void $client = $this->_getClient([], $callback); $client->setConnections($connections); - $response = $client->request('_aliases'); + $response = $client->indices()->getAlias(); $this->_checkResponse($response); @@ -83,10 +115,46 @@ public function testWithOneFailConnection(): void */ public function testWithNoValidConnection(): void { + $httpClientOptions = [ + RequestOptions::TIMEOUT => 1, + RequestOptions::CONNECT_TIMEOUT => 1, + ]; + + $transportConnectionBuilder1 = TransportBuilder::create(); + $transportConnectionBuilder1->setHosts(['255.255.255.0']); + $transportConnectionBuilder1->setClient( + $this->setHttpClientOptions($transportConnectionBuilder1->getClient(), [], $httpClientOptions) + ); + + $transportConnectionBuilder2 = TransportBuilder::create(); + $transportConnectionBuilder2->setHosts(['45.45.45.45:80']); + $transportConnectionBuilder2->setClient( + $this->setHttpClientOptions($transportConnectionBuilder1->getClient(), [], $httpClientOptions) + ); + + $transportConnectionBuilder3 = TransportBuilder::create(); + $transportConnectionBuilder3->setHosts(['10.123.213.123']); + $transportConnectionBuilder3->setClient( + $this->setHttpClientOptions($transportConnectionBuilder1->getClient(), [], $httpClientOptions) + ); + $connections = [ - new Connection(['host' => '255.255.255.0', 'timeout' => $this->_timeout]), - new Connection(['host' => '45.45.45.45', 'port' => '80', 'timeout' => $this->_timeout]), - new Connection(['host' => '10.123.213.123', 'timeout' => $this->_timeout]), + new Connection([ + 'host' => '255.255.255.0', + 'timeout' => $this->_timeout, + 'transport' => $transportConnectionBuilder1->build(), + ]), + new Connection([ + 'host' => '45.45.45.45', + 'port' => '80', + 'timeout' => $this->_timeout, + 'transport' => $transportConnectionBuilder2->build(), + ]), + new Connection([ + 'host' => '10.123.213.123', + 'timeout' => $this->_timeout, + 'transport' => $transportConnectionBuilder3->build(), + ]), ]; $count = 0; @@ -97,9 +165,9 @@ public function testWithNoValidConnection(): void $client->setConnections($connections); try { - $client->request('_aliases'); + $client->indices()->getAlias(); $this->fail('Should throw exception as no connection valid'); - } catch (ConnectionException $e) { + } catch (NoNodeAvailableException $e) { $this->assertEquals(\count($connections), $count); } } @@ -111,8 +179,23 @@ protected function _checkStrategy(Client $client): void $this->assertInstanceOf(Simple::class, $strategy); } - protected function _checkResponse(Response $response): void + protected function _checkResponse(Elasticsearch $response): void { - $this->assertTrue($response->isOk()); + $responseElastica = ResponseConverter::toElastica($response); + + $this->assertTrue($responseElastica->isOk()); + } + + protected function setHttpClientOptions(HttpClientInterface $client, array $config, array $clientOptions = []): HttpClientInterface + { + if (empty($config) && empty($clientOptions)) { + return $client; + } + $class = \get_class($client); + $adapterClass = AdapterOptions::HTTP_ADAPTERS[$class]; + + $adapter = new $adapterClass(); + + return $adapter->setConfig($client, $config, $clientOptions); } } diff --git a/tests/ConnectionTest.php b/tests/ConnectionTest.php index 84120da618..0e8fb4a370 100644 --- a/tests/ConnectionTest.php +++ b/tests/ConnectionTest.php @@ -2,14 +2,12 @@ namespace Elastica\Test; +use Elastic\Transport\Transport; +use Elastic\Transport\TransportBuilder; use Elastica\Client; use Elastica\Connection; -use Elastica\Exception\ConnectionException; use Elastica\Exception\InvalidException; -use Elastica\Request; use Elastica\Test\Base as BaseTest; -use Elastica\Transport\AbstractTransport; -use Elastica\Transport\Http; /** * @internal @@ -24,10 +22,6 @@ public function testEmptyConstructor(): void $connection = new Connection(); $this->assertEquals(Connection::DEFAULT_HOST, $connection->getHost()); $this->assertEquals(Connection::DEFAULT_PORT, $connection->getPort()); - $this->assertEquals(Connection::DEFAULT_TRANSPORT, $connection->getTransport()); - $this->assertInstanceOf(AbstractTransport::class, $connection->getTransportObject()); - $this->assertEquals(Connection::TIMEOUT, $connection->getTimeout()); - $this->assertEquals(Connection::CONNECT_TIMEOUT, $connection->getConnectTimeout()); $this->assertEquals([], $connection->getConfig()); $this->assertTrue($connection->isEnabled()); } @@ -45,22 +39,6 @@ public function testEnabledDisable(): void $this->assertTrue($connection->isEnabled()); } - /** - * @group unit - */ - public function testInvalidConnection(): void - { - $this->expectException(ConnectionException::class); - - $connection = new Connection(['port' => 9999]); - - $request = new Request('_stats', Request::GET); - $request->setConnection($connection); - - // Throws exception because no valid connection - $request->send(); - } - /** * @group unit */ @@ -104,20 +82,11 @@ public function testGetConfig(): void */ public function testGetConfigWithArrayUsedForTransport(): void { - $connection = new Connection(['transport' => ['type' => 'Http']]); - $this->assertInstanceOf(Http::class, $connection->getTransportObject()); - } - - /** - * @group unit - */ - public function testGetInvalidConfigWithArrayUsedForTransport(): void - { - $this->expectException(InvalidException::class); - $this->expectExceptionMessage('Invalid transport'); + $transportConnectionBuilder = TransportBuilder::create(); + $transportConnectionBuilder->setHosts([$this->_getHost().':9101']); - $connection = new Connection(['transport' => ['type' => 'invalidtransport']]); - $connection->getTransportObject(); + $connection = new Connection(['transport' => $transportConnectionBuilder->build()]); + $this->assertInstanceOf(Transport::class, $connection->getTransportObject()); } /** diff --git a/tests/DocumentTest.php b/tests/DocumentTest.php index eddba2b17e..324105f4da 100644 --- a/tests/DocumentTest.php +++ b/tests/DocumentTest.php @@ -253,7 +253,7 @@ public function testDataPropertiesOverloading(): void } $this->assertEquals('changed1', $document->field1); - $this->assertObjectNotHasAttribute('field3', $document); + $this->assertFalse(\property_exists($document, 'field3')); $newData = $document->getData(); diff --git a/tests/Exception/Connection/GuzzleExceptionTest.php b/tests/Exception/Connection/GuzzleExceptionTest.php deleted file mode 100644 index 15b8f3d073..0000000000 --- a/tests/Exception/Connection/GuzzleExceptionTest.php +++ /dev/null @@ -1,18 +0,0 @@ -_getClient(); - $index = $client->getIndex('elastica_partial_failure'); - $index->create( - [ - 'settings' => [ - 'index' => [ - 'number_of_shards' => 5, - 'number_of_replicas' => 0, - ], - ], - ], - [ - 'recreate' => true, - ] - ); - - $index->addDocument(new Document('', ['name' => 'ruflin'])); - $index->addDocument(new Document('', ['name' => 'bobrik'])); - $index->addDocument(new Document('', ['name' => 'kimchy'])); - - $index->refresh(); - - $query = Query::create([ - 'query' => [ - 'bool' => [ - 'filter' => [ - 'script' => [ - 'script' => 'doc["undefined"] > 8', // compiles, but doesn't work - ], - ], - ], - ], - ]); - - try { - $index->search($query); - - $this->fail('PartialShardFailureException should have been thrown'); - } catch (PartialShardFailureException $e) { - $builder = new DefaultBuilder(); - $resultSet = $builder->buildResultSet($e->getResponse(), $query); - $this->assertCount(0, $resultSet->getResults()); - - $message = JSON::parse($e->getMessage()); - $this->assertArrayHasKey('failures', $message, 'Failures are absent'); - $this->assertGreaterThan(0, \count($message['failures']), 'Failures are empty'); - } - } -} diff --git a/tests/Exception/ResponseExceptionTest.php b/tests/Exception/ResponseExceptionTest.php deleted file mode 100644 index ef6601b075..0000000000 --- a/tests/Exception/ResponseExceptionTest.php +++ /dev/null @@ -1,78 +0,0 @@ -_createIndex('woo', true); - - try { - $this->_createIndex('woo', false); - $this->fail('Index created when it should fail'); - } catch (ResponseException $ex) { - $error = $ex->getResponse()->getFullError(); - - $this->assertNotEquals('index_already_exists_exception', $error['type']); - $this->assertEquals('resource_already_exists_exception', $error['type']); - $this->assertEquals(400, $ex->getResponse()->getStatus()); - } - } - - /** - * @group functional - */ - public function testBadType(): void - { - $index = $this->_createIndex(); - - $index->setMapping(new Mapping([ - 'num' => [ - 'type' => 'long', - ], - ])); - - try { - $index->addDocument(new Document('', [ - 'num' => 'not number at all', - ])); - $this->fail('Indexing with wrong type should fail'); - } catch (ResponseException $ex) { - $error = $ex->getResponse()->getFullError(); - if (\version_compare($_SERVER['ES_VERSION'], '8.8.0', '>=')) { - $this->assertEquals('document_parsing_exception', $error['type']); - } else { - $this->assertEquals('mapper_parsing_exception', $error['type']); - } - $this->assertEquals(400, $ex->getResponse()->getStatus()); - } - } - - /** - * @group functional - */ - public function testWhatever(): void - { - $index = $this->_createIndex(); - $index->delete(); - - try { - $index->search(); - } catch (ResponseException $ex) { - $error = $ex->getResponse()->getFullError(); - $this->assertEquals('index_not_found_exception', $error['type']); - $this->assertEquals(404, $ex->getResponse()->getStatus()); - } - } -} diff --git a/tests/Index/SettingsTest.php b/tests/Index/SettingsTest.php index 3ca3e4b21c..ce25e806ee 100644 --- a/tests/Index/SettingsTest.php +++ b/tests/Index/SettingsTest.php @@ -2,9 +2,10 @@ namespace Elastica\Test\Index; +use Elastic\Elasticsearch\Exception\ClientResponseException; use Elastica\Document; -use Elastica\Exception\ResponseException; use Elastica\Index\Settings as IndexSettings; +use Elastica\ResponseConverter; use Elastica\Test\Base as BaseTest; /** @@ -134,8 +135,9 @@ public function testDeleteAliasWithException(): void try { $indexAlias->delete(); $this->fail('Should throw exception because you should delete the concrete index and not the alias'); - } catch (ResponseException $e) { - $error = $e->getResponse()->getFullError(); + } catch (ClientResponseException $e) { + $response = ResponseConverter::toElastica($e->getResponse()); + $error = $response->getFullError(); $this->assertSame('illegal_argument_exception', $error['type']); $this->assertStringContainsString('specify the corresponding concrete indices instead.', $error['reason']); @@ -346,8 +348,9 @@ public function testSetReadOnly(): void try { $index->addDocument($doc2); $this->fail('Should throw exception because of read only'); - } catch (ResponseException $e) { - $error = $e->getResponse()->getFullError(); + } catch (ClientResponseException $e) { + $response = ResponseConverter::toElastica($e->getResponse()); + $error = $response->getFullError(); $this->assertSame('cluster_block_exception', $error['type']); $this->assertStringContainsString('read-only', $error['reason']); @@ -447,8 +450,10 @@ public function testNotFoundIndex(): void try { $index->getSettings()->get(); $this->fail('Should throw exception because of index not found'); - } catch (ResponseException $e) { - $error = $e->getResponse()->getFullError(); + } catch (ClientResponseException $e) { + $response = ResponseConverter::toElastica($e->getResponse()); + $error = $response->getFullError(); + $this->assertSame('index_not_found_exception', $error['type']); } } diff --git a/tests/IndexTemplateTest.php b/tests/IndexTemplateTest.php index af668a7dfc..6ccad10961 100644 --- a/tests/IndexTemplateTest.php +++ b/tests/IndexTemplateTest.php @@ -2,12 +2,10 @@ namespace Elastica\Test; -use Elastica\Client; +use Elastic\Elasticsearch\Exception\ClientResponseException; use Elastica\Exception\InvalidException; -use Elastica\Exception\ResponseException; use Elastica\IndexTemplate; -use Elastica\Request; -use Elastica\Response; +use Elastica\ResponseConverter; use Elastica\Test\Base as BaseTest; /** @@ -43,61 +41,6 @@ public function testIncorrectInstantiate(): void new IndexTemplate($client, null); } - /** - * @group unit - */ - public function testDelete(): void - { - $name = 'index_template1'; - $response = new Response(''); - /** @var Client|\PHPUnit\Framework\MockObject\MockObject $clientMock */ - $clientMock = $this->createMock(Client::class); - $clientMock->expects($this->once()) - ->method('request') - ->with('_template/'.$name, Request::DELETE, [], []) - ->willReturn($response) - ; - $indexTemplate = new IndexTemplate($clientMock, $name); - $this->assertSame($response, $indexTemplate->delete()); - } - - /** - * @group unit - */ - public function testCreate(): void - { - $args = [1]; - $response = new Response(''); - $name = 'index_template1'; - /** @var Client|\PHPUnit\Framework\MockObject\MockObject $clientMock */ - $clientMock = $this->createMock(Client::class); - $clientMock->expects($this->once()) - ->method('request') - ->with('_template/'.$name, Request::PUT, $args, []) - ->willReturn($response) - ; - $indexTemplate = new IndexTemplate($clientMock, $name); - $this->assertSame($response, $indexTemplate->create($args)); - } - - /** - * @group unit - */ - public function testExists(): void - { - $name = 'index_template1'; - $response = new Response('', 200); - /** @var Client|\PHPUnit\Framework\MockObject\MockObject $clientMock */ - $clientMock = $this->createMock(Client::class); - $clientMock->expects($this->once()) - ->method('request') - ->with('_template/'.$name, Request::HEAD, [], []) - ->willReturn($response) - ; - $indexTemplate = new IndexTemplate($clientMock, $name); - $this->assertTrue($indexTemplate->exists()); - } - /** * @group functional */ @@ -133,12 +76,13 @@ public function testCreateAlreadyExistsTemplateException(): void $indexTemplate->create($template); try { $indexTemplate->create($template); - } catch (ResponseException $ex) { - $error = $ex->getResponse()->getFullError(); + } catch (ClientResponseException $ex) { + $response = ResponseConverter::toElastica($ex->getResponse()); + $error = $response->getFullError(); $this->assertNotEquals('index_template_already_exists_exception', $error['type']); $this->assertEquals('resource_already_exists_exception', $error['type']); - $this->assertEquals(400, $ex->getResponse()->getStatus()); + $this->assertEquals(400, $ex->getResponse()->getStatusCode()); } } } diff --git a/tests/IndexTest.php b/tests/IndexTest.php index fa70d4c818..74effafbfd 100644 --- a/tests/IndexTest.php +++ b/tests/IndexTest.php @@ -2,10 +2,9 @@ namespace Elastica\Test; +use Elastic\Elasticsearch\Exception\ClientResponseException; use Elastica\Client; use Elastica\Document; -use Elastica\Exception\InvalidException; -use Elastica\Exception\ResponseException; use Elastica\Index; use Elastica\Mapping; use Elastica\Query\QueryString; @@ -15,7 +14,6 @@ use Elastica\Script\Script; use Elastica\Status; use Elastica\Test\Base as BaseTest; -use Elasticsearch\Endpoints\Indices\Analyze; use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; /** @@ -87,7 +85,7 @@ public function testGetMappingAlias(): void */ public function testAddRemoveAlias(): void { - $this->expectException(ResponseException::class); + $this->expectException(ClientResponseException::class); $client = $this->_getClient(); @@ -470,7 +468,7 @@ public function testOpenPointInTime(): void $index = $this->_createIndex(); $response = $index->openPointInTime('10s'); - $this->assertTrue($response->isOk()); + $this->assertTrue($response->getStatus() >= 200 && $response->getStatus() < 300); $data = $response->getData(); $this->assertIsArray($data); @@ -626,8 +624,8 @@ public function testDeleteMissingIndexHasError(): void try { $index->delete(); $this->fail('This should never be reached. Deleting an unknown index will throw an exception'); - } catch (ResponseException $error) { - $this->assertTrue($error->getResponse()->hasError()); + } catch (ClientResponseException $error) { + $this->assertTrue(404 === $error->getCode()); } } @@ -727,21 +725,6 @@ public function testCreate(): void $this->assertTrue($status->indexExists($indexName)); } - /** - * @group unit - */ - public function testCreateWithInvalidOption(): void - { - $this->expectException(InvalidException::class); - $this->expectExceptionMessageMatches('/"testing_invalid_option" is not a valid option\. Allowed options are ((("[a-z_]+")(, )?)+)\./'); - - $client = $this->createMock(Client::class); - $index = new Index($client, 'test'); - - $opts = ['testing_invalid_option' => true]; - $index->create([], $opts); - } - /** * @group unit */ @@ -893,10 +876,9 @@ public function testRequestEndpoint(): void { $index = $this->_createIndex(); $index->refresh(); - $endpoint = new Analyze(); - $endpoint->setIndex('fooIndex'); - $endpoint->setBody(['text' => 'foo']); - $returnedTokens = $index->requestEndpoint($endpoint)->getData()['tokens']; + + $response = $index->getClient()->indices()->analyze(['body' => ['text' => 'foo']]); + $returnedTokens = $response->asArray()['tokens']; $tokens = [ [ diff --git a/tests/MappingTest.php b/tests/MappingTest.php index d396096583..79babfcf62 100644 --- a/tests/MappingTest.php +++ b/tests/MappingTest.php @@ -196,16 +196,12 @@ public function testMappingExample(): void { $index = $this->_createIndex(); - // The boost parameter on field mappings has been removed. - // @see https://www.elastic.co/guide/en/elasticsearch/reference/master/migrating-8.0.html#breaking-changes-8.0 - $isEs8 = \version_compare($_SERVER['ES_VERSION'], '8.0.0', '>='); - $mapping = new Mapping([ 'note' => [ 'properties' => [ - 'titulo' => ['type' => 'text', 'copy_to' => 'testall'] + (!$isEs8 ? ['boost' => 1.0] : []), - 'contenido' => ['type' => 'text', 'copy_to' => 'testall'] + (!$isEs8 ? ['boost' => 1.0] : []), - 'testall' => ['type' => 'text'] + (!$isEs8 ? ['boost' => 1.0] : []), + 'titulo' => ['type' => 'text', 'copy_to' => 'testall'], + 'contenido' => ['type' => 'text', 'copy_to' => 'testall'], + 'testall' => ['type' => 'text'], ], ], ]); diff --git a/tests/Multi/MultiBuilderTest.php b/tests/Multi/MultiBuilderTest.php index b44ce1b0fa..a3a77b30b3 100644 --- a/tests/Multi/MultiBuilderTest.php +++ b/tests/Multi/MultiBuilderTest.php @@ -57,10 +57,12 @@ public function testBuildMultiResultSet(): void [], ], ]); + $searches = [ $s1 = new Search($this->_getClient(), $this->builder), $s2 = new Search($this->_getClient(), $this->builder), ]; + $resultSet1 = new ResultSet(new Response([]), $s1->getQuery(), []); $resultSet2 = new ResultSet(new Response([]), $s2->getQuery(), []); diff --git a/tests/Multi/SearchTest.php b/tests/Multi/SearchTest.php index 6b7095e7c6..edc51400fc 100644 --- a/tests/Multi/SearchTest.php +++ b/tests/Multi/SearchTest.php @@ -292,7 +292,7 @@ public function testSearchWithError(): void $multiSearch->addSearch($searchBad); - $this->markTestSkipped('Elastica\Exception\ResponseException: [size] parameter cannot be negative, found [-2]'); + $this->markTestSkipped('Elastic\Elasticsearch\Exception\ClientResponseException: [size] parameter cannot be negative, found [-2]'); $multiResultSet = $multiSearch->search(); $resultSets = $multiResultSet->getResultSets(); $this->assertIsArray($resultSets); @@ -337,7 +337,7 @@ public function testSearchWithErrorWithKeys(): void $multiSearch->addSearch($searchBad); - $this->markTestSkipped('Elastica\Exception\ResponseException: [size] parameter cannot be negative, found [-2]'); + $this->markTestSkipped('Elastic\Elasticsearch\Exception\ClientResponseException: [size] parameter cannot be negative, found [-2]'); $multiResultSet = $multiSearch->search(); $resultSets = $multiResultSet->getResultSets(); $this->assertIsArray($resultSets); @@ -521,55 +521,6 @@ public function testGlobalSearchTypeSearchWithKeys(): void $this->assertEquals(6, $resultSets[1]->getTotalHits()); } - /** - * @group functional - */ - public function testSearchWithSearchOptions(): void - { - if (\version_compare($_SERVER['ES_VERSION'], '8.8.0', '>=')) { - $this->markTestSkipped('This test was not working as expected initially, `terminate_after` is not appended to `multisearch` also total-hits does not reflect real result https://www.elastic.co/guide/en/elasticsearch/reference/8.7/search-your-data.html#quickly-check-for-matching-docs'); - } - - $index = $this->_createIndex(); - $client = $index->getClient(); - - $multiSearch = new MultiSearch($client); - - $search1 = new Search($client); - $search1->addIndex($index); - $search1->setOption('terminate_after', '1'); - $query1 = new Query(); - $termQuery1 = new Term(); - $termQuery1->setTerm('username', 'bunny'); - $query1->setQuery($termQuery1); - $query1->setSize(1); - $search1->setQuery($query1); - - $multiSearch->addSearch($search1); - - $this->assertCount(1, $multiSearch->getSearches()); - - $search2 = new Search($client); - $search2->addIndex($index); - $query2 = new Query(); - $termQuery2 = new Term(); - $termQuery2->setTerm('username', 'bunny'); - $query2->setQuery($termQuery2); - $query2->setSize(3); - $search2->setQuery($query2); - - $multiSearch->addSearch($search2); - - $this->assertCount(2, $multiSearch->getSearches()); - - // assert - $multiResultSet = $multiSearch->search(); - $resultSets = $multiResultSet->getResultSets(); - - $this->assertEquals(1, $resultSets[0]->getTotalHits()); - $this->assertEquals(6, $resultSets[1]->getTotalHits()); - } - protected function _createIndex(?string $name = null, bool $delete = true, int $shards = 1): Index { $client = $this->_getClient(); diff --git a/tests/Node/InfoTest.php b/tests/Node/InfoTest.php index 3420cf49d4..792fa23bcd 100644 --- a/tests/Node/InfoTest.php +++ b/tests/Node/InfoTest.php @@ -80,7 +80,7 @@ public function testGetName(): void { $client = $this->_getClient(); - $data = $client->request('_nodes/stats')->getData(); + $data = $client->nodes()->stats()->asArray(); $rawNodes = $data['nodes']; $nodes = $client->getCluster()->getNodes(); diff --git a/tests/NodeTest.php b/tests/NodeTest.php index 93a9c88658..e039bb455e 100644 --- a/tests/NodeTest.php +++ b/tests/NodeTest.php @@ -66,7 +66,7 @@ public function testGetName(): void // At least 1 instance must exist $this->assertGreaterThan(0, $nodes); - $data = $client->request('_nodes')->getData(); + $data = $client->nodes()->info()->asArray(); $rawNodes = $data['nodes']; foreach ($nodes as $node) { diff --git a/tests/PipelineTest.php b/tests/PipelineTest.php index 004c40b8c1..b7b17e850b 100644 --- a/tests/PipelineTest.php +++ b/tests/PipelineTest.php @@ -2,14 +2,15 @@ namespace Elastica\Test; +use Elastic\Elasticsearch\Exception\ClientResponseException; use Elastica\Bulk; use Elastica\Client; use Elastica\Document; -use Elastica\Exception\ResponseException; use Elastica\Pipeline; use Elastica\Processor\RenameProcessor; use Elastica\Processor\SetProcessor; use Elastica\Processor\TrimProcessor; +use Elastica\ResponseConverter; /** * @internal @@ -131,8 +132,9 @@ public function testDeletePipeline(): void try { $pipeline->deletePipeline('non_existent_pipeline'); $this->fail('an exception should be raised!'); - } catch (ResponseException $e) { - $result = $e->getResponse()->getFullError(); + } catch (ClientResponseException $e) { + $response = ResponseConverter::toElastica($e->getResponse()); + $result = $response->getFullError(); $this->assertEquals('resource_not_found_exception', $result['type']); $this->assertEquals('pipeline [non_existent_pipeline] is missing', $result['reason']); diff --git a/tests/Query/MultiMatchTest.php b/tests/Query/MultiMatchTest.php index 065f772271..190cb456ea 100644 --- a/tests/Query/MultiMatchTest.php +++ b/tests/Query/MultiMatchTest.php @@ -186,6 +186,12 @@ private function _generateIndex(): Index $client = $this->_getClient(); $index = $client->getIndex('test'); + $mapping = new Mapping([ + 'name' => ['type' => 'text', 'analyzer' => 'noStops'], + 'last_name' => ['type' => 'text', 'analyzer' => 'noStops'], + 'full_name' => ['type' => 'text', 'analyzer' => 'noStops'], + ]); + $index->create( [ 'settings' => [ @@ -202,20 +208,13 @@ private function _generateIndex(): Index ], ], ], + 'mappings' => $mapping->toArray(), ], [ 'recreate' => true, ] ); - $mapping = new Mapping([ - 'name' => ['type' => 'text', 'analyzer' => 'noStops'], - 'last_name' => ['type' => 'text', 'analyzer' => 'noStops'], - 'full_name' => ['type' => 'text', 'analyzer' => 'noStops'], - ]); - - $index->setMapping($mapping); - foreach (self::$data as $key => $docData) { $index->addDocument(new Document($key, $docData)); } diff --git a/tests/Query/QueryStringTest.php b/tests/Query/QueryStringTest.php index a62f6ae88e..afdce8dc64 100644 --- a/tests/Query/QueryStringTest.php +++ b/tests/Query/QueryStringTest.php @@ -2,9 +2,10 @@ namespace Elastica\Test\Query; +use Elastic\Elasticsearch\Exception\ClientResponseException; use Elastica\Document; -use Elastica\Exception\ResponseException; use Elastica\Query\QueryString; +use Elastica\ResponseConverter; use Elastica\Test\Base as BaseTest; /** @@ -103,14 +104,15 @@ public function testSearchFieldsValidationException(): void try { $index->search($query); - } catch (ResponseException $ex) { - $error = $ex->getResponse()->getFullError(); + } catch (ClientResponseException $e) { + $response = ResponseConverter::toElastica($e->getResponse()); + $error = $response->getFullError(); $this->assertSame('query_shard_exception', $error['root_cause'][0]['type']); $this->assertStringContainsString('failed to create query', $error['root_cause'][0]['reason']); $this->assertStringContainsString('[fields] parameter in conjunction with [default_field]', $error['failed_shards'][0]['reason']['caused_by']['reason']); - $this->assertEquals(400, $ex->getResponse()->getStatus()); + $this->assertEquals(400, $response->getStatus()); } } diff --git a/tests/Query/WildcardTest.php b/tests/Query/WildcardTest.php index 2520b616c3..0d789b7046 100644 --- a/tests/Query/WildcardTest.php +++ b/tests/Query/WildcardTest.php @@ -55,6 +55,10 @@ public function testSearchWithAnalyzer(): void $client = $this->_getClient(); $index = $client->getIndex('test'); + $mapping = new Mapping([ + 'name' => ['type' => 'text', 'analyzer' => 'lw'], + ]); + $indexParams = [ 'settings' => [ 'analysis' => [ @@ -67,15 +71,11 @@ public function testSearchWithAnalyzer(): void ], ], ], + 'mappings' => $mapping->toArray(), ]; $index->create($indexParams, ['recreate' => true]); - $mapping = new Mapping([ - 'name' => ['type' => 'text', 'analyzer' => 'lw'], - ]); - $index->setMapping($mapping); - $index->addDocuments([ new Document('1', ['name' => 'Basel-Stadt']), new Document('2', ['name' => 'New York']), @@ -96,11 +96,6 @@ public function testSearchWithAnalyzer(): void $this->assertEquals(2, $resultSet->count()); - $query = new Wildcard('name', 'baden b*'); - $resultSet = $index->search($query); - - $this->assertEquals(1, $resultSet->count()); - $query = new Wildcard('name', 'baden bas*'); $resultSet = $index->search($query); diff --git a/tests/ReindexTest.php b/tests/ReindexTest.php index 1a6421073f..2e933df0b3 100644 --- a/tests/ReindexTest.php +++ b/tests/ReindexTest.php @@ -2,8 +2,8 @@ namespace Elastica\Test; +use Elastic\Elasticsearch\Exception\ClientResponseException; use Elastica\Document; -use Elastica\Exception\ResponseException; use Elastica\Index; use Elastica\Pipeline; use Elastica\Processor\RenameProcessor; @@ -82,10 +82,15 @@ public function testReindexOpTypeOptionWithProceedSetOnConflictStop(): void Reindex::OPERATION_TYPE => Reindex::OPERATION_TYPE_CREATE, ]); - $response = $reindex->run(); - $newIndex->refresh(); + try { + $reindex->run(); + + $this->fail('Elasticsearch should have thrown an Exception.'); + } catch (ClientResponseException $e) { + $newIndex->refresh(); - $this->assertEquals(5, $response->getData()['version_conflicts']); + $this->assertEquals(5, \json_decode($e->getResponse()->getBody()->__toString(), true)['version_conflicts']); + } } /** @@ -198,7 +203,7 @@ public function testReindexWithRemote(): void $reindex->run(); $this->fail('Elasticsearch should have thrown an Exception, maybe the remote option has not been sent.'); - } catch (ResponseException $exception) { + } catch (ClientResponseException $exception) { $this->assertStringContainsString('reindex.remote.whitelist', $exception->getMessage()); } } diff --git a/tests/RequestTest.php b/tests/RequestTest.php deleted file mode 100644 index 41361693c0..0000000000 --- a/tests/RequestTest.php +++ /dev/null @@ -1,99 +0,0 @@ - 'params']; - $data = ['key' => 'value']; - - $request = new Request($path, $method, $data, $query); - - $this->assertEquals($path, $request->getPath()); - $this->assertEquals($method, $request->getMethod()); - $this->assertEquals($query, $request->getQuery()); - $this->assertEquals($data, $request->getData()); - } - - /** - * @group unit - */ - public function testInvalidConnection(): void - { - $this->expectException(InvalidException::class); - - $request = new Request('', Request::GET); - $request->send(); - } - - /** - * @group functional - */ - public function testSend(): void - { - $connection = new Connection(); - $connection->setHost($this->_getHost()); - $connection->setPort(9200); - - $request = new Request('_stats', Request::GET, [], [], $connection); - - $response = $request->send(); - - $this->assertTrue($response->isOk()); - } - - /** - * @group unit - */ - public function testToString(): void - { - $path = 'test'; - $method = Request::POST; - $query = ['no' => 'params']; - $data = ['key' => 'value']; - - $connection = new Connection(); - $connection->setHost($this->_getHost()); - $connection->setPort(9200); - - $request = new Request($path, $method, $data, $query, $connection); - - $data = $request->toArray(); - - $this->assertIsArray($data); - $this->assertArrayHasKey('method', $data); - $this->assertArrayHasKey('path', $data); - $this->assertArrayHasKey('query', $data); - $this->assertArrayHasKey('data', $data); - $this->assertArrayHasKey('connection', $data); - $this->assertEquals($request->getMethod(), $data['method']); - $this->assertEquals($request->getPath(), $data['path']); - $this->assertEquals($request->getQuery(), $data['query']); - $this->assertEquals($request->getData(), $data['data']); - $this->assertIsArray($data['connection']); - $this->assertArrayHasKey('host', $data['connection']); - $this->assertArrayHasKey('port', $data['connection']); - $this->assertEquals($request->getConnection()->getHost(), $data['connection']['host']); - $this->assertEquals($request->getConnection()->getPort(), $data['connection']['port']); - - $this->assertIsString((string) $request); - } -} diff --git a/tests/ResponseFunctionalTest.php b/tests/ResponseFunctionalTest.php index d8396ed629..1d8ec28ef2 100644 --- a/tests/ResponseFunctionalTest.php +++ b/tests/ResponseFunctionalTest.php @@ -3,11 +3,9 @@ namespace Elastica\Test; use Elastica\Document; -use Elastica\Exception\ResponseException; use Elastica\Mapping; use Elastica\Query; use Elastica\Query\MatchAll; -use Elastica\Request; use Elastica\Test\Base as BaseTest; /** @@ -68,33 +66,4 @@ public function testIsOkMultiple(): void $this->assertTrue($response->isOk()); } - - public function testGetDataEmpty(): void - { - $index = $this->_createIndex(); - $gotException = false; - - // @see https://www.elastic.co/guide/en/elasticsearch/reference/master/migrating-8.0.html#breaking-changes-8.0 - $isEs8 = \version_compare($_SERVER['ES_VERSION'], '8.0.0', '>='); - if ($isEs8) { - $this->markTestSkipped('REST API endpoints containing mapping types have been removed.'); - } - - try { - $index->request( - 'non-existent-type/_mapping', - Request::GET, - [], - ['include_type_name' => true] - ); - } catch (ResponseException $e) { - $error = $e->getResponse()->getFullError(); - $this->assertEquals('type_missing_exception', $error['type']); - $this->assertStringContainsString('non-existent-type', $error['reason']); - - $gotException = true; - } - - $this->assertTrue($gotException); - } } diff --git a/tests/SearchTest.php b/tests/SearchTest.php index 0498015013..dcaa143662 100644 --- a/tests/SearchTest.php +++ b/tests/SearchTest.php @@ -2,22 +2,24 @@ namespace Elastica\Test; +use Elastic\Elasticsearch\Exception\ClientResponseException; +use Elastic\Elasticsearch\Response\Elasticsearch; use Elastica\Aggregation\Cardinality; use Elastica\Client; use Elastica\Document; use Elastica\Exception\InvalidException; -use Elastica\Exception\ResponseException; use Elastica\Query; use Elastica\Query\FunctionScore; use Elastica\Query\MatchAll; use Elastica\Query\QueryString; use Elastica\Request; -use Elastica\Response; +use Elastica\ResponseConverter; use Elastica\ResultSet; use Elastica\Script\Script; use Elastica\Search; use Elastica\Suggest; use Elastica\Test\Base as BaseTest; +use GuzzleHttp\Psr7\Response as Psr7Response; /** * @internal @@ -252,19 +254,27 @@ public function testSearchScrollRequest(): void Search::OPTION_SCROLL => '5m', Search::OPTION_SCROLL_ID => $scrollId, ]); + + \parse_str($search->getClient()->getLastRequest()->getUri()->getQuery(), $lastRequestQuery); + $lastRequestData = \json_decode($search->getClient()->getLastRequest()->getBody(), true); + $this->assertFalse($result->getResponse()->hasError()); $this->assertCount(5, $result->getResults()); - $this->assertArrayNotHasKey(Search::OPTION_SCROLL_ID, $search->getClient()->getLastRequest()->getQuery()); - $this->assertEquals([Search::OPTION_SCROLL_ID => $scrollId], $search->getClient()->getLastRequest()->getData()); + $this->assertArrayNotHasKey(Search::OPTION_SCROLL_ID, $lastRequestQuery); + $this->assertEquals([Search::OPTION_SCROLL_ID => $scrollId], $lastRequestData); $result = $search->search([], [ Search::OPTION_SCROLL => '5m', Search::OPTION_SCROLL_ID => $scrollId, ]); + + \parse_str($search->getClient()->getLastRequest()->getUri()->getQuery(), $lastRequestQuery); + $lastRequestData = \json_decode($search->getClient()->getLastRequest()->getBody(), true); + $this->assertFalse($result->getResponse()->hasError()); $this->assertCount(0, $result->getResults()); - $this->assertArrayNotHasKey(Search::OPTION_SCROLL_ID, $search->getClient()->getLastRequest()->getQuery()); - $this->assertEquals([Search::OPTION_SCROLL_ID => $scrollId], $search->getClient()->getLastRequest()->getData()); + $this->assertArrayNotHasKey(Search::OPTION_SCROLL_ID, $lastRequestQuery); + $this->assertEquals([Search::OPTION_SCROLL_ID => $scrollId], $lastRequestData); } /** @@ -400,9 +410,19 @@ public function testArrayConfigSearch(): void $this->assertNotEmpty($resultSet->getSuggests(), 'term#name_suggest'); // Timeout - this one is a bit more tricky to test - $mockResponse = new Response(\json_encode(['timed_out' => true])); + $mockResponse = new Elasticsearch(); + $mockResponse->setResponse(new Psr7Response( + 200, + [ + Elasticsearch::HEADER_CHECK => Elasticsearch::PRODUCT_NAME, + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + ], + \json_encode(['timed_out' => true]) + )); + $client = $this->createMock(Client::class); - $client->method('request') + $client->method('search') ->willReturn($mockResponse) ; $search = new Search($client); @@ -662,12 +682,12 @@ public function testIgnoreUnavailableOption(): void $search = new Search($client); $search->addIndex($index); - $exception = null; try { $search->search($query); $this->fail('Should raise an Index not found exception'); - } catch (ResponseException $e) { - $error = $e->getResponse()->getFullError(); + } catch (ClientResponseException $e) { + $response = ResponseConverter::toElastica($e->getResponse()); + $error = $response->getFullError(); $this->assertEquals('index_not_found_exception', $error['type']); $this->assertEquals('no such index [elastica_7086b4c2ee585bbb6740ece5ed7ece01]', $error['reason']); diff --git a/tests/SnapshotTest.php b/tests/SnapshotTest.php index 9206d862df..6a6169da7f 100644 --- a/tests/SnapshotTest.php +++ b/tests/SnapshotTest.php @@ -47,6 +47,9 @@ protected function setUp(): void $this->index->refresh(); } + /** + * @group functional + */ public function testRegisterRepository(): void { $location = $this->registerRepository('backup1'); @@ -57,8 +60,15 @@ public function testRegisterRepository(): void // attempt to retrieve a repository which does not exist $this->expectException(NotFoundException::class); $this->snapshot->getRepository('foobar'); + + // delete repository + $response = $this->snapshot->deleteRepository(self::REPOSITORY_NAME); + $this->assertTrue($response->isOk()); } + /** + * @group functional + */ public function testSnapshotAndRestore(): void { $this->registerRepository('backup2'); @@ -73,8 +83,7 @@ public function testSnapshotAndRestore(): void $data = $response->getData(); $this->assertContains($this->index->getName(), $data['snapshot']['indices']); - $this->markTestSkipped('Failed asserting that actual size 2 matches expected size 1.'); - $this->assertCount(1, $data['snapshot']['indices']); // only the specified index should be present + $this->assertEquals(\in_array($this->index->getName(), $data['snapshot']['indices']), 1); $this->assertEquals($snapshotName, $data['snapshot']['snapshot']); // retrieve data regarding the snapshot @@ -82,10 +91,14 @@ public function testSnapshotAndRestore(): void $this->assertContains($this->index->getName(), $response['indices']); // delete our test index + $this->index->close(); $this->index->delete(); // restore the index from our snapshot - $response = $this->snapshot->restoreSnapshot(self::REPOSITORY_NAME, $snapshotName, [], true); + $response = $this->snapshot->restoreSnapshot(self::REPOSITORY_NAME, $snapshotName, [ + 'indices' => ['logs-*', $this->index->getName()], + 'include_global_state' => true, + ], true); $this->assertTrue($response->isOk()); $this->index->refresh(); @@ -100,8 +113,34 @@ public function testSnapshotAndRestore(): void $this->assertTrue($response->isOk()); // ensure that the snapshot has been deleted - $this->expectException(NotFoundException::class); - $this->snapshot->getSnapshot(self::REPOSITORY_NAME, $snapshotName); + $expectedExceptionDeleteSnapshot = null; + try { + $this->snapshot->getSnapshot(self::REPOSITORY_NAME, $snapshotName); + } catch (NotFoundException $e) { + $expectedExceptionDeleteSnapshot = $e; + } + $this->assertInstanceOf(NotFoundException::class, $expectedExceptionDeleteSnapshot); + + // check all snapshots + $allSnapshots = $this->snapshot->getAllSnapshots(self::REPOSITORY_NAME); + $this->assertCount(0, $allSnapshots); + + // delete repository + $response = $this->snapshot->deleteRepository(self::REPOSITORY_NAME); + $this->assertTrue($response->isOk()); + + // check all repositories + $allRepositories = $this->snapshot->getAllRepositories(); + $this->assertCount(0, $allRepositories); + + // ensure that the repository has been deleted + $expectedExceptionDeleteRepository = null; + try { + $this->snapshot->getRepository(self::REPOSITORY_NAME); + } catch (NotFoundException $e) { + $expectedExceptionDeleteRepository = $e; + } + $this->assertInstanceOf(NotFoundException::class, $expectedExceptionDeleteRepository); } private function registerRepository(string $name): string diff --git a/tests/StatusTest.php b/tests/StatusTest.php index 23a6a989cb..dced6609e9 100644 --- a/tests/StatusTest.php +++ b/tests/StatusTest.php @@ -2,7 +2,7 @@ namespace Elastica\Test; -use Elastica\Exception\ResponseException; +use Elastic\Elasticsearch\Exception\ClientResponseException; use Elastica\Response; use Elastica\Status; use Elastica\Test\Base as BaseTest; @@ -60,7 +60,7 @@ public function testIndexExists(): void try { // Make sure index is deleted first $index->delete(); - } catch (ResponseException $e) { + } catch (ClientResponseException $e) { } $status = new Status($client); diff --git a/tests/Transport/AbstractTransportTest.php b/tests/Transport/AbstractTransportTest.php deleted file mode 100644 index 76f89531eb..0000000000 --- a/tests/Transport/AbstractTransportTest.php +++ /dev/null @@ -1,149 +0,0 @@ - 'Http', 'curl' => [\CURLINFO_HEADER_OUT => true]], - ], - [ - ['transport' => 'Guzzle', 'curl' => [\CURLINFO_HEADER_OUT => true]], - ], - ]; - } - - /** - * Return transport configuration and the expected HTTP method. - * - * @return array[] - */ - public function getValidDefinitions(): array - { - return [ - ['Http'], - [['type' => 'Http']], - [['type' => new Http()]], - [new Http()], - [DummyTransport::class], - ]; - } - - /** - * @group unit - * - * @dataProvider getValidDefinitions - * - * @param mixed $transport - */ - public function testCanCreateTransportInstances($transport): void - { - $connection = new Connection(); - $transport = AbstractTransport::create($transport, $connection); - - $this->assertSame($connection, $transport->getConnection()); - } - - public function getInvalidDefinitions(): array - { - return [ - [['transport' => 'Http']], - ['InvalidTransport'], - ]; - } - - /** - * @group unit - * - * @dataProvider getInvalidDefinitions - * - * @param mixed $transport - */ - public function testThrowsExecptionOnInvalidTransportDefinition($transport): void - { - $this->expectException(InvalidException::class); - $this->expectExceptionMessage('Invalid transport'); - - AbstractTransport::create($transport, new Connection()); - } - - /** - * @group unit - */ - public function testCanInjectParamsWhenUsingArray(): void - { - $connection = new Connection(); - $params = [ - 'param1' => 'some value', - 'param3' => 'value3', - ]; - - $transport = AbstractTransport::create([ - 'type' => 'Http', - 'param1' => 'value1', - 'param2' => 'value2', - ], $connection, $params); - - $this->assertSame('value1', $transport->getParam('param1')); - $this->assertSame('value2', $transport->getParam('param2')); - $this->assertSame('value3', $transport->getParam('param3')); - } - - /** - * This test check that boolean values in the querystring - * are passed as string (true|false) and not with other values - * due to boolean strict type in ES. - * - * @group functional - * - * @dataProvider getTransport - * - * @param mixed $transport - */ - public function testBooleanStringValues($transport): void - { - $client = $this->_getClient($transport); - $index = $client->getIndex('elastica_testbooleanstringvalues'); - - $doc = new Document('1', ['id' => 1, 'email' => 'test@test.com', 'username' => 'ruflin']); - $index->addDocument($doc); - $index->refresh(); - - $search = new Search($index->getClient()); - $search->addIndex($index); - - // Added version param to result - try { - $results = $search->search([], ['version' => true]); - $this->assertTrue(true); - } catch (ResponseException $e) { - $this->fail('Failed to parse value [1] as only [true] or [false] are allowed.'); - } - - if ('Http' === $transport['transport']) { - $info = $results->getResponse()->getTransferInfo(); - $url = $info['url']; - $this->assertStringEndsWith('version=true', $url); - } - } -} diff --git a/tests/Transport/AwsAuthV4Test.php b/tests/Transport/AwsAuthV4Test.php deleted file mode 100644 index 7eecba5364..0000000000 --- a/tests/Transport/AwsAuthV4Test.php +++ /dev/null @@ -1,240 +0,0 @@ - false, - 'transport' => 'AwsAuthV4', - 'aws_credential_provider' => CredentialProvider::fromCredentials( - new Credentials('foo', 'bar', 'baz') - ), - 'aws_region' => 'us-east-1', - ]; - - $client = $this->_getClient($config); - - try { - $client->request('_stats'); - } catch (GuzzleException $e) { - $guzzleException = $e->getGuzzleException(); - $this->assertInstanceOf(ConnectException::class, $guzzleException); - $request = $guzzleException->getRequest(); - $expected = 'AWS4-HMAC-SHA256 Credential=foo/' - .\date('Ymd').'/us-east-1/es/aws4_request, '; - $this->assertStringStartsWith( - $expected, - $request->getHeaderLine('Authorization') - ); - $this->assertSame( - 'baz', - $request->getHeaderLine('X-Amz-Security-Token') - ); - } - } - - /** - * @group unit - */ - public function testPrefersCredentialProviderToHardCodedCredentials(): void - { - $config = [ - 'persistent' => false, - 'transport' => 'AwsAuthV4', - 'aws_credential_provider' => CredentialProvider::fromCredentials( - new Credentials('foo', 'bar', 'baz') - ), - 'aws_access_key_id' => 'snap', - 'aws_secret_access_key' => 'crackle', - 'aws_session_token' => 'pop', - 'aws_region' => 'us-east-1', - ]; - - $client = $this->_getClient($config); - - try { - $client->request('_stats'); - } catch (GuzzleException $e) { - $guzzleException = $e->getGuzzleException(); - $this->assertInstanceOf(ConnectException::class, $guzzleException); - $request = $guzzleException->getRequest(); - $expected = 'AWS4-HMAC-SHA256 Credential=foo/' - .\date('Ymd').'/us-east-1/es/aws4_request, '; - $this->assertStringStartsWith( - $expected, - $request->getHeaderLine('Authorization') - ); - $this->assertSame( - 'baz', - $request->getHeaderLine('X-Amz-Security-Token') - ); - } - } - - /** - * @group unit - */ - public function testSignsWithProvidedCredentials(): void - { - $config = [ - 'persistent' => false, - 'transport' => 'AwsAuthV4', - 'aws_access_key_id' => 'foo', - 'aws_secret_access_key' => 'bar', - 'aws_session_token' => 'baz', - 'aws_region' => 'us-east-1', - ]; - - $client = $this->_getClient($config); - - try { - $client->request('_stats'); - } catch (GuzzleException $e) { - $guzzleException = $e->getGuzzleException(); - $this->assertInstanceOf(ConnectException::class, $guzzleException); - $request = $guzzleException->getRequest(); - $expected = 'AWS4-HMAC-SHA256 Credential=foo/' - .\date('Ymd').'/us-east-1/es/aws4_request, '; - $this->assertStringStartsWith( - $expected, - $request->getHeaderLine('Authorization') - ); - $this->assertSame( - 'baz', - $request->getHeaderLine('X-Amz-Security-Token') - ); - } - } - - public function testUseHttpAsDefaultProtocol(): void - { - $config = [ - 'persistent' => false, - 'transport' => 'AwsAuthV4', - 'aws_access_key_id' => 'foo', - 'aws_secret_access_key' => 'bar', - 'aws_session_token' => 'baz', - 'aws_region' => 'us-east-1', - ]; - $client = $this->_getClient($config); - - try { - $client->request('_stats'); - } catch (GuzzleException $e) { - $guzzleException = $e->getGuzzleException(); - $this->assertInstanceOf(ConnectException::class, $guzzleException); - $request = $guzzleException->getRequest(); - - $this->assertSame('http', $request->getUri()->getScheme()); - } - } - - public function testSetHttpsIfItIsRequired(): void - { - $config = [ - 'persistent' => false, - 'transport' => 'AwsAuthV4', - 'aws_access_key_id' => 'foo', - 'aws_secret_access_key' => 'bar', - 'aws_session_token' => 'baz', - 'aws_region' => 'us-east-1', - 'ssl' => true, - ]; - $client = $this->_getClient($config); - - try { - $client->request('_stats'); - } catch (GuzzleException $e) { - $guzzleException = $e->getGuzzleException(); - $this->assertInstanceOf(ConnectException::class, $guzzleException); - $request = $guzzleException->getRequest(); - - $this->assertSame('https', $request->getUri()->getScheme()); - } - } - - public function testSignsWithEnvironmentalCredentials(): void - { - $config = [ - 'persistent' => false, - 'transport' => 'AwsAuthV4', - ]; - \putenv('AWS_REGION=us-east-1'); - \putenv('AWS_ACCESS_KEY_ID=foo'); - \putenv('AWS_SECRET_ACCESS_KEY=bar'); - \putenv('AWS_SESSION_TOKEN=baz'); - - $client = $this->_getClient($config); - try { - $client->request('_stats'); - } catch (GuzzleException $e) { - $guzzleException = $e->getGuzzleException(); - $this->assertInstanceOf(ConnectException::class, $guzzleException); - $request = $guzzleException->getRequest(); - $expected = 'AWS4-HMAC-SHA256 Credential=foo/' - .\date('Ymd').'/us-east-1/es/aws4_request, '; - $this->assertStringStartsWith( - $expected, - $request->getHeaderLine('Authorization') - ); - $this->assertSame( - 'baz', - $request->getHeaderLine('X-Amz-Security-Token') - ); - } - } - - /** - * @group unit - * - * @depends testSignsWithProvidedCredentials - */ - public function testStripsTrailingDotInHost(): void - { - $host = $this->_getHost(); - $hostWithTrailingDot = $host.'.'; - - $config = [ - 'persistent' => false, - 'transport' => 'AwsAuthV4', - 'aws_access_key_id' => 'foo', - 'aws_secret_access_key' => 'bar', - 'aws_session_token' => 'baz', - 'aws_region' => 'us-east-1', - 'host' => $hostWithTrailingDot, - ]; - $client = $this->_getClient($config); - - try { - $client->request('_stats'); - } catch (GuzzleException $e) { - $guzzleException = $e->getGuzzleException(); - $this->assertInstanceOf(ConnectException::class, $guzzleException); - $request = $guzzleException->getRequest(); - $this->assertSame($host, $request->getHeader('host')[0]); - $this->assertSame($hostWithTrailingDot, $request->getUri()->getHost()); - } - } -} diff --git a/tests/Transport/DummyTransport.php b/tests/Transport/DummyTransport.php deleted file mode 100644 index 8b9b447b7e..0000000000 --- a/tests/Transport/DummyTransport.php +++ /dev/null @@ -1,15 +0,0 @@ -_getProxyUrl().'/'); - - $client = $this->_getClient(['transport' => 'Guzzle', 'persistent' => false]); - $transferInfo = $client->request('/_nodes')->getTransferInfo(); - $this->assertEquals(200, $transferInfo['http_code']); - - $client->getConnection()->setProxy(null); // will not change anything - $transferInfo = $client->request('/_nodes')->getTransferInfo(); - $this->assertEquals(200, $transferInfo['http_code']); - - \putenv('http_proxy='); - } - - /** - * @group functional - */ - public function testWithEnabledEnvironmentalProxy(): void - { - \putenv('http_proxy='.$this->_getProxyUrl403().'/'); - - $client = $this->_getClient(['transport' => 'Guzzle', 'persistent' => false]); - $transferInfo = $client->request('/_nodes')->getTransferInfo(); - $this->assertEquals(403, $transferInfo['http_code']); - - $client = $this->_getClient(['transport' => 'Guzzle', 'persistent' => false]); - $client->getConnection()->setProxy(''); - $transferInfo = $client->request('/_nodes')->getTransferInfo(); - $this->assertEquals(200, $transferInfo['http_code']); - - \putenv('http_proxy='); - } - - /** - * @group functional - */ - public function testWithProxy(): void - { - $client = $this->_getClient(['transport' => 'Guzzle', 'persistent' => false]); - $client->getConnection()->setProxy($this->_getProxyUrl()); - - $transferInfo = $client->request('/_nodes')->getTransferInfo(); - $this->assertEquals(200, $transferInfo['http_code']); - } - - /** - * @group functional - */ - public function testWithoutProxy(): void - { - $client = $this->_getClient(['transport' => 'Guzzle', 'persistent' => false]); - $client->getConnection()->setProxy(''); - - $transferInfo = $client->request('/_nodes')->getTransferInfo(); - $this->assertEquals(200, $transferInfo['http_code']); - } - - /** - * @group functional - */ - public function testBodyReuse(): void - { - $client = $this->_getClient(['transport' => 'Guzzle', 'persistent' => false]); - $index = $client->getIndex('elastica_body_reuse_test'); - $index->create([], [ - 'recreate' => true, - ]); - $this->_waitForAllocation($index); - - $index->addDocument(new Document('1', ['test' => 'test'])); - - $index->refresh(); - - $resultSet = $index->search([ - 'query' => [ - 'query_string' => [ - 'query' => 'pew pew pew', - ], - ], - ]); - - $this->assertEquals(0, $resultSet->getTotalHits()); - - $response = $index->request('_search', 'POST'); - - $builder = new DefaultBuilder(); - $resultSet = $builder->buildResultSet($response, Query::create([])); - - $this->assertEquals(1, $resultSet->getTotalHits()); - } - - /** - * @group unit - */ - public function testInvalidConnection(): void - { - $this->expectException(GuzzleException::class); - - $client = $this->_getClient(['transport' => 'Guzzle', 'port' => 4500, 'persistent' => false]); - $client->request('_stats', 'GET'); - } -} diff --git a/tests/Transport/HttpTest.php b/tests/Transport/HttpTest.php deleted file mode 100644 index 65b8010ceb..0000000000 --- a/tests/Transport/HttpTest.php +++ /dev/null @@ -1,214 +0,0 @@ -_getClient(); - $index = $client->getIndex('curl_test'); - $index->create([], [ - 'recreate' => true, - ]); - $this->_waitForAllocation($index); - - // Force HEAD request to set CURLOPT_NOBODY = true - $index->exists(); - - $id = '1'; - $data = ['id' => $id, 'name' => 'Item 1']; - $doc = new Document($id, $data); - - $index->addDocument($doc); - - $index->refresh(); - - $doc = $index->getDocument($id); - - // Document should be retrieved correctly - $this->assertSame($data, $doc->getData()); - $this->assertEquals($id, $doc->getId()); - } - - /** - * @group functional - */ - public function testUnicodeData(): void - { - $client = $this->_getClient(); - $index = $client->getIndex('curl_test'); - $index->create([], [ - 'recreate' => true, - ]); - $this->_waitForAllocation($index); - - // Force HEAD request to set CURLOPT_NOBODY = true - $index->exists(); - - $id = '22'; - $data = ['id' => $id, 'name' => ' - Сегодня, я вижу, особенно грустен твой взгляд, / - И руки особенно тонки, колени обняв. / - Послушай: далеко, далеко, на озере Чад / - Изысканный бродит жираф.']; - - $doc = new Document($id, $data); - - $index->addDocument($doc); - - $index->refresh(); - - $doc = $index->getDocument($id); - - // Document should be retrieved correctly - $this->assertSame($data, $doc->getData()); - $this->assertEquals($id, $doc->getId()); - } - - /** - * @group functional - */ - public function testWithEnvironmentalProxy(): void - { - \putenv('http_proxy='.$this->_getProxyUrl().'/'); - - $client = $this->_getClient(); - $transferInfo = $client->request('_nodes')->getTransferInfo(); - $this->assertEquals(200, $transferInfo['http_code']); - - $client->getConnection()->setProxy(null); // will not change anything - $transferInfo = $client->request('_nodes')->getTransferInfo(); - $this->assertEquals(200, $transferInfo['http_code']); - - \putenv('http_proxy='); - } - - /** - * @group functional - */ - public function testWithEnabledEnvironmentalProxy(): void - { - \putenv('http_proxy='.$this->_getProxyUrl403().'/'); - $client = $this->_getClient(); - $transferInfo = $client->request('_nodes')->getTransferInfo(); - $this->assertEquals(403, $transferInfo['http_code']); - $client = $this->_getClient(); - $client->getConnection()->setProxy(''); - $transferInfo = $client->request('_nodes')->getTransferInfo(); - $this->assertEquals(200, $transferInfo['http_code']); - \putenv('http_proxy='); - } - - /** - * @group functional - */ - public function testWithProxy(): void - { - $client = $this->_getClient(); - $client->getConnection()->setProxy($this->_getProxyUrl()); - - $transferInfo = $client->request('_nodes')->getTransferInfo(); - $this->assertEquals(200, $transferInfo['http_code']); - } - - /** - * @group functional - */ - public function testWithoutProxy(): void - { - $client = $this->_getClient(); - $client->getConnection()->setProxy(''); - - $transferInfo = $client->request('_nodes')->getTransferInfo(); - $this->assertEquals(200, $transferInfo['http_code']); - } - - /** - * @group functional - */ - public function testBodyReuse(): void - { - $client = $this->_getClient(); - - $index = $client->getIndex('elastica_body_reuse_test'); - $index->create([], [ - 'recreate' => true, - ]); - $this->_waitForAllocation($index); - - $index->addDocument(new Document('1', ['test' => 'test'])); - - $index->refresh(); - - $resultSet = $index->search([ - 'query' => [ - 'query_string' => [ - 'query' => 'pew pew pew', - ], - ], - ]); - - $this->assertEquals(0, $resultSet->getTotalHits()); - - $response = $index->request('_search', 'POST'); - - $builder = new DefaultBuilder(); - $resultSet = $builder->buildResultSet($response, Query::create([])); - - $this->assertEquals(1, $resultSet->getTotalHits()); - } - - /** - * @group functional - */ - public function testRequestSuccessWithHttpCompressionEnabled(): void - { - $client = $this->_getClient(['transport' => ['type' => 'Http', 'compression' => true, 'curl' => [\CURLINFO_HEADER_OUT => true]]]); - - $index = $client->getIndex('elastica_request_with_body_and_http_compression_enabled'); - - $createIndexResponse = $index->create([], [ - 'recreate' => true, - ]); - - $createIndexResponseTransferInfo = $createIndexResponse->getTransferInfo(); - $this->assertMatchesRegularExpression('/Accept-Encoding:\ (gzip|deflate)/', $createIndexResponseTransferInfo['request_header']); - $this->assertArrayHasKey('acknowledged', $createIndexResponse->getData()); - } - - /** - * @group functional - */ - public function testRequestSuccessWithHttpCompressionDisabled(): void - { - $client = $this->_getClient(['transport' => ['type' => 'Http', 'compression' => false, 'curl' => [\CURLINFO_HEADER_OUT => true]]]); - - $index = $client->getIndex('elastica_request_with_body_and_http_compression_disabled'); - - $createIndexResponse = $index->create([], [ - 'recreate' => true, - ]); - - $createIndexResponseTransferInfo = $createIndexResponse->getTransferInfo(); - $this->assertMatchesRegularExpression('/Accept-Encoding:\ (gzip|deflate)/', $createIndexResponseTransferInfo['request_header']); - $this->assertArrayHasKey('acknowledged', $createIndexResponse->getData()); - } -} diff --git a/tests/Transport/NullTransportTest.php b/tests/Transport/NullTransportTest.php deleted file mode 100644 index 7da68a008e..0000000000 --- a/tests/Transport/NullTransportTest.php +++ /dev/null @@ -1,114 +0,0 @@ - - * @author Jan Domanski - * - * @internal - */ -class NullTransportTest extends BaseTest -{ - /** @var NullTransport NullTransport */ - protected $transport; - - protected function setUp(): void - { - parent::setUp(); - $this->transport = new NullTransport(); - } - - /** - * @group functional - */ - public function testEmptyResult(): void - { - // Creates a client with any destination, and verify it returns a response object when executed - $client = $this->_getClient(); - $connection = new Connection(['transport' => 'NullTransport']); - $client->setConnections([$connection]); - - $index = $client->getIndex('elasticaNullTransportTest1'); - - $resultSet = $index->search(new Query()); - $this->assertNotNull($resultSet); - - $response = $resultSet->getResponse(); - $this->assertNotNull($response); - - // Validate most of the expected fields in the response data. Consumers of the response - // object have a reasonable expectation of finding "hits", "took", etc - $responseData = $response->getData(); - - $this->assertArrayHasKey('took', $responseData); - $this->assertEquals(0, $responseData['took']); - $this->assertArrayHasKey('_shards', $responseData); - $this->assertArrayHasKey('hits', $responseData); - $this->assertArrayHasKey('total', $responseData['hits']); - $this->assertEquals(0, $responseData['hits']['total']['value']); - $this->assertArrayHasKey('params', $responseData); - - $took = $response->getEngineTime(); - $this->assertEquals(0, $took); - - $errorString = $response->getError(); - $this->assertEmpty($errorString); - - $shards = $response->getShardsStatistics(); - $this->assertArrayHasKey('total', $shards); - $this->assertEquals(0, $shards['total']); - $this->assertArrayHasKey('successful', $shards); - $this->assertEquals(0, $shards['successful']); - $this->assertArrayHasKey('failed', $shards); - $this->assertEquals(0, $shards['failed']); - } - - /** - * @group functional - */ - public function testExec(): void - { - $request = new Request('/test'); - $params = ['name' => 'ruflin']; - $transport = new NullTransport(); - $response = $transport->exec($request, $params); - - $data = $response->getData(); - $this->assertEquals($params, $data['params']); - } - - /** - * @group unit - */ - public function testResponse(): void - { - $resposeString = ''; - $response = new Response($resposeString); - $this->transport->setResponse($response); - $this->assertEquals($response, $this->transport->getResponse()); - } - - /** - * @group unit - */ - public function testGenerateDefaultResponse(): void - { - $params = ['blah' => 123]; - $response = $this->transport->generateDefaultResponse($params); - $this->assertEquals([], $response->getTransferInfo()); - - $responseData = $response->getData(); - $this->assertArrayHasKey('params', $responseData); - $this->assertEquals($params, $responseData['params']); - } -} diff --git a/tests/Transport/TransportBenchmarkTest.php b/tests/Transport/TransportBenchmarkTest.php deleted file mode 100644 index 1bf2aa2255..0000000000 --- a/tests/Transport/TransportBenchmarkTest.php +++ /dev/null @@ -1,246 +0,0 @@ -markTestSkipped('Benchmarks currently skipped: it has to be reworked'); - } - - /** - * @dataProvider providerTransport - */ - public function testAddDocument(array $config, string $transport): void - { - $index = $this->getIndex($config); - $index->create([], [ - 'recreate' => true, - ]); - - $times = []; - for ($i = 0; $i < $this->_max; ++$i) { - $data = $this->getData((string) $i); - $doc = new Document((string) $i, $data); - $result = $index->addDocument($doc); - $times[] = $result->getQueryTime(); - $this->assertTrue($result->isOk()); - } - - $index->refresh(); - - self::logResults('insert', $transport, $times); - } - - /** - * @depends testAddDocument - * - * @dataProvider providerTransport - * - * @param mixed $transport - */ - public function testRandomRead(array $config, $transport): void - { - $index = $this->getIndex($config); - $index->search('test'); - - $times = []; - for ($i = 0; $i < $this->_max; ++$i) { - $test = \mt_rand(1, $this->_max); - $query = new Query(); - $query->setQuery(new MatchAll()); - $query->setPostFilter(new Term(['test' => $test])); - $result = $index->search($query); - $times[] = $result->getResponse()->getQueryTime(); - } - - self::logResults('random read', $transport, $times); - } - - /** - * @depends testAddDocument - * - * @dataProvider providerTransport - * - * @param mixed $transport - */ - public function testBulk(array $config, $transport): void - { - $index = $this->getIndex($config); - - $times = []; - for ($i = 0; $i < $this->_max; ++$i) { - $docs = []; - for ($j = 0; $j < 10; ++$j) { - $data = $this->getData($i.$j); - $docs[] = new Document((string) $i, $data); - } - - $result = $index->addDocuments($docs); - $times[] = $result->getQueryTime(); - } - - self::logResults('bulk', $transport, $times); - } - - /** - * @dataProvider providerTransport - */ - public function testGetMapping(array $config, string $transport): void - { - $client = $this->_getClient($config); - $index = $client->getIndex('benchmark'); - $index->create([], [ - 'recreate' => true, - ]); - - // Define mapping - $mapping = new Mapping(); - $mapping->setParam('_boost', ['name' => '_boost', 'null_value' => 1.0]); - $mapping->setProperties([ - 'id' => ['type' => 'integer'], - 'user' => [ - 'type' => 'object', - 'properties' => [ - 'name' => ['type' => 'text', 'copy_to' => 'allincluded'], - 'fullName' => ['type' => 'text', 'copy_to' => 'allincluded'], - ], - ], - 'msg' => ['type' => 'text', 'copy_to' => 'allincluded'], - 'tstamp' => ['type' => 'date'], - 'location' => ['type' => 'geo_point'], - '_boost' => ['type' => 'float'], - 'allincluded' => ['type' => 'text'], - ]); - - $index->setMapping($mapping); - $index->refresh(); - - $times = []; - for ($i = 0; $i < $this->_max; ++$i) { - $response = $index->request('_mapping', Request::GET); - $times[] = $response->getQueryTime(); - } - self::logResults('get mapping', $transport, $times); - } - - public function providerTransport(): iterable - { - yield [[ - 'transport' => 'Http', - 'host' => $this->_getHost(), - 'port' => $this->_getPort(), - 'persistent' => false, - ], - 'Http:NotPersistent', - ]; - yield [[ - 'transport' => 'Http', - 'host' => $this->_getHost(), - 'port' => $this->_getPort(), - 'persistent' => true, - ], - 'Http:Persistent', - ]; - } - - protected function getIndex(array $config): Index - { - $client = $this->_getClient($config); - $index = $client->getIndex('benchmark'.self::buildUniqueId()); - $index->create(['index' => ['number_of_shards' => 1, 'number_of_replicas' => 0]], ['recreate' => true]); - - return $index; - } - - protected function getData(string $test): array - { - $data = [ - 'test' => $test, - 'name' => [], - ]; - for ($i = 0; $i < $this->_maxData; ++$i) { - $data['name'][] = self::buildUniqueId(); - } - - return $data; - } - - protected static function logResults(string $name, string $transport, array $times): void - { - self::$_results[$name][$transport] = [ - 'count' => \count($times), - 'max' => \max($times) * 1000, - 'min' => \min($times) * 1000, - 'mean' => (\array_sum($times) / \count($times)) * 1000, - ]; - } - - protected static function printResults(): void - { - if (!\count(self::$_results)) { - return; - } - - echo \sprintf( - "\n%-12s | %-20s | %-12s | %-12s | %-12s | %-12s\n\n", - 'NAME', - 'TRANSPORT', - 'COUNT', - 'MAX', - 'MIN', - 'MEAN' - ); - foreach (self::$_results as $name => $values) { - $means = []; - foreach ($values as $times) { - $means[] = $times['mean']; - } - $minMean = \min($means); - foreach ($values as $transport => $times) { - $perc = 0; - - if (0 !== $minMean) { - $perc = (($times['mean'] - $minMean) / $minMean) * 100; - } - - echo \sprintf( - "%-12s | %-20s | %-12d | %-12.2f | %-12.2f | %-12.2f | %+03.2f\n", - $name, - $transport, - $times['count'], - $times['max'], - $times['min'], - $times['mean'], - $perc - ); - } - echo "\n"; - } - } -} diff --git a/tests/UtilTest.php b/tests/UtilTest.php index dd6011a32d..27106a72d7 100644 --- a/tests/UtilTest.php +++ b/tests/UtilTest.php @@ -2,8 +2,6 @@ namespace Elastica\Test; -use Elastica\Connection; -use Elastica\Request; use Elastica\Test\Base as BaseTest; use Elastica\Util; @@ -159,36 +157,29 @@ public function testToSnakeCase(): void /** * @group unit */ - public function testConvertRequestToCurlCommand(): void + public function testConvertDateTimeObjectWithTimezone(): void { - $path = 'test'; - $method = Request::POST; - $query = ['no' => 'params']; - $data = ['key' => 'value']; - - $connection = new Connection(); - $connection->setHost($this->_getHost()); - $connection->setPort(9200); + $dateTimeObject = new \DateTime(); + $timestamp = $dateTimeObject->getTimestamp(); - $request = new Request($path, $method, $data, $query, $connection); + $convertedString = Util::convertDateTimeObject($dateTimeObject); - $curlCommand = Util::convertRequestToCurlCommand($request); + $date = \date('Y-m-d\TH:i:sP', $timestamp); - $expected = 'curl -XPOST \'http://'.$this->_getHost().':9200/test?no=params\' -d \'{"key":"value"}\''; - $this->assertEquals($expected, $curlCommand); + $this->assertEquals($convertedString, $date); } /** * @group unit */ - public function testConvertDateTimeObjectWithTimezone(): void + public function testConvertDateTimeObjectWithoutTimezone(): void { $dateTimeObject = new \DateTime(); $timestamp = $dateTimeObject->getTimestamp(); - $convertedString = Util::convertDateTimeObject($dateTimeObject); + $convertedString = Util::convertDateTimeObject($dateTimeObject, false); - $date = \date('Y-m-d\TH:i:sP', $timestamp); + $date = \date('Y-m-d\TH:i:s\Z', $timestamp); $this->assertEquals($convertedString, $date); } @@ -196,14 +187,27 @@ public function testConvertDateTimeObjectWithTimezone(): void /** * @group unit */ - public function testConvertDateTimeObjectWithoutTimezone(): void + public function testConvertDateWithInt(): void { - $dateTimeObject = new \DateTime(); - $timestamp = $dateTimeObject->getTimestamp(); + $dateInt = 1705361024; - $convertedString = Util::convertDateTimeObject($dateTimeObject, false); + $convertedString = Util::convertDate($dateInt); - $date = \date('Y-m-d\TH:i:s\Z', $timestamp); + $date = \date('Y-m-d\TH:i:s\Z', $dateInt); + + $this->assertEquals($convertedString, $date); + } + + /** + * @group unit + */ + public function testConvertDateWithString(): void + { + $dateString = 'Mon, 15 Jan 2024 23:23:44 GMT'; + + $convertedString = Util::convertDate($dateString); + + $date = \date('Y-m-d\TH:i:s\Z', \strtotime($dateString)); $this->assertEquals($convertedString, $date); }