diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0fbd342d..1b6bd62a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -102,7 +102,6 @@ jobs: fail-fast: false matrix: php-version: - - '8.0' - '8.1' - '8.2' - '8.3' diff --git a/CHANGELOG.md b/CHANGELOG.md index 35d9587c..03039ea8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,12 +9,16 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - Added samples ([#218](https://github.com/opensearch-project/opensearch-php/pull/218)) - Added support for PHP 8.3 and 8.4 ([#229](https://github.com/opensearch-project/opensearch-php/pull/229)) ### Changed +- Switched to PSR Interfaces +- Increased PHP min version to 8.1 - Increased min version of `ezimuel/ringphp` to `^1.2.2` - Changed fluent setters to return static ### Deprecated - Passing a callable to \OpenSearch\ClientBuilder::setEndpoint() is deprecated and replaced with passing an EndpointFactory to \OpenSearch\ClientBuilder::setEndpointFactory() ([#237](https://github.com/opensearch-project/opensearch-php/pull/237)) +- Connections, Connection pools and Selectors are deprecated. Use a PSR HTTP Client that supports retries instead. ### Removed -- Removed support for PHP 7.3 and 7.4 +- Removed support for PHP 7.3, 7.4 and 8.0. +- Removed support for async requests which were never actually working. ### Fixed - Fixed PHP 8.4 deprecations ### Updated APIs diff --git a/UPGRADING.md b/UPGRADING.md new file mode 100644 index 00000000..33f30976 --- /dev/null +++ b/UPGRADING.md @@ -0,0 +1,109 @@ +- [Upgrading OpenSearch PHP Client](#upgrading-opensearch-php-client) + - [Upgrading to >= 2.0.0](#upgrading-to--240) + - [HTTP Client Auto-Discovery](#http-client-auto-discovery) + - [Configuring Guzzle HTTP Client in 2.x](#configuring-guzzle-http-client-in-2x) + - [Configuring Symfony HTTP Client in 2.x](#configuring-symfony-http-client-in-2x) + +# Upgrading OpenSearch PHP Client + +## Upgrading to >= 2.4.0 + +openseach-php removes the hard-coded dependency on the [Guzzle HTTP client](https://docs.guzzlephp.org/en/stable/#) and switches to the following PSR interfaces: + +- [PSR-7 HTTP message interfaces](https://www.php-fig.org/psr/psr-7/) +- [PSR-17 HTTP Factories](https://www.php-fig.org/psr/psr-17/) +- [PSR-18 HTTP Client](https://www.php-fig.org/psr/psr-18/) + +You can continue to use Guzzle, but will need to configure it as a PSR-18 HTTP Client. + +### HTTP Client Auto-Discovery + +opensearch-php 2.x will try and discover and install a PSR HTTP Client using [PHP-HTTP Discovery](https://docs.php-http.org/en/latest/discovery.html) +if one is not explicitly provided. + +```php +$transport = (new \OpenSearch\TransportFactory())->create(); +$endpointFactory = new \OpenSearch\EndpointFactory(); +$client = new Client($transport, $endpointFactory, []); + +// Send a request to the 'info' endpoint. +$info = $client->info(); +``` + +### Configuring Guzzle HTTP Client in 2.x + +To configure Guzzle as a PSR HTTP Client with the similar configuration to opensearch 1.x you can use the following example: + +```php +$guzzleClient = new \GuzzleHttp\Client([ + 'base_uri' => 'https://localhost:9200', + 'auth' => ['admin', getenv('OPENSEARCH_PASSWORD')], + 'verify' => false, + 'retries' => 2, + 'headers' => [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + 'User-Agent' => sprintf('opensearch-php/%s (%s; PHP %s)', \OpenSearch\Client::VERSION, PHP_OS, PHP_VERSION), + ] +]); + +$guzzleHttpFactory = new \GuzzleHttp\Psr7\HttpFactory(); + +$serializer = new \OpenSearch\Serializers\SmartSerializer(); + +$requestFactory = new \OpenSearch\RequestFactory( + $guzzleHttpFactory, + $guzzleHttpFactory, + $guzzleHttpFactory, + $serializer, +); + +$transport = (new OpenSearch\TransportFactory()) + ->setHttpClient($guzzleClient) + ->setRequestFactory($requestFactory) + ->create(); + +$endpointFactory = new \OpenSearch\EndpointFactory(); +$client = new \OpenSearch\Client($transport, $endpointFactory, []); + +// Send a request to the 'info' endpoint. +$info = $client->info(); +``` + +### Configuring Symfony HTTP Client in 2.x + +You can configure [Symfony HTTP Client](https://symfony.com/doc/current/http_client.html) as a PSR HTTP Client using +the following example: + +```php +$symfonyPsr18Client = (new \Symfony\Component\HttpClient\Psr18Client())->withOptions([ + 'base_uri' => 'https://localhost:9200', + 'auth_basic' => ['admin', getenv('OPENSEARCH_PASSWORD')], + 'verify_peer' => false, + 'max_retries' => 2, + 'headers' => [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + ], +]); + +$serializer = new \OpenSearch\Serializers\SmartSerializer(); + +$requestFactory = new \OpenSearch\RequestFactory( + $symfonyPsr18Client, + $symfonyPsr18Client, + $symfonyPsr18Client, + $serializer, +); + +$transport = (new \OpenSearch\TransportFactory()) + ->setHttpClient($symfonyPsr18Client) + ->setRequestFactory($requestFactory) + ->create(); + +$client = new \OpenSearch\Client($transport, $endpointFactory, []); + +// Send a request to the 'info' endpoint. +$info = $client->info(); + +``` diff --git a/composer.json b/composer.json index dee24ad8..5e227710 100644 --- a/composer.json +++ b/composer.json @@ -21,22 +21,32 @@ } ], "require": { - "php": "^8.0", + "php": "^8.1", "ext-json": ">=1.3.7", "ext-curl": "*", "ezimuel/ringphp": "^1.2.2", - "psr/log": "^1|^2|^3", + "php-http/discovery": "^1.20", + "psr/http-client": "^1.0", + "psr/http-client-implementation": "*", + "psr/http-factory": "^1.1", + "psr/http-factory-implementation": "*", + "psr/http-message": "^2.0", + "psr/http-message-implementation": "*", + "psr/log": "^2|^3", "symfony/yaml": "*" }, "require-dev": { "ext-zip": "*", "aws/aws-sdk-php": "^3.0", - "friendsofphp/php-cs-fixer": "^3.0", - "mockery/mockery": "^1.2", - "phpstan/phpstan": "^1.7.15", - "phpstan/phpstan-mockery": "^1.1.0", - "phpunit/phpunit": "^9.3", - "symfony/finder": "~4.0 || ~5.0" + "friendsofphp/php-cs-fixer": "^v3.64", + "guzzlehttp/psr7": "^2.7", + "mockery/mockery": "^1.6", + "phpstan/phpstan": "^1.12", + "phpstan/phpstan-mockery": "^1.1", + "phpunit/phpunit": "^9.6", + "symfony/finder": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/http-client-contracts": "^3.0" }, "suggest": { "monolog/monolog": "Allows for client-level logging and tracing", @@ -54,7 +64,10 @@ } }, "config": { - "sort-packages": true + "sort-packages": true, + "allow-plugins": { + "php-http/discovery": true + } }, "scripts": { "php-cs": [ diff --git a/samples/index.php b/samples/index.php index b7bb97f8..266be954 100644 --- a/samples/index.php +++ b/samples/index.php @@ -7,15 +7,79 @@ require_once __DIR__ . '/vendor/autoload.php'; -$client = OpenSearch\ClientBuilder::fromConfig([ - 'Hosts' => [ - 'https://localhost:9200' - ], - 'BasicAuthentication' => ['admin', getenv('OPENSEARCH_PASSWORD')], - 'Retries' => 2, - 'SSLVerification' => false +// Auto-configure by discovery example + +$transport = (new \OpenSearch\TransportFactory())->create(); +$endpointFactory = new \OpenSearch\EndpointFactory(); +$client = new \OpenSearch\Client($transport, $endpointFactory, []); + +// Send a request to the 'info' endpoint. +$info = $client->info(); + +// Guzzle example + +$guzzleClient = new \GuzzleHttp\Client([ + 'base_uri' => 'https://localhost:9200', + 'auth' => ['admin', getenv('OPENSEARCH_PASSWORD')], + 'verify' => false, + 'retries' => 2, + 'headers' => [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + 'User-Agent' => sprintf('opensearch-php/%s (%s; PHP %s)', \OpenSearch\Client::VERSION, PHP_OS, PHP_VERSION), + ] ]); +$guzzleHttpFactory = new \GuzzleHttp\Psr7\HttpFactory(); + +$serializer = new \OpenSearch\Serializers\SmartSerializer(); + +$requestFactory = new \OpenSearch\RequestFactory( + $guzzleHttpFactory, + $guzzleHttpFactory, + $guzzleHttpFactory, + $serializer, +); + +$transport = (new OpenSearch\TransportFactory()) + ->setHttpClient($guzzleClient) + ->setRequestFactory($requestFactory) + ->create(); + +$endpointFactory = new \OpenSearch\EndpointFactory(); +$client = new \OpenSearch\Client($transport, $endpointFactory, []); + +// Send a request to the 'info' endpoint. $info = $client->info(); -echo "{$info['version']['distribution']}: {$info['version']['number']}\n"; +// Symfony example + +$symfonyPsr18Client = (new \Symfony\Component\HttpClient\Psr18Client())->withOptions([ + 'base_uri' => 'https://localhost:9200', + 'auth_basic' => ['admin', getenv('OPENSEARCH_PASSWORD')], + 'verify_peer' => false, + 'max_retries' => 2, + 'headers' => [ + 'Accept' => 'application/json', + 'Content-Type' => 'application/json', + ], +]); + +$serializer = new \OpenSearch\Serializers\SmartSerializer(); + +$requestFactory = new \OpenSearch\RequestFactory( + $symfonyPsr18Client, + $symfonyPsr18Client, + $symfonyPsr18Client, + $serializer, +); + +$transport = (new \OpenSearch\TransportFactory()) + ->setHttpClient($symfonyPsr18Client) + ->setRequestFactory($requestFactory) + ->create(); + +$client = new \OpenSearch\Client($transport, $endpointFactory, []); + +// Send a request to the 'info' endpoint. +$info = $client->info(); diff --git a/src/OpenSearch/Aws/SigningClientDecorator.php b/src/OpenSearch/Aws/SigningClientDecorator.php new file mode 100644 index 00000000..e54752f3 --- /dev/null +++ b/src/OpenSearch/Aws/SigningClientDecorator.php @@ -0,0 +1,29 @@ +withHeader('x-amz-content-sha256', hash('sha256', (string) $request->getBody())); + $request = $this->signer->signRequest($request, $this->credentials); + return $this->inner->sendRequest($request); + } +} diff --git a/src/OpenSearch/Client.php b/src/OpenSearch/Client.php index 580fe032..ae6cbb29 100644 --- a/src/OpenSearch/Client.php +++ b/src/OpenSearch/Client.php @@ -75,6 +75,8 @@ class Client */ public $transport; + private TransportInterface $httpTransport; + /** * @var array */ @@ -263,13 +265,19 @@ class Client /** * Client constructor * - * @param Transport $transport + * @param \OpenSearch\TransportInterface|\OpenSearch\Transport $transport * @param callable|EndpointFactoryInterface $endpointFactory * @param NamespaceBuilderInterface[] $registeredNamespaces */ - public function __construct(Transport $transport, callable|EndpointFactoryInterface $endpointFactory, array $registeredNamespaces) + public function __construct(TransportInterface|Transport $transport, callable|EndpointFactoryInterface $endpointFactory, array $registeredNamespaces) { - $this->transport = $transport; + if (!$transport instanceof TransportInterface) { + @trigger_error('Passing an instance of \OpenSearch\Transport to ' . __METHOD__ . '() is deprecated in 2.3.2 and will be removed in 3.0.0. Pass an instance of \OpenSearch\TransportInterface instead.', E_USER_DEPRECATED); + $this->transport = $transport; + $this->httpTransport = new LegacyTransportWrapper($transport); + } else { + $this->httpTransport = $transport; + } if (is_callable($endpointFactory)) { @trigger_error('Passing a callable as the $endpointFactory param in ' . __METHOD__ . ' is deprecated in 2.3.2 and will be removed in 3.0.0. Pass an instance of \OpenSearch\EndpointFactoryInterface instead.', E_USER_DEPRECATED); $endpoints = $endpointFactory; @@ -2063,33 +2071,36 @@ public function extractArgument(array &$params, string $arg) /** * Sends a raw request to the cluster - * @return callable|array - * @throws NoNodesAvailableException + * @return array|string|null + * @throws \Exception */ - public function request(string $method, string $uri, array $attributes = []) + public function request(string $method, string $uri, array $attributes = []): array|string|null { $params = $attributes['params'] ?? []; $body = $attributes['body'] ?? null; $options = $attributes['options'] ?? []; - $promise = $this->transport->performRequest($method, $uri, $params, $body, $options); - - return $this->transport->resultOrFuture($promise, $options); + return $this->httpTransport->sendRequest($method, $uri, $params, $body, $options['headers'] ?? []); } /** - * @return callable|array + * Sends a request for the given endpoint. + * + * @param \OpenSearch\Endpoints\AbstractEndpoint $endpoint + * + * @return array|string|null + * + * @throws \Exception */ - private function performRequest(AbstractEndpoint $endpoint) + private function performRequest(AbstractEndpoint $endpoint): array|string|null { - $promise = $this->transport->performRequest( + $options = $endpoint->getOptions(); + return $this->httpTransport->sendRequest( $endpoint->getMethod(), $endpoint->getURI(), $endpoint->getParams(), $endpoint->getBody(), - $endpoint->getOptions() + $options['headers'] ?? [] ); - - return $this->transport->resultOrFuture($promise, $endpoint->getOptions()); } } diff --git a/src/OpenSearch/ClientBuilder.php b/src/OpenSearch/ClientBuilder.php index a075e36f..da41753b 100644 --- a/src/OpenSearch/ClientBuilder.php +++ b/src/OpenSearch/ClientBuilder.php @@ -45,6 +45,11 @@ use Psr\Log\NullLogger; use ReflectionClass; +@trigger_error(ClientBuilder::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class ClientBuilder { public const ALLOWED_METHODS_FROM_CONFIG = ['includePortInHostHeader']; diff --git a/src/OpenSearch/ClientFactory.php b/src/OpenSearch/ClientFactory.php new file mode 100644 index 00000000..f94f2605 --- /dev/null +++ b/src/OpenSearch/ClientFactory.php @@ -0,0 +1,19 @@ +transport, $this->endpointFactory, $this->registeredNamespaces); + } + +} diff --git a/src/OpenSearch/Common/EmptyLogger.php b/src/OpenSearch/Common/EmptyLogger.php index c2bfeaf4..d44f8166 100644 --- a/src/OpenSearch/Common/EmptyLogger.php +++ b/src/OpenSearch/Common/EmptyLogger.php @@ -24,11 +24,15 @@ use Psr\Log\AbstractLogger; use Psr\Log\LoggerInterface; +@trigger_error(EmptyLogger::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0. Use Psr\Log\NullLogger instead', E_USER_DEPRECATED); + /** * Class EmptyLogger * * Logger that doesn't do anything. Similar to Monolog's NullHandler, * but avoids the overhead of partially loading Monolog + * + * @deprecated in 2.3.2 and will be removed in 3.0.0. Use Psr\Log\NullLogger instead. */ class EmptyLogger extends AbstractLogger implements LoggerInterface { diff --git a/src/OpenSearch/Common/Exceptions/AuthenticationConfigException.php b/src/OpenSearch/Common/Exceptions/AuthenticationConfigException.php index 7b070a1a..28afa295 100644 --- a/src/OpenSearch/Common/Exceptions/AuthenticationConfigException.php +++ b/src/OpenSearch/Common/Exceptions/AuthenticationConfigException.php @@ -22,6 +22,11 @@ namespace OpenSearch\Common\Exceptions; +@trigger_error(AuthenticationConfigException::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class AuthenticationConfigException extends \RuntimeException implements OpenSearchException { } diff --git a/src/OpenSearch/Common/Exceptions/BadMethodCallException.php b/src/OpenSearch/Common/Exceptions/BadMethodCallException.php index 62b6551d..0dfe1fec 100644 --- a/src/OpenSearch/Common/Exceptions/BadMethodCallException.php +++ b/src/OpenSearch/Common/Exceptions/BadMethodCallException.php @@ -21,10 +21,14 @@ namespace OpenSearch\Common\Exceptions; +@trigger_error(BadMethodCallException::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + /** * BadMethodCallException * * Denote problems with a method call (e.g. incorrect number of arguments) + * + * @deprecated in 2.3.2 and will be removed in 3.0.0. */ class BadMethodCallException extends \BadMethodCallException implements OpenSearchException { diff --git a/src/OpenSearch/Common/Exceptions/BadRequest400Exception.php b/src/OpenSearch/Common/Exceptions/BadRequest400Exception.php index 11480bfd..ac232f01 100644 --- a/src/OpenSearch/Common/Exceptions/BadRequest400Exception.php +++ b/src/OpenSearch/Common/Exceptions/BadRequest400Exception.php @@ -21,6 +21,11 @@ namespace OpenSearch\Common\Exceptions; +@trigger_error(BadRequest400Exception::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class BadRequest400Exception extends \Exception implements OpenSearchException { } diff --git a/src/OpenSearch/Common/Exceptions/ClientErrorResponseException.php b/src/OpenSearch/Common/Exceptions/ClientErrorResponseException.php index 3982d81f..884e00f2 100644 --- a/src/OpenSearch/Common/Exceptions/ClientErrorResponseException.php +++ b/src/OpenSearch/Common/Exceptions/ClientErrorResponseException.php @@ -21,6 +21,11 @@ namespace OpenSearch\Common\Exceptions; +@trigger_error(ClientErrorResponseException::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class ClientErrorResponseException extends TransportException implements OpenSearchException { } diff --git a/src/OpenSearch/Common/Exceptions/Conflict409Exception.php b/src/OpenSearch/Common/Exceptions/Conflict409Exception.php index 215b8035..36e6ed34 100644 --- a/src/OpenSearch/Common/Exceptions/Conflict409Exception.php +++ b/src/OpenSearch/Common/Exceptions/Conflict409Exception.php @@ -21,6 +21,11 @@ namespace OpenSearch\Common\Exceptions; +@trigger_error(Conflict409Exception::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class Conflict409Exception extends \Exception implements OpenSearchException { } diff --git a/src/OpenSearch/Common/Exceptions/Curl/CouldNotConnectToHost.php b/src/OpenSearch/Common/Exceptions/Curl/CouldNotConnectToHost.php index 63ab1834..7f95c944 100644 --- a/src/OpenSearch/Common/Exceptions/Curl/CouldNotConnectToHost.php +++ b/src/OpenSearch/Common/Exceptions/Curl/CouldNotConnectToHost.php @@ -25,6 +25,11 @@ use OpenSearch\Common\Exceptions\OpenSearchException; use OpenSearch\Common\Exceptions\TransportException; +@trigger_error(CouldNotConnectToHost::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class CouldNotConnectToHost extends TransportException implements OpenSearchException { } diff --git a/src/OpenSearch/Common/Exceptions/Curl/CouldNotResolveHostException.php b/src/OpenSearch/Common/Exceptions/Curl/CouldNotResolveHostException.php index ac4cc9c0..3b6a5417 100644 --- a/src/OpenSearch/Common/Exceptions/Curl/CouldNotResolveHostException.php +++ b/src/OpenSearch/Common/Exceptions/Curl/CouldNotResolveHostException.php @@ -24,6 +24,11 @@ use OpenSearch\Common\Exceptions\OpenSearchException; use OpenSearch\Common\Exceptions\TransportException; +@trigger_error(CouldNotResolveHostException::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class CouldNotResolveHostException extends TransportException implements OpenSearchException { } diff --git a/src/OpenSearch/Common/Exceptions/Curl/OperationTimeoutException.php b/src/OpenSearch/Common/Exceptions/Curl/OperationTimeoutException.php index f009d132..19bf5fc5 100644 --- a/src/OpenSearch/Common/Exceptions/Curl/OperationTimeoutException.php +++ b/src/OpenSearch/Common/Exceptions/Curl/OperationTimeoutException.php @@ -24,6 +24,11 @@ use OpenSearch\Common\Exceptions\OpenSearchException; use OpenSearch\Common\Exceptions\TransportException; +@trigger_error(OperationTimeoutException::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class OperationTimeoutException extends TransportException implements OpenSearchException { } diff --git a/src/OpenSearch/Common/Exceptions/Forbidden403Exception.php b/src/OpenSearch/Common/Exceptions/Forbidden403Exception.php index e6cc8d6a..8a307cd0 100644 --- a/src/OpenSearch/Common/Exceptions/Forbidden403Exception.php +++ b/src/OpenSearch/Common/Exceptions/Forbidden403Exception.php @@ -21,6 +21,11 @@ namespace OpenSearch\Common\Exceptions; +@trigger_error(Forbidden403Exception::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class Forbidden403Exception extends \Exception implements OpenSearchException { } diff --git a/src/OpenSearch/Common/Exceptions/InvalidArgumentException.php b/src/OpenSearch/Common/Exceptions/InvalidArgumentException.php index 78a12f1e..0ff06b20 100644 --- a/src/OpenSearch/Common/Exceptions/InvalidArgumentException.php +++ b/src/OpenSearch/Common/Exceptions/InvalidArgumentException.php @@ -21,6 +21,11 @@ namespace OpenSearch\Common\Exceptions; +@trigger_error(InvalidArgumentException::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class InvalidArgumentException extends \InvalidArgumentException implements OpenSearchException { } diff --git a/src/OpenSearch/Common/Exceptions/MaxRetriesException.php b/src/OpenSearch/Common/Exceptions/MaxRetriesException.php index fc1eb71c..9d4be5fa 100644 --- a/src/OpenSearch/Common/Exceptions/MaxRetriesException.php +++ b/src/OpenSearch/Common/Exceptions/MaxRetriesException.php @@ -21,6 +21,11 @@ namespace OpenSearch\Common\Exceptions; +@trigger_error(MaxRetriesException::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class MaxRetriesException extends TransportException implements OpenSearchException { } diff --git a/src/OpenSearch/Common/Exceptions/Missing404Exception.php b/src/OpenSearch/Common/Exceptions/Missing404Exception.php index 7105f475..c9b69be7 100644 --- a/src/OpenSearch/Common/Exceptions/Missing404Exception.php +++ b/src/OpenSearch/Common/Exceptions/Missing404Exception.php @@ -21,6 +21,11 @@ namespace OpenSearch\Common\Exceptions; +@trigger_error(Missing404Exception::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class Missing404Exception extends \Exception implements OpenSearchException { } diff --git a/src/OpenSearch/Common/Exceptions/NoDocumentsToGetException.php b/src/OpenSearch/Common/Exceptions/NoDocumentsToGetException.php index 8bd30eed..82fdd9fa 100644 --- a/src/OpenSearch/Common/Exceptions/NoDocumentsToGetException.php +++ b/src/OpenSearch/Common/Exceptions/NoDocumentsToGetException.php @@ -21,6 +21,11 @@ namespace OpenSearch\Common\Exceptions; +@trigger_error(NoDocumentsToGetException::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class NoDocumentsToGetException extends ServerErrorResponseException implements OpenSearchException { } diff --git a/src/OpenSearch/Common/Exceptions/RequestTimeout408Exception.php b/src/OpenSearch/Common/Exceptions/RequestTimeout408Exception.php index 80e26aa7..0892ea13 100644 --- a/src/OpenSearch/Common/Exceptions/RequestTimeout408Exception.php +++ b/src/OpenSearch/Common/Exceptions/RequestTimeout408Exception.php @@ -21,6 +21,11 @@ namespace OpenSearch\Common\Exceptions; +@trigger_error(RequestTimeout408Exception::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class RequestTimeout408Exception extends BadRequest400Exception implements OpenSearchException { } diff --git a/src/OpenSearch/ConnectionPool/AbstractConnectionPool.php b/src/OpenSearch/ConnectionPool/AbstractConnectionPool.php index 7d0ea6a6..ecb0f0b6 100644 --- a/src/OpenSearch/ConnectionPool/AbstractConnectionPool.php +++ b/src/OpenSearch/ConnectionPool/AbstractConnectionPool.php @@ -26,6 +26,11 @@ use OpenSearch\Connections\ConnectionFactoryInterface; use OpenSearch\Connections\ConnectionInterface; +@trigger_error(AbstractConnectionPool::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ abstract class AbstractConnectionPool implements ConnectionPoolInterface { /** diff --git a/src/OpenSearch/ConnectionPool/ConnectionPoolInterface.php b/src/OpenSearch/ConnectionPool/ConnectionPoolInterface.php index 42917d51..0c5da67d 100644 --- a/src/OpenSearch/ConnectionPool/ConnectionPoolInterface.php +++ b/src/OpenSearch/ConnectionPool/ConnectionPoolInterface.php @@ -23,6 +23,11 @@ use OpenSearch\Connections\ConnectionInterface; +@trigger_error(ConnectionPoolInterface::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ interface ConnectionPoolInterface { public function nextConnection(bool $force = false): ConnectionInterface; diff --git a/src/OpenSearch/ConnectionPool/Selectors/RandomSelector.php b/src/OpenSearch/ConnectionPool/Selectors/RandomSelector.php index ecb7b380..557a9063 100644 --- a/src/OpenSearch/ConnectionPool/Selectors/RandomSelector.php +++ b/src/OpenSearch/ConnectionPool/Selectors/RandomSelector.php @@ -23,6 +23,11 @@ use OpenSearch\Connections\ConnectionInterface; +@trigger_error(RandomSelector::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class RandomSelector implements SelectorInterface { /** diff --git a/src/OpenSearch/ConnectionPool/Selectors/RoundRobinSelector.php b/src/OpenSearch/ConnectionPool/Selectors/RoundRobinSelector.php index 8daeef60..1c0204bc 100644 --- a/src/OpenSearch/ConnectionPool/Selectors/RoundRobinSelector.php +++ b/src/OpenSearch/ConnectionPool/Selectors/RoundRobinSelector.php @@ -23,6 +23,11 @@ use OpenSearch\Connections\ConnectionInterface; +@trigger_error(RoundRobinSelector::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class RoundRobinSelector implements SelectorInterface { /** diff --git a/src/OpenSearch/ConnectionPool/Selectors/SelectorInterface.php b/src/OpenSearch/ConnectionPool/Selectors/SelectorInterface.php index eeb29086..2d7ba1f2 100644 --- a/src/OpenSearch/ConnectionPool/Selectors/SelectorInterface.php +++ b/src/OpenSearch/ConnectionPool/Selectors/SelectorInterface.php @@ -23,6 +23,11 @@ use OpenSearch\Connections\ConnectionInterface; +@trigger_error(SelectorInterface::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ interface SelectorInterface { /** diff --git a/src/OpenSearch/ConnectionPool/Selectors/StickyRoundRobinSelector.php b/src/OpenSearch/ConnectionPool/Selectors/StickyRoundRobinSelector.php index 7da041a0..5091ac40 100644 --- a/src/OpenSearch/ConnectionPool/Selectors/StickyRoundRobinSelector.php +++ b/src/OpenSearch/ConnectionPool/Selectors/StickyRoundRobinSelector.php @@ -23,6 +23,11 @@ use OpenSearch\Connections\ConnectionInterface; +@trigger_error(StickyRoundRobinSelector::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class StickyRoundRobinSelector implements SelectorInterface { /** diff --git a/src/OpenSearch/ConnectionPool/SimpleConnectionPool.php b/src/OpenSearch/ConnectionPool/SimpleConnectionPool.php index 29c14d26..0108a3e6 100644 --- a/src/OpenSearch/ConnectionPool/SimpleConnectionPool.php +++ b/src/OpenSearch/ConnectionPool/SimpleConnectionPool.php @@ -22,10 +22,14 @@ namespace OpenSearch\ConnectionPool; use OpenSearch\ConnectionPool\Selectors\SelectorInterface; -use OpenSearch\Connections\Connection; use OpenSearch\Connections\ConnectionFactoryInterface; use OpenSearch\Connections\ConnectionInterface; +@trigger_error(SimpleConnectionPool::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class SimpleConnectionPool extends AbstractConnectionPool implements ConnectionPoolInterface { /** diff --git a/src/OpenSearch/ConnectionPool/SniffingConnectionPool.php b/src/OpenSearch/ConnectionPool/SniffingConnectionPool.php index 2c6d6b83..d27d1228 100644 --- a/src/OpenSearch/ConnectionPool/SniffingConnectionPool.php +++ b/src/OpenSearch/ConnectionPool/SniffingConnectionPool.php @@ -28,6 +28,11 @@ use OpenSearch\Connections\ConnectionFactoryInterface; use OpenSearch\Connections\ConnectionInterface; +@trigger_error(SniffingConnectionPool::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class SniffingConnectionPool extends AbstractConnectionPool { /** diff --git a/src/OpenSearch/ConnectionPool/StaticConnectionPool.php b/src/OpenSearch/ConnectionPool/StaticConnectionPool.php index c074e8c8..0f16fbe6 100644 --- a/src/OpenSearch/ConnectionPool/StaticConnectionPool.php +++ b/src/OpenSearch/ConnectionPool/StaticConnectionPool.php @@ -24,9 +24,14 @@ use OpenSearch\Common\Exceptions\NoNodesAvailableException; use OpenSearch\ConnectionPool\Selectors\SelectorInterface; use OpenSearch\Connections\Connection; -use OpenSearch\Connections\ConnectionInterface; use OpenSearch\Connections\ConnectionFactoryInterface; +use OpenSearch\Connections\ConnectionInterface; + +@trigger_error(StaticConnectionPool::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class StaticConnectionPool extends AbstractConnectionPool implements ConnectionPoolInterface { /** diff --git a/src/OpenSearch/ConnectionPool/StaticNoPingConnectionPool.php b/src/OpenSearch/ConnectionPool/StaticNoPingConnectionPool.php index a4a85e32..9b3b6001 100644 --- a/src/OpenSearch/ConnectionPool/StaticNoPingConnectionPool.php +++ b/src/OpenSearch/ConnectionPool/StaticNoPingConnectionPool.php @@ -24,9 +24,14 @@ use OpenSearch\Common\Exceptions\NoNodesAvailableException; use OpenSearch\ConnectionPool\Selectors\SelectorInterface; use OpenSearch\Connections\Connection; -use OpenSearch\Connections\ConnectionInterface; use OpenSearch\Connections\ConnectionFactoryInterface; +use OpenSearch\Connections\ConnectionInterface; + +@trigger_error(StaticNoPingConnectionPool::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class StaticNoPingConnectionPool extends AbstractConnectionPool implements ConnectionPoolInterface { /** diff --git a/src/OpenSearch/Connections/Connection.php b/src/OpenSearch/Connections/Connection.php index 7279f56c..653a1bcb 100644 --- a/src/OpenSearch/Connections/Connection.php +++ b/src/OpenSearch/Connections/Connection.php @@ -21,18 +21,22 @@ namespace OpenSearch\Connections; +use Exception; +use GuzzleHttp\Ring\Core; +use GuzzleHttp\Ring\Exception\ConnectException; +use GuzzleHttp\Ring\Exception\RingException; use OpenSearch\Client; use OpenSearch\Common\Exceptions\BadRequest400Exception; use OpenSearch\Common\Exceptions\Conflict409Exception; use OpenSearch\Common\Exceptions\Curl\CouldNotConnectToHost; use OpenSearch\Common\Exceptions\Curl\CouldNotResolveHostException; use OpenSearch\Common\Exceptions\Curl\OperationTimeoutException; -use OpenSearch\Common\Exceptions\OpenSearchException; use OpenSearch\Common\Exceptions\Forbidden403Exception; use OpenSearch\Common\Exceptions\MaxRetriesException; use OpenSearch\Common\Exceptions\Missing404Exception; use OpenSearch\Common\Exceptions\NoDocumentsToGetException; use OpenSearch\Common\Exceptions\NoShardAvailableException; +use OpenSearch\Common\Exceptions\OpenSearchException; use OpenSearch\Common\Exceptions\RequestTimeout408Exception; use OpenSearch\Common\Exceptions\RoutingMissingException; use OpenSearch\Common\Exceptions\ScriptLangNotSupportedException; @@ -41,12 +45,13 @@ use OpenSearch\Common\Exceptions\Unauthorized401Exception; use OpenSearch\Serializers\SerializerInterface; use OpenSearch\Transport; -use Exception; -use GuzzleHttp\Ring\Core; -use GuzzleHttp\Ring\Exception\ConnectException; -use GuzzleHttp\Ring\Exception\RingException; use Psr\Log\LoggerInterface; +@trigger_error(Connection::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class Connection implements ConnectionInterface { /** diff --git a/src/OpenSearch/Connections/ConnectionFactory.php b/src/OpenSearch/Connections/ConnectionFactory.php index f95c2f29..f19d78b4 100644 --- a/src/OpenSearch/Connections/ConnectionFactory.php +++ b/src/OpenSearch/Connections/ConnectionFactory.php @@ -24,6 +24,11 @@ use OpenSearch\Serializers\SerializerInterface; use Psr\Log\LoggerInterface; +@trigger_error(ConnectionFactory::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class ConnectionFactory implements ConnectionFactoryInterface { /** diff --git a/src/OpenSearch/Connections/ConnectionFactoryInterface.php b/src/OpenSearch/Connections/ConnectionFactoryInterface.php index da209abd..ddb240cd 100644 --- a/src/OpenSearch/Connections/ConnectionFactoryInterface.php +++ b/src/OpenSearch/Connections/ConnectionFactoryInterface.php @@ -21,6 +21,11 @@ namespace OpenSearch\Connections; +@trigger_error(ConnectionFactoryInterface::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ interface ConnectionFactoryInterface { /** diff --git a/src/OpenSearch/Connections/ConnectionInterface.php b/src/OpenSearch/Connections/ConnectionInterface.php index 0e6280ae..791c5a79 100644 --- a/src/OpenSearch/Connections/ConnectionInterface.php +++ b/src/OpenSearch/Connections/ConnectionInterface.php @@ -21,10 +21,13 @@ namespace OpenSearch\Connections; -use OpenSearch\Serializers\SerializerInterface; use OpenSearch\Transport; -use Psr\Log\LoggerInterface; +@trigger_error(ConnectionInterface::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ interface ConnectionInterface { /** diff --git a/src/OpenSearch/EndpointFactory.php b/src/OpenSearch/EndpointFactory.php index 65bb5b36..61abbe25 100644 --- a/src/OpenSearch/EndpointFactory.php +++ b/src/OpenSearch/EndpointFactory.php @@ -4,6 +4,7 @@ use OpenSearch\Endpoints\AbstractEndpoint; use OpenSearch\Serializers\SerializerInterface; +use OpenSearch\Serializers\SmartSerializer; use ReflectionClass; /** @@ -16,9 +17,11 @@ class EndpointFactory implements EndpointFactoryInterface */ private array $endpoints = []; - public function __construct( - protected SerializerInterface $serializer, - ) { + private ?SerializerInterface $serializer; + + public function __construct(?SerializerInterface $serializer = null) + { + $this->serializer = $serializer; } /** @@ -33,6 +36,14 @@ public function getEndpoint(string $class): AbstractEndpoint return $this->endpoints[$class]; } + private function getSerializer(): SerializerInterface + { + if ($this->serializer === null) { + $this->serializer = new SmartSerializer(); + } + return $this->serializer; + } + /** * Creates an endpoint. * @@ -47,7 +58,7 @@ private function createEndpoint(string $class): AbstractEndpoint $constructor = $reflection->getConstructor(); if ($constructor && $constructor->getParameters()) { - return new $class($this->serializer); + return new $class($this->getSerializer()); } return new $class(); } diff --git a/src/OpenSearch/EndpointInterface.php b/src/OpenSearch/EndpointInterface.php index c28c63ef..f29783df 100644 --- a/src/OpenSearch/EndpointInterface.php +++ b/src/OpenSearch/EndpointInterface.php @@ -66,16 +66,14 @@ public function setId(int|string|null $docID): static; /** * Get the body of the request. - * - * @return array|string */ - public function getBody(): array|string; + public function getBody(): string|array|null; /** * Set the body of the request. * - * @param array $body + * @param string|iterable|null $body */ - public function setBody(array $body): static; + public function setBody(string|iterable|null $body): static; } diff --git a/src/OpenSearch/Endpoints/AbstractEndpoint.php b/src/OpenSearch/Endpoints/AbstractEndpoint.php index 0b049dae..31c19c09 100644 --- a/src/OpenSearch/Endpoints/AbstractEndpoint.php +++ b/src/OpenSearch/Endpoints/AbstractEndpoint.php @@ -22,11 +22,12 @@ namespace OpenSearch\Endpoints; use OpenSearch\Common\Exceptions\UnexpectedValueException; +use OpenSearch\EndpointInterface; use OpenSearch\Serializers\SerializerInterface; use function array_filter; -abstract class AbstractEndpoint +abstract class AbstractEndpoint implements EndpointInterface { /** * @var array @@ -85,7 +86,7 @@ abstract public function getMethod(): string; * @param mixed[] $params Array of parameters * @return $this */ - public function setParams(array $params) + public function setParams(array $params): static { $this->extractOptions($params); $this->checkUserParams($params); @@ -117,7 +118,7 @@ public function getIndex(): ?string * * @return $this */ - public function setIndex($index) + public function setIndex($index): static { if ($index === null) { return $this; @@ -134,12 +135,7 @@ public function setIndex($index) return $this; } - /** - * @param int|string|null $docID - * - * @return $this - */ - public function setId($docID) + public function setId(int|string|null $docID): static { if ($docID === null) { return $this; @@ -154,16 +150,12 @@ public function setId($docID) return $this; } - /** - * @return array|string - */ - public function getBody() + public function getBody(): string|array|null { return $this->body; } - - public function setBody(array $body) + public function setBody(string|iterable|null $body): static { $this->body = $body; diff --git a/src/OpenSearch/Endpoints/AsyncSearch/Submit.php b/src/OpenSearch/Endpoints/AsyncSearch/Submit.php index d10e801b..21edf79a 100644 --- a/src/OpenSearch/Endpoints/AsyncSearch/Submit.php +++ b/src/OpenSearch/Endpoints/AsyncSearch/Submit.php @@ -87,7 +87,7 @@ public function getMethod(): string return 'POST'; } - public function setBody($body): Submit + public function setBody($body): static { if (isset($body) !== true) { return $this; diff --git a/src/OpenSearch/Endpoints/DataFrameTransformDeprecated/PreviewTransform.php b/src/OpenSearch/Endpoints/DataFrameTransformDeprecated/PreviewTransform.php index e2db220a..3f949ea1 100644 --- a/src/OpenSearch/Endpoints/DataFrameTransformDeprecated/PreviewTransform.php +++ b/src/OpenSearch/Endpoints/DataFrameTransformDeprecated/PreviewTransform.php @@ -40,7 +40,7 @@ public function getMethod(): string return 'POST'; } - public function setBody($body): PreviewTransform + public function setBody($body): static { if (isset($body) !== true) { return $this; diff --git a/src/OpenSearch/Endpoints/DataFrameTransformDeprecated/PutTransform.php b/src/OpenSearch/Endpoints/DataFrameTransformDeprecated/PutTransform.php index ca259759..675dcced 100644 --- a/src/OpenSearch/Endpoints/DataFrameTransformDeprecated/PutTransform.php +++ b/src/OpenSearch/Endpoints/DataFrameTransformDeprecated/PutTransform.php @@ -50,7 +50,7 @@ public function getMethod(): string return 'PUT'; } - public function setBody($body): PutTransform + public function setBody($body): static { if (isset($body) !== true) { return $this; diff --git a/src/OpenSearch/Endpoints/DataFrameTransformDeprecated/UpdateTransform.php b/src/OpenSearch/Endpoints/DataFrameTransformDeprecated/UpdateTransform.php index 30b3dd2d..b5d6f13f 100644 --- a/src/OpenSearch/Endpoints/DataFrameTransformDeprecated/UpdateTransform.php +++ b/src/OpenSearch/Endpoints/DataFrameTransformDeprecated/UpdateTransform.php @@ -50,7 +50,7 @@ public function getMethod(): string return 'POST'; } - public function setBody($body): UpdateTransform + public function setBody($body): static { if (isset($body) !== true) { return $this; @@ -60,7 +60,7 @@ public function setBody($body): UpdateTransform return $this; } - public function setTransformId($transform_id): UpdateTransform + public function setTransformId($transform_id): static { if (isset($transform_id) !== true) { return $this; diff --git a/src/OpenSearch/Endpoints/Monitoring/Bulk.php b/src/OpenSearch/Endpoints/Monitoring/Bulk.php index a40ffdab..797a3f6b 100644 --- a/src/OpenSearch/Endpoints/Monitoring/Bulk.php +++ b/src/OpenSearch/Endpoints/Monitoring/Bulk.php @@ -52,7 +52,7 @@ public function getMethod(): string return 'POST'; } - public function setBody($body): Bulk + public function setBody($body): static { if (isset($body) !== true) { return $this; diff --git a/src/OpenSearch/Endpoints/SearchableSnapshots/Mount.php b/src/OpenSearch/Endpoints/SearchableSnapshots/Mount.php index 3f51d266..af223c10 100644 --- a/src/OpenSearch/Endpoints/SearchableSnapshots/Mount.php +++ b/src/OpenSearch/Endpoints/SearchableSnapshots/Mount.php @@ -54,7 +54,7 @@ public function getMethod(): string return 'POST'; } - public function setBody($body): Mount + public function setBody($body): static { if (isset($body) !== true) { return $this; @@ -64,7 +64,7 @@ public function setBody($body): Mount return $this; } - public function setRepository($repository): Mount + public function setRepository($repository): static { if (isset($repository) !== true) { return $this; @@ -74,7 +74,7 @@ public function setRepository($repository): Mount return $this; } - public function setSnapshot($snapshot): Mount + public function setSnapshot($snapshot): static { if (isset($snapshot) !== true) { return $this; diff --git a/src/OpenSearch/Handlers/SigV4Handler.php b/src/OpenSearch/Handlers/SigV4Handler.php index e45639c7..fa1edad1 100644 --- a/src/OpenSearch/Handlers/SigV4Handler.php +++ b/src/OpenSearch/Handlers/SigV4Handler.php @@ -13,8 +13,12 @@ use Psr\Http\Message\RequestInterface; use RuntimeException; +@trigger_error(SigV4Handler::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + /** * @phpstan-type RingPhpRequest array{http_method: string, scheme: string, uri: string, query_string?: string, version?: string, headers: array>, body: string|resource|null, client?: array} + * + * @deprecated in 2.3.2 and will be removed in 3.0.0. Use \OpenSearch\Aws\SigV4RequestFactory instead. */ class SigV4Handler { diff --git a/src/OpenSearch/HttpTransport.php b/src/OpenSearch/HttpTransport.php new file mode 100644 index 00000000..c7c4608e --- /dev/null +++ b/src/OpenSearch/HttpTransport.php @@ -0,0 +1,44 @@ +requestFactory->createRequest($method, $uri, $params, $body, $headers); + } + + /** + * {@inheritdoc} + */ + public function sendRequest( + string $method, + string $uri, + array $params = [], + mixed $body = null, + array $headers = [], + ): array|string|null { + $request = $this->createRequest($method, $uri, $params, $body, $headers); + $response = $this->client->sendRequest($request); + return $this->serializer->deserialize($response->getBody()->getContents(), $response->getHeaders()); + } + +} diff --git a/src/OpenSearch/LegacyTransportWrapper.php b/src/OpenSearch/LegacyTransportWrapper.php new file mode 100644 index 00000000..9b058216 --- /dev/null +++ b/src/OpenSearch/LegacyTransportWrapper.php @@ -0,0 +1,37 @@ +transport->performRequest($method, $uri, $params, $body); + $futureArray = $this->transport->resultOrFuture($promise); + if ($futureArray instanceof FutureArrayInterface) { + return $futureArray->wait(); + } + return $futureArray; + } + +} diff --git a/src/OpenSearch/Namespaces/AbstractNamespace.php b/src/OpenSearch/Namespaces/AbstractNamespace.php index ec908dca..d11b66ef 100644 --- a/src/OpenSearch/Namespaces/AbstractNamespace.php +++ b/src/OpenSearch/Namespaces/AbstractNamespace.php @@ -24,15 +24,21 @@ use OpenSearch\EndpointFactoryInterface; use OpenSearch\Endpoints\AbstractEndpoint; use OpenSearch\LegacyEndpointFactory; +use OpenSearch\LegacyTransportWrapper; use OpenSearch\Transport; +use OpenSearch\TransportInterface; abstract class AbstractNamespace { /** * @var \OpenSearch\Transport + * + * @deprecated in 2.3.2 and will be removed in 3.0.0. Use $httpTransport property instead. */ protected $transport; + protected TransportInterface $httpTransport; + protected EndpointFactoryInterface $endpointFactory; /** @@ -42,9 +48,15 @@ abstract class AbstractNamespace */ protected $endpoints; - public function __construct(Transport $transport, callable|EndpointFactoryInterface $endpointFactory) + public function __construct(TransportInterface|Transport $transport, callable|EndpointFactoryInterface $endpointFactory) { - $this->transport = $transport; + if (!$transport instanceof TransportInterface) { + @trigger_error('Passing an instance of \OpenSearch\Transport to ' . __METHOD__ . '() is deprecated in 2.3.2 and will be removed in 3.0.0. Pass an instance of \OpenSearch\TransportInterface instead.', E_USER_DEPRECATED); + $this->transport = $transport; + $this->httpTransport = new LegacyTransportWrapper($transport); + } else { + $this->httpTransport = $transport; + } if (is_callable($endpointFactory)) { @trigger_error('Passing a callable as $endpointFactory param to ' . __METHOD__ . '() is deprecated in 2.3.2 and will be removed in 3.0.0. Pass an instance of \OpenSearch\EndpointFactoryInterface instead.', E_USER_DEPRECATED); $endpoints = $endpointFactory; @@ -75,7 +87,7 @@ public function extractArgument(array &$params, string $arg) protected function performRequest(AbstractEndpoint $endpoint) { - $response = $this->transport->performRequest( + return $this->httpTransport->sendRequest( $endpoint->getMethod(), $endpoint->getURI(), $endpoint->getParams(), @@ -83,6 +95,5 @@ protected function performRequest(AbstractEndpoint $endpoint) $endpoint->getOptions() ); - return $this->transport->resultOrFuture($response, $endpoint->getOptions()); } } diff --git a/src/OpenSearch/Namespaces/BooleanRequestWrapper.php b/src/OpenSearch/Namespaces/BooleanRequestWrapper.php index 9c29f656..1e6f55b5 100644 --- a/src/OpenSearch/Namespaces/BooleanRequestWrapper.php +++ b/src/OpenSearch/Namespaces/BooleanRequestWrapper.php @@ -26,17 +26,51 @@ use OpenSearch\Endpoints\AbstractEndpoint; use OpenSearch\Transport; use GuzzleHttp\Ring\Future\FutureArrayInterface; +use OpenSearch\TransportInterface; +use Psr\Http\Client\ClientExceptionInterface; abstract class BooleanRequestWrapper { + /** + * Send a request with a boolean response. + * + * @return bool + * Returns FALSE for a 404 error, otherwise TRUE. + * + * @throws \Psr\Http\Client\ClientExceptionInterface + */ + public static function sendRequest(AbstractEndpoint $endpoint, TransportInterface $transport): bool + { + try { + $transport->sendRequest( + $endpoint->getMethod(), + $endpoint->getURI(), + $endpoint->getParams(), + $endpoint->getBody(), + $endpoint->getOptions() + ); + } catch (ClientExceptionInterface $e) { + if ($e->getCode() === 404) { + return false; + } + throw $e; + } + return true; + } + /** * Perform Request * * @throws Missing404Exception * @throws RoutingMissingException + * + * @deprecated in 2.3.2 and will be removed in 3.0.0. Use \OpenSearch\Namespaces\BooleanRequestWrapper::sendRequest() instead. */ public static function performRequest(AbstractEndpoint $endpoint, Transport $transport) { + @trigger_error( + __METHOD__ . '() is deprecated in 2.3.2 and will be removed in 3.0.0. Use \OpenSearch\Namespaces\BooleanRequestWrapper::sendRequest() instead.' + ); try { $response = $transport->performRequest( $endpoint->getMethod(), diff --git a/src/OpenSearch/Namespaces/ClusterNamespace.php b/src/OpenSearch/Namespaces/ClusterNamespace.php index 7d763a50..3ebcd0f8 100644 --- a/src/OpenSearch/Namespaces/ClusterNamespace.php +++ b/src/OpenSearch/Namespaces/ClusterNamespace.php @@ -174,7 +174,7 @@ public function existsComponentTemplate(array $params = []): bool $endpoint->setParams($params); $endpoint->setName($name); - return BooleanRequestWrapper::performRequest($endpoint, $this->transport); + return BooleanRequestWrapper::sendRequest($endpoint, $this->httpTransport); } /** diff --git a/src/OpenSearch/RequestFactory.php b/src/OpenSearch/RequestFactory.php new file mode 100644 index 00000000..e0729b97 --- /dev/null +++ b/src/OpenSearch/RequestFactory.php @@ -0,0 +1,48 @@ +uriFactory->createUri($uri); + $uri = $uri->withQuery(http_build_query($params)); + $request = $this->psrRequestFactory->createRequest($method, $uri); + if ($body !== null) { + $bodyJson = $this->serializer->serialize($body); + $bodyStream = $this->streamFactory->createStream($bodyJson); + $request = $request->withBody($bodyStream); + } + foreach ($headers as $name => $value) { + $request = $request->withHeader($name, $value); + } + return $request; + } + +} diff --git a/src/OpenSearch/RequestFactoryInterface.php b/src/OpenSearch/RequestFactoryInterface.php new file mode 100644 index 00000000..46562496 --- /dev/null +++ b/src/OpenSearch/RequestFactoryInterface.php @@ -0,0 +1,23 @@ + $params + * @param string|array|null $body + * @param array $headers + */ + public function createRequest( + string $method, + string $uri, + array $params = [], + string|array|null $body = null, + array $headers = [], + ): RequestInterface; +} diff --git a/src/OpenSearch/Transport.php b/src/OpenSearch/Transport.php index df6b5b98..cd4ecf07 100644 --- a/src/OpenSearch/Transport.php +++ b/src/OpenSearch/Transport.php @@ -21,13 +21,17 @@ namespace OpenSearch; +use GuzzleHttp\Ring\Future\FutureArrayInterface; use OpenSearch\Common\Exceptions; use OpenSearch\ConnectionPool\AbstractConnectionPool; -use OpenSearch\Connections\Connection; use OpenSearch\Connections\ConnectionInterface; -use GuzzleHttp\Ring\Future\FutureArrayInterface; use Psr\Log\LoggerInterface; +@trigger_error(Transport::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0.', E_USER_DEPRECATED); + +/** + * @deprecated in 2.3.2 and will be removed in 3.0.0. + */ class Transport { /** diff --git a/src/OpenSearch/TransportFactory.php b/src/OpenSearch/TransportFactory.php new file mode 100644 index 00000000..c137a867 --- /dev/null +++ b/src/OpenSearch/TransportFactory.php @@ -0,0 +1,134 @@ +httpClient; + } + + public function setHttpClient(?ClientInterface $httpClient): static + { + $this->httpClient = $httpClient; + return $this; + } + + protected function getRequestFactory(): ?RequestFactoryInterface + { + return $this->requestFactory; + } + + public function setRequestFactory(?RequestFactoryInterface $requestFactory): static + { + $this->requestFactory = $requestFactory; + return $this; + } + + protected function getPsrRequestFactory(): PsrRequestFactoryInterface + { + if ($this->psrRequestFactory === null) { + $this->psrRequestFactory = Psr17FactoryDiscovery::findRequestFactory(); + } + return $this->psrRequestFactory; + } + + public function setPsrRequestFactory(PsrRequestFactoryInterface $psrRequestFactory): static + { + $this->psrRequestFactory = $psrRequestFactory; + return $this; + } + + protected function getStreamFactory(): StreamFactoryInterface + { + if ($this->streamFactory === null) { + $this->streamFactory = Psr17FactoryDiscovery::findStreamFactory(); + } + return $this->streamFactory; + } + + public function setStreamFactory(StreamFactoryInterface $streamFactory): static + { + $this->streamFactory = $streamFactory; + return $this; + } + + protected function getUriFactory(): UriFactoryInterface + { + if ($this->uriFactory === null) { + $this->uriFactory = Psr17FactoryDiscovery::findUriFactory(); + } + return $this->uriFactory; + } + + public function setUriFactory(UriFactoryInterface $uriFactory): static + { + $this->uriFactory = $uriFactory; + return $this; + } + + protected function getSerializer(): Serializers\SerializerInterface + { + if ($this->serializer === null) { + $this->serializer = new SmartSerializer(); + } + return $this->serializer; + } + + public function setSerializer(Serializers\SerializerInterface $serializer): static + { + $this->serializer = $serializer; + return $this; + } + + /** + * Creates a new transport. + */ + public function create(): HttpTransport + { + if ($this->requestFactory === null) { + $psrRequestFactory = $this->getPsrRequestFactory(); + $streamFactory = $this->getStreamFactory(); + $uriFactory = $this->getUriFactory(); + $serializer = $this->getSerializer(); + + $this->requestFactory = new RequestFactory( + $psrRequestFactory, + $streamFactory, + $uriFactory, + $serializer + ); + } + if ($this->httpClient === null) { + $this->httpClient = Psr18ClientDiscovery::find(); + } + + return new HttpTransport($this->httpClient, $this->requestFactory, $this->getSerializer()); + } + +} diff --git a/src/OpenSearch/TransportInterface.php b/src/OpenSearch/TransportInterface.php new file mode 100644 index 00000000..5346fee0 --- /dev/null +++ b/src/OpenSearch/TransportInterface.php @@ -0,0 +1,27 @@ + $params + * @param string|array|null $body + * @param array $headers + * + * @throws \Psr\Http\Client\ClientExceptionInterface + */ + public function sendRequest( + string $method, + string $uri, + array $params = [], + string|array|null $body = null, + array $headers = [], + ): array|string|null; + +} diff --git a/tests/ClientTest.php b/tests/ClientTest.php index b580bae5..ce5a2bc7 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -21,443 +21,108 @@ namespace OpenSearch\Tests; -use GuzzleHttp\Ring\Client\MockHandler; -use GuzzleHttp\Ring\Future\FutureArray; -use Mockery as m; -use OpenSearch; use OpenSearch\Client; -use OpenSearch\ClientBuilder; -use OpenSearch\Common\Exceptions\MaxRetriesException; +use OpenSearch\Common\Exceptions\RuntimeException; +use OpenSearch\EndpointFactoryInterface; +use OpenSearch\Endpoints\Delete; +use OpenSearch\Transport; +use OpenSearch\TransportInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; /** * Class ClientTest * * @subpackage Tests + * @coversDefaultClass \OpenSearch\Client */ -class ClientTest extends \PHPUnit\Framework\TestCase +class ClientTest extends TestCase { - public function tearDown(): void - { - m::close(); - } + /** + * The client under test. + */ + private Client $client; - public function testConstructorIllegalPort() - { - $this->expectException(\OpenSearch\Common\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Could not parse URI'); + private EndpointFactoryInterface|MockObject $endpointFactory; - $client = OpenSearch\ClientBuilder::create()->setHosts(['localhost:abc'])->build(); - } + private TransportInterface|MockObject $transport; - public function testFromConfig() + /** + * {@inheritdoc} + */ + public function setUp(): void { - $params = [ - 'hosts' => [ - 'localhost:9200' - ], - 'retries' => 2, - 'handler' => ClientBuilder::multiHandler() - ]; - $client = ClientBuilder::fromConfig($params); - - $this->assertInstanceOf(Client::class, $client); + parent::setUp(); + $this->transport = $this->createMock(TransportInterface::class); + $this->endpointFactory = $this->createMock(EndpointFactoryInterface::class); + $registeredNamespaces = []; + $this->client = new Client($this->transport, $this->endpointFactory, $registeredNamespaces); } - public function testFromConfigBadParam() + /** + * @covers ::__call + */ + public function testUnknownNamespace(): void { - $params = [ - 'hosts' => [ - 'localhost:9200' - ], - 'retries' => 2, - 'imNotReal' => 5 - ]; - - $this->expectException(\OpenSearch\Common\Exceptions\RuntimeException::class); - $this->expectExceptionMessage('Unknown parameters provided: imNotReal'); - - $client = ClientBuilder::fromConfig($params); + $this->expectException(\BadMethodCallException::class); + $this->client->foo(); } - public function testFromConfigBadParamQuiet() - { - $params = [ - 'hosts' => [ - 'localhost:9200' - ], - 'retries' => 2, - 'imNotReal' => 5 - ]; - $client = ClientBuilder::fromConfig($params, true); - - $this->assertInstanceOf(Client::class, $client); - } public function testIndexCannotBeNullForDelete() { - $client = ClientBuilder::create()->build(); + $this->endpointFactory->expects($this->once()) + ->method('getEndpoint') + ->with(Delete::class) + ->willReturn(new Delete()); - $this->expectException(OpenSearch\Common\Exceptions\RuntimeException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('index is required for delete'); - $client->delete( + $this->client->delete( [ - 'index' => null, - 'id' => 'test' + 'index' => null, + 'id' => 'test' ] ); } public function testIdCannotBeNullForDelete() { - $client = ClientBuilder::create()->build(); + $this->endpointFactory->expects($this->once()) + ->method('getEndpoint') + ->with(Delete::class) + ->willReturn(new Delete()); - $this->expectException(OpenSearch\Common\Exceptions\RuntimeException::class); + $this->expectException(RuntimeException::class); $this->expectExceptionMessage('id is required for delete'); - $client->delete( + $this->client->delete( [ - 'index' => 'test', - 'id' => null + 'index' => 'test', + 'id' => null ] ); } - public function testMaxRetriesException() + /** + * @covers ::request + */ + public function testSendRawRequest(): void { - $client = OpenSearch\ClientBuilder::create() - ->setHosts(["localhost:1"]) - ->setRetries(0) - ->build(); - - $searchParams = [ - 'index' => 'test', - 'body' => [ - 'query' => [ - 'match_all' => [] - ] - ] - ]; - - $client = OpenSearch\ClientBuilder::create() - ->setHosts(["localhost:1"]) - ->setRetries(0) - ->build(); - - try { - $client->search($searchParams); - $this->fail("Should have thrown CouldNotConnectToHost"); - } catch (OpenSearch\Common\Exceptions\Curl\CouldNotConnectToHost $e) { - // All good - $previous = $e->getPrevious(); - $this->assertInstanceOf(MaxRetriesException::class, $previous); - } catch (\Exception $e) { - throw $e; - } - - - $client = OpenSearch\ClientBuilder::create() - ->setHosts(["localhost:1"]) - ->setRetries(0) - ->build(); - - try { - $client->search($searchParams); - $this->fail("Should have thrown TransportException"); - } catch (OpenSearch\Common\Exceptions\TransportException $e) { - // All good - $previous = $e->getPrevious(); - $this->assertInstanceOf(MaxRetriesException::class, $previous); - } catch (\Exception $e) { - throw $e; - } - } - - public function testInlineHosts() - { - $client = OpenSearch\ClientBuilder::create()->setHosts( - [ - 'localhost:9200' - ] - )->build(); - $host = $client->transport->getConnection(); - $this->assertSame("localhost", $host->getHost()); - $this->assertSame(9200, $host->getPort()); - $this->assertSame("http", $host->getTransportSchema()); - - - $client = OpenSearch\ClientBuilder::create()->setHosts( - [ - 'http://localhost:9200' - ] - )->build(); - $host = $client->transport->getConnection(); - $this->assertSame("localhost", $host->getHost()); - $this->assertSame(9200, $host->getPort()); - $this->assertSame("http", $host->getTransportSchema()); - - $client = OpenSearch\ClientBuilder::create()->setHosts( - [ - 'http://foo.com:9200' - ] - )->build(); - $host = $client->transport->getConnection(); - $this->assertSame("foo.com", $host->getHost()); - $this->assertSame(9200, $host->getPort()); - $this->assertSame("http", $host->getTransportSchema()); - - $client = OpenSearch\ClientBuilder::create()->setHosts( - [ - 'https://foo.com:9200' - ] - )->build(); - $host = $client->transport->getConnection(); - $this->assertSame("foo.com", $host->getHost()); - $this->assertSame(9200, $host->getPort()); - $this->assertSame("https", $host->getTransportSchema()); - - - $client = OpenSearch\ClientBuilder::create()->setHosts( - [ - 'https://user:pass@foo.com:9200' - ] - )->build(); - $host = $client->transport->getConnection(); - $this->assertSame("foo.com", $host->getHost()); - $this->assertSame(9200, $host->getPort()); - $this->assertSame("https", $host->getTransportSchema()); - $this->assertSame("user:pass", $host->getUserPass()); - - $client = OpenSearch\ClientBuilder::create()->setHosts( - [ - 'https://user:pass@the_foo.com:9200' - ] - )->build(); - $host = $client->transport->getConnection(); - $this->assertSame("the_foo.com", $host->getHost()); - $this->assertSame(9200, $host->getPort()); - $this->assertSame("https", $host->getTransportSchema()); - $this->assertSame("user:pass", $host->getUserPass()); - } - - public function testExtendedHosts() - { - $client = OpenSearch\ClientBuilder::create()->setHosts( - [ - [ - 'host' => 'localhost', - 'port' => 9200, - 'scheme' => 'http' - ] - ] - )->build(); - $host = $client->transport->getConnection(); - $this->assertSame("localhost", $host->getHost()); - $this->assertSame(9200, $host->getPort()); - $this->assertSame("http", $host->getTransportSchema()); - - - $client = OpenSearch\ClientBuilder::create()->setHosts( - [ - [ - 'host' => 'foo.com', - 'port' => 9200, - 'scheme' => 'http' - ] - ] - )->build(); - $host = $client->transport->getConnection(); - $this->assertSame("foo.com", $host->getHost()); - $this->assertSame(9200, $host->getPort()); - $this->assertSame("http", $host->getTransportSchema()); - - - $client = OpenSearch\ClientBuilder::create()->setHosts( - [ - [ - 'host' => 'foo.com', - 'port' => 9200, - 'scheme' => 'https' - ] - ] - )->build(); - $host = $client->transport->getConnection(); - $this->assertSame("foo.com", $host->getHost()); - $this->assertSame(9200, $host->getPort()); - $this->assertSame("https", $host->getTransportSchema()); - - - $client = OpenSearch\ClientBuilder::create()->setHosts( - [ - [ - 'host' => 'foo.com', - 'scheme' => 'http' - ] - ] - )->build(); - $host = $client->transport->getConnection(); - $this->assertSame("foo.com", $host->getHost()); - $this->assertSame(9200, $host->getPort()); - $this->assertSame("http", $host->getTransportSchema()); - + $this->transport->expects($this->once()) + ->method('sendRequest') + ->with('GET', '/', ['foo' => 'bar'], 'whizz') + ->willReturn(['bang']); - $client = OpenSearch\ClientBuilder::create()->setHosts( - [ - [ - 'host' => 'foo.com' - ] - ] - )->build(); - $host = $client->transport->getConnection(); - $this->assertSame("foo.com", $host->getHost()); - $this->assertSame(9200, $host->getPort()); - $this->assertSame("http", $host->getTransportSchema()); - - - $client = OpenSearch\ClientBuilder::create()->setHosts( - [ - [ - 'host' => 'foo.com', - 'port' => 9500, - 'scheme' => 'https' - ] - ] - )->build(); - $host = $client->transport->getConnection(); - $this->assertSame("foo.com", $host->getHost()); - $this->assertSame(9500, $host->getPort()); - $this->assertSame("https", $host->getTransportSchema()); - - - try { - $client = OpenSearch\ClientBuilder::create()->setHosts( - [ - [ - 'port' => 9200, - 'scheme' => 'http' - ] - ] - )->build(); - $this->fail("Expected RuntimeException from missing host, none thrown"); - } catch (OpenSearch\Common\Exceptions\RuntimeException $e) { - // good - } - - // Underscore host, questionably legal - $client = OpenSearch\ClientBuilder::create()->setHosts( - [ - [ - 'host' => 'the_foo.com' - ] - ] - )->build(); - $host = $client->transport->getConnection(); - $this->assertSame("the_foo.com", $host->getHost()); - $this->assertSame(9200, $host->getPort()); - $this->assertSame("http", $host->getTransportSchema()); - - - // Special characters in user/pass, would break inline - $client = OpenSearch\ClientBuilder::create()->setHosts( - [ - [ - 'host' => 'foo.com', - 'user' => 'user', - 'pass' => 'abc#$@?%!abc' - ] - ] - )->build(); - $host = $client->transport->getConnection(); - $this->assertSame("foo.com", $host->getHost()); - $this->assertSame(9200, $host->getPort()); - $this->assertSame("http", $host->getTransportSchema()); - $this->assertSame("user:abc#$@?%!abc", $host->getUserPass()); - } - - public function testClientLazy() - { - $handler = new MockHandler([ - 'status' => 200, - 'transfer_stats' => [ - 'total_time' => 100 - ], - 'body' => '{test}', - 'effective_url' => 'localhost' + $response = $this->client->request('GET', '/', [ + 'params' => ['foo' => 'bar'], + 'body' => 'whizz', ]); - $builder = ClientBuilder::create(); - $builder->setHosts(['somehost']); - $builder->setHandler($handler); - $client = $builder->build(); - - $params = [ - 'client' => [ - 'future' => 'lazy', - ] - ]; - $result = $client->info($params); - $this->assertInstanceOf(FutureArray::class, $result); - } - - public function testExtractArgumentIterable() - { - $client = OpenSearch\ClientBuilder::create()->build(); - // array iterator can be casted to array back, so make more real with IteratorIterator - $body = new \IteratorIterator(new \ArrayIterator([1, 2, 3])); - $params = ['body' => $body]; - $argument = $client->extractArgument($params, 'body'); - $this->assertEquals($body, $argument); - $this->assertCount(0, $params); - $this->assertInstanceOf(\IteratorIterator::class, $argument); - } - - /** @test */ - public function sendRawRequest(): void - { - $transport = $this->createMock(OpenSearch\Transport::class); - $endpointFactory = $this->createMock(OpenSearch\EndpointFactoryInterface::class); - $client = new OpenSearch\Client($transport, $endpointFactory, []); - - $transport->expects($this->once())->method('performRequest')->with('GET', '/', [], null, []); - $client->request('GET', '/'); + $this->assertEquals(['bang'], $response); } - /** @test */ - public function sendRawRequestWithBody(): void - { - $transport = $this->createMock(OpenSearch\Transport::class); - $endpointFactory = $this->createMock(OpenSearch\EndpointFactoryInterface::class); - $client = new OpenSearch\Client($transport, $endpointFactory, []); - $body = ['query' => ['match' => ['text_entry' => 'long live king']]]; - - $transport->expects($this->once())->method('performRequest')->with('GET', '/shakespeare/_search', [], $body, []); - - $client->request('GET', '/shakespeare/_search', compact('body')); - } - - /** @test */ - public function sendRawRequestWithParams(): void - { - $transport = $this->createMock(OpenSearch\Transport::class); - $endpointFactory = $this->createMock(OpenSearch\EndpointFactoryInterface::class); - $client = new OpenSearch\Client($transport, $endpointFactory, []); - $params = ['foo' => 'bar']; - - $transport->expects($this->once())->method('performRequest')->with('GET', '/_search', $params, null, []); - - $client->request('GET', '/_search', compact('params')); - } - - /** @test */ - public function sendRawRequestWithOptions(): void - { - $transport = $this->createMock(OpenSearch\Transport::class); - $endpointFactory = $this->createMock(OpenSearch\EndpointFactoryInterface::class); - $client = new OpenSearch\Client($transport, $endpointFactory, []); - $options = ['client' => ['future' => 'lazy']]; - - $transport->expects($this->once())->method('performRequest')->with('GET', '/', [], null, $options); - - $client->request('GET', '/', compact('options')); - } } diff --git a/tests/Endpoints/AbstractEndpointTest.php b/tests/Endpoints/AbstractEndpointTest.php index 7a3e7b87..5c28ca69 100644 --- a/tests/Endpoints/AbstractEndpointTest.php +++ b/tests/Endpoints/AbstractEndpointTest.php @@ -21,18 +21,17 @@ namespace OpenSearch\Tests\Endpoints; +use OpenSearch\EndpointInterface; use OpenSearch\Endpoints\AbstractEndpoint; use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; /** * @covers \OpenSearch\Endpoints\AbstractEndpoint */ -class AbstractEndpointTest extends \PHPUnit\Framework\TestCase +class AbstractEndpointTest extends TestCase { - /** - * @var AbstractEndpoint&MockObject - */ - private $endpoint; + private EndpointInterface&MockObject $endpoint; protected function setUp(): void { diff --git a/tests/HttpTransportTest.php b/tests/HttpTransportTest.php new file mode 100644 index 00000000..6c566bb1 --- /dev/null +++ b/tests/HttpTransportTest.php @@ -0,0 +1,57 @@ +createMock(RequestInterface::class); + + $requestFactory = $this->createMock(RequestFactoryInterface::class); + $requestFactory->expects($this->once()) + ->method('createRequest') + ->with($this->anything()) + ->willReturn($request); + + $bodyStream = $this->createMock(StreamInterface::class); + $bodyStream->expects($this->once()) + ->method('getContents') + ->willReturn('{"foo":"bar"}'); + + $response = $this->createMock(ResponseInterface::class); + $response->expects($this->once()) + ->method('getBody') + ->willReturn($bodyStream); + + $client = $this->createMock(ClientInterface::class); + $client->expects($this->once()) + ->method('sendRequest') + ->with($request) + ->willReturn($response); + + $serializer = new SmartSerializer(); + + $transport = new HttpTransport($client, $requestFactory, $serializer); + $response = $transport->sendRequest('GET', '/'); + + $this->assertEquals(['foo' => 'bar'], $response); + } +} diff --git a/tests/TransportTest.php b/tests/LegacyTransportTest.php similarity index 97% rename from tests/TransportTest.php rename to tests/LegacyTransportTest.php index 96469261..582f87ca 100644 --- a/tests/TransportTest.php +++ b/tests/LegacyTransportTest.php @@ -21,19 +21,23 @@ namespace OpenSearch\Tests; +use GuzzleHttp\Ring\Future\FutureArray; +use GuzzleHttp\Ring\Future\FutureArrayInterface; use OpenSearch\Common\Exceptions\ServerErrorResponseException; use OpenSearch\ConnectionPool\AbstractConnectionPool; use OpenSearch\Connections\Connection; -use OpenSearch\Serializers\SerializerInterface; use OpenSearch\Transport; -use GuzzleHttp\Ring\Future\FutureArray; -use GuzzleHttp\Ring\Future\FutureArrayInterface; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use React\Promise\Deferred; -class TransportTest extends TestCase +/** + * Legacy transport test. + * + * @group legacy + */ +class LegacyTransportTest extends TestCase { /** * @var Connection|MockObject diff --git a/util/Endpoint.php b/util/Endpoint.php index c91ab10f..4b7fc189 100644 --- a/util/Endpoint.php +++ b/util/Endpoint.php @@ -544,15 +544,18 @@ private function extractParamsDescription(int $space): string if (in_array($param, $this->addedPartInDoc)) { continue; } + $type = $values['type'] ?? 'any'; + // var_dump($type); + // var_dump($values); $result .= sprintf( " * \$params['%s']%s = (%s) %s%s%s%s\n", $param, str_repeat(' ', $space - strlen($param)), - $values['type'] ?? 'any', + $type, $values['description'] ?? '', isset($values['required']) && $values['required'] ? ' (Required)' : '', isset($values['options']) ? sprintf(" (Options = %s)", implode(',', $values['options'])) : '', - isset($values['default']) ? sprintf(" (Default = %s)", $values['type'] === 'boolean' ? ($values['default'] ? 'true' : 'false') : (is_array($values['default']) ? implode(',', $values['default']) : $values['default'])) : '' + isset($values['default']) ? sprintf(" (Default = %s)", ($type === 'boolean') ? ($values['default'] ? 'true' : 'false') : (is_array($values['default']) ? implode(',', $values['default']) : $values['default'])) : '' ); } return $result; diff --git a/util/template/client-class b/util/template/client-class index 99971086..53782840 100644 --- a/util/template/client-class +++ b/util/template/client-class @@ -21,13 +21,10 @@ declare(strict_types=1); namespace OpenSearch; -use OpenSearch\Common\Exceptions\BadMethodCallException; -use OpenSearch\Common\Exceptions\NoNodesAvailableException; use OpenSearch\Endpoints\AbstractEndpoint; -use OpenSearch\Namespaces\NamespaceBuilderInterface; use OpenSearch\Namespaces\BooleanRequestWrapper; +use OpenSearch\Namespaces\NamespaceBuilderInterface; :use-namespaces -use OpenSearch\Traits\DeprecatedPropertyTrait; /** * Class Client @@ -41,9 +38,13 @@ class Client /** * @var Transport + * + * @deprecated in 2.3.2 and will be removed in 3.0.0. */ public $transport; + private TransportInterface $httpTransport; + /** * @var array */ @@ -67,13 +68,19 @@ class Client /** * Client constructor * - * @param Transport $transport + * @param TransportInterface|Transport $transport * @param callable|EndpointFactoryInterface $endpointFactory * @param NamespaceBuilderInterface[] $registeredNamespaces */ - public function __construct(Transport $transport, callable|EndpointFactoryInterface $endpointFactory, array $registeredNamespaces) + public function __construct(TransportInterface|Transport $transport, callable|EndpointFactoryInterface $endpointFactory, array $registeredNamespaces) { - $this->transport = $transport; + if (!$transport instanceof TransportInterface) { + @trigger_error('Passing an instance of \OpenSearch\Transport to ' . __METHOD__ . '() is deprecated in 2.3.2 and will be removed in 3.0.0. Pass an instance of \OpenSearch\TransportInterface instead.', E_USER_DEPRECATED); + $this->transport = $transport; + $this->httpTransport = new LegacyTransportWrapper($transport); + } else { + $this->httpTransport = $transport; + } if (is_callable($endpointFactory)) { @trigger_error('Passing a callable as the $endpointFactory param in ' . __METHOD__ . ' is deprecated in 2.3.2 and will be removed in 3.0.0. Pass an instance of \OpenSearch\EndpointFactoryInterface instead.', E_USER_DEPRECATED); $endpoints = $endpointFactory; @@ -103,14 +110,14 @@ class Client * Catchall for registered namespaces * * @return object - * @throws BadMethodCallException if the namespace cannot be found + * @throws \BadMethodCallException if the namespace cannot be found */ public function __call(string $name, array $arguments) { if (isset($this->registeredNamespaces[$name])) { return $this->registeredNamespaces[$name]; } - throw new BadMethodCallException("Namespace [$name] not found"); + throw new \BadMethodCallException("Namespace [$name] not found"); } /** @@ -133,34 +140,33 @@ class Client } /** - * Sends a raw request to the cluster - * @return callable|array - * @throws NoNodesAvailableException + * Send a raw request to the cluster. + * + * @throws \Psr\Http\Client\ClientExceptionInterface */ - public function request(string $method, string $uri, array $attributes = []) + public function request(string $method, string $uri, array $attributes = []): array|string|null { $params = $attributes['params'] ?? []; $body = $attributes['body'] ?? null; $options = $attributes['options'] ?? []; - $promise = $this->transport->performRequest($method, $uri, $params, $body, $options); - - return $this->transport->resultOrFuture($promise, $options); + return $this->httpTransport->sendRequest($method, $uri, $params, $body, $options['headers'] ?? []); } /** - * @return callable|array + * Send a request for an endpoint. + * + * @throws \Psr\Http\Client\ClientExceptionInterface */ - private function performRequest(AbstractEndpoint $endpoint) + private function performRequest(AbstractEndpoint $endpoint): array|string|null { - $promise = $this->transport->performRequest( + return $this->httpTransport->sendRequest( $endpoint->getMethod(), $endpoint->getURI(), $endpoint->getParams(), $endpoint->getBody(), $endpoint->getOptions() ); - - return $this->transport->resultOrFuture($promise, $endpoint->getOptions()); } + } diff --git a/util/template/endpoint-function-bool b/util/template/endpoint-function-bool index d4c90d6d..f38a848a 100644 --- a/util/template/endpoint-function-bool +++ b/util/template/endpoint-function-bool @@ -8,5 +8,5 @@ $endpoint = $this->endpointFactory->getEndpoint(:EndpointClass::class); $endpoint->setParams($params); :setparam - return BooleanRequestWrapper::performRequest($endpoint, $this->transport); + return BooleanRequestWrapper::sendRequest($endpoint, $this->httpTransport); } diff --git a/util/template/namespace-class b/util/template/namespace-class index 7ea1ddd8..6a1c0d23 100644 --- a/util/template/namespace-class +++ b/util/template/namespace-class @@ -4,8 +4,6 @@ declare(strict_types=1); namespace OpenSearch\Namespaces; -use OpenSearch\Namespaces\AbstractNamespace; - /** * Class :namespace * diff --git a/util/template/namespace-property b/util/template/namespace-property index 7b88dd7f..c7ae3a27 100644 --- a/util/template/namespace-property +++ b/util/template/namespace-property @@ -2,4 +2,3 @@ * @var :namespaceNamespace */ protected $:var_namespace; - \ No newline at end of file diff --git a/util/template/set-bulk-body b/util/template/set-bulk-body index ece8963f..4ec05057 100644 --- a/util/template/set-bulk-body +++ b/util/template/set-bulk-body @@ -1,20 +1,22 @@ - public function setBody($body): static + public function setBody(string|iterable|null $body): static { - if (isset($body) !== true) { + if (is_null($body)) { return $this; } - if (is_array($body) === true || $body instanceof Traversable) { - foreach ($body as $item) { - $this->body .= $this->serializer->serialize($item) . "\n"; + + if (is_string($body)) { + if (!str_ends_with($body, "\n")) { + $body .= "\n"; } - } elseif (is_string($body)) { $this->body = $body; - if (substr($body, -1) != "\n") { - $this->body .= "\n"; - } - } else { - throw new InvalidArgumentException("Body must be an array, traversable object or string"); + return $this; } + + // Must be an iterable. + foreach ($body as $item) { + $this->body .= $this->serializer->serialize($item) . "\n"; + } + return $this; } diff --git a/util/template/set-part b/util/template/set-part index e84cc82e..47e7d422 100644 --- a/util/template/set-part +++ b/util/template/set-part @@ -1,7 +1,7 @@ public function set:Part($:part): static { - if (isset($:part) !== true) { + if (is_null($:part)) { return $this; } $this->:part = $:part;