Skip to content

Commit

Permalink
Handle HTTP exceptions in HttpTransport (#254)
Browse files Browse the repository at this point in the history
* Handle HTTP exceptions in HttpTransport

Signed-off-by: Kim Pepper <[email protected]>

* Generate Client

Signed-off-by: Kim Pepper <[email protected]>

---------

Signed-off-by: Kim Pepper <[email protected]>
  • Loading branch information
kimpepper authored Jan 17, 2025
1 parent 274adf2 commit d30188f
Show file tree
Hide file tree
Showing 41 changed files with 671 additions and 283 deletions.
126 changes: 4 additions & 122 deletions src/OpenSearch/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,6 @@

namespace OpenSearch;

use OpenSearch\Common\Exceptions\BadRequest400Exception;
use OpenSearch\Common\Exceptions\Conflict409Exception;
use OpenSearch\Common\Exceptions\Forbidden403Exception;
use OpenSearch\Common\Exceptions\Missing404Exception;
use OpenSearch\Common\Exceptions\NoDocumentsToGetException;
use OpenSearch\Common\Exceptions\NoShardAvailableException;
use OpenSearch\Common\Exceptions\RequestTimeout408Exception;
use OpenSearch\Common\Exceptions\RoutingMissingException;
use OpenSearch\Common\Exceptions\ScriptLangNotSupportedException;
use OpenSearch\Common\Exceptions\ServerErrorResponseException;
use OpenSearch\Common\Exceptions\Unauthorized401Exception;
use OpenSearch\Endpoints\AbstractEndpoint;
use OpenSearch\Namespaces\BooleanRequestWrapper;
use OpenSearch\Namespaces\NamespaceBuilderInterface;
Expand Down Expand Up @@ -106,11 +95,6 @@ class Client
*/
protected $registeredNamespaces = [];

/**
* @deprecated in 2.3.2 and will be removed in 3.0.0.
*/
private bool $throwExceptions = false;

/**
* @var AsyncSearchNamespace
*/
Expand Down Expand Up @@ -284,13 +268,11 @@ class Client
* @param TransportInterface|Transport $transport
* @param callable|EndpointFactoryInterface $endpointFactory
* @param NamespaceBuilderInterface[] $registeredNamespaces
* @param bool $throwExceptions
*/
public function __construct(
TransportInterface|Transport $transport,
callable|EndpointFactoryInterface $endpointFactory,
array $registeredNamespaces,
bool $throwExceptions = false,
) {
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);
Expand All @@ -311,13 +293,6 @@ public function __construct(
}
$this->endpoints = $endpoints;
$this->endpointFactory = $endpointFactory;
if ($throwExceptions === true) {
@trigger_error(
'The $throwExceptions parameter is deprecated in 2.4.0 and will be removed in 3.0.0. Check the response \'status_code\' instead',
E_USER_DEPRECATED
);
$this->throwExceptions = true;
}
$this->asyncSearch = new AsyncSearchNamespace($transport, $this->endpointFactory);
$this->asynchronousSearch = new AsynchronousSearchNamespace($transport, $this->endpointFactory);
$this->cat = new CatNamespace($transport, $this->endpointFactory);
Expand Down Expand Up @@ -2098,7 +2073,7 @@ public function extractArgument(array &$params, string $arg)
* Send a raw request to the cluster.
*
* @throws \Psr\Http\Client\ClientExceptionInterface
* @throws \OpenSearch\Common\Exceptions\OpenSearchException
* @throws \OpenSearch\Exception\HttpExceptionInterface
*/
public function request(
string $method,
Expand All @@ -2109,117 +2084,24 @@ public function request(
$body = $attributes['body'] ?? null;
$options = $attributes['options'] ?? [];

$response = $this->httpTransport->sendRequest($method, $uri, $params, $body, $options['headers'] ?? []);

// @todo: Remove this in the next major release.
// Throw legacy exceptions.
if ($this->throwExceptions) {
if (isset($response['status']) && $response['status'] >= 400) {
$this->throwLegacyException($response);
}
}

return $response;
return $this->httpTransport->sendRequest($method, $uri, $params, $body, $options['headers'] ?? []);
}

/**
* Send a request for an endpoint.
*
* @throws \Psr\Http\Client\ClientExceptionInterface
* @throws \OpenSearch\Common\Exceptions\OpenSearchException
* @throws \OpenSearch\Exception\HttpExceptionInterface
*/
private function performRequest(AbstractEndpoint $endpoint): array|string|null
{
$response = $this->httpTransport->sendRequest(
return $this->httpTransport->sendRequest(
$endpoint->getMethod(),
$endpoint->getURI(),
$endpoint->getParams(),
$endpoint->getBody(),
$endpoint->getOptions()
);

// @todo: Remove this in the next major release.
// Throw legacy exceptions.
if ($this->throwExceptions) {
if (isset($response['status']) && $response['status'] >= 400) {
$this->throwLegacyException($response);
}
}

return $response;
}

/**
* Throw legacy exceptions.
*
* @param array<string,mixed> $response
*
* @throws \OpenSearch\Common\Exceptions\OpenSearchException
*/
private function throwLegacyException(array $response): void
{
if ($response['status'] >= 400 && $response['status'] < 500) {
$this->throwLegacyClientException($response);
}
if ($response['status'] >= 500) {
$this->throwLegacyServerException($response);
}
}

/**
* Throw legacy client exceptions based on status code.
*
* @throws \OpenSearch\Common\Exceptions\OpenSearchException
*/
private function throwLegacyClientException($response): void
{
$statusCode = $response['status_code'];
$responseBody = $this->convertBodyToString($response['body'], $statusCode);
throw match ($statusCode) {
401 => new Unauthorized401Exception($responseBody, $statusCode),
403 => new Forbidden403Exception($responseBody, $statusCode),
404 => new Missing404Exception($responseBody, $statusCode),
409 => new Conflict409Exception($responseBody, $statusCode),
400 => (str_contains($responseBody, 'script_lang not supported'))
? new ScriptLangNotSupportedException($responseBody . $statusCode)
: new BadRequest400Exception($responseBody, $statusCode),
408 => new RequestTimeout408Exception($responseBody, $statusCode),
default => new BadRequest400Exception($responseBody, $statusCode),
};
}

/**
* Throw legacy server exceptions based on status code.
*
* @throws \OpenSearch\Common\Exceptions\OpenSearchException
*/
private function throwLegacyServerException($response): void
{
$statusCode = $response['status_code'];
$error = $response['body']['error'] ?? [];
$reason = $error['reason'] ?? 'undefined reason';
$type = $error['type'] ?? 'undefined type';
$errorMessage = "$type: $reason";
$responseBody = $this->convertBodyToString($response['body'], $statusCode);

$exception = new ServerErrorResponseException($responseBody, $statusCode);
if ($statusCode === 500) {
if (str_contains($responseBody, "RoutingMissingException")) {
$exception = new RoutingMissingException($errorMessage, $statusCode);
} elseif (preg_match('/ActionRequestValidationException.+ no documents to get/', $responseBody) === 1) {
$exception = new NoDocumentsToGetException($errorMessage, $statusCode);
} elseif (str_contains($responseBody, 'NoShardAvailableActionException')) {
$exception = new NoShardAvailableException($errorMessage, $statusCode);
}
}
throw $exception;
}

private function convertBodyToString(mixed $body, int $statusCode): string
{
return empty($body)
? "Unknown $statusCode error from OpenSearch"
: (is_string($body) ? $body : json_encode($body));
}

}
8 changes: 6 additions & 2 deletions src/OpenSearch/Common/Exceptions/Forbidden403Exception.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@

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);
use OpenSearch\Exception\ForbiddenHttpException;

@trigger_error(Forbidden403Exception::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0. Use \OpenSearch\Exception\ForbiddenHttpException instead', E_USER_DEPRECATED);

/**
* @deprecated in 2.3.2 and will be removed in 3.0.0.
*
* @see \OpenSearch\Exception\ForbiddenHttpException
*/
class Forbidden403Exception extends \Exception implements OpenSearchException
class Forbidden403Exception extends ForbiddenHttpException
{
}
11 changes: 9 additions & 2 deletions src/OpenSearch/Common/Exceptions/Missing404Exception.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,18 @@

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);
use OpenSearch\Exception\NotFoundHttpException;

@trigger_error(
Missing404Exception::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0. Use \OpenSearch\Exception\NotFoundHttpException instead.',
E_USER_DEPRECATED
);

/**
* @deprecated in 2.3.2 and will be removed in 3.0.0.
*
* @see \OpenSearch\Exception\NotFoundHttpException
*/
class Missing404Exception extends \Exception implements OpenSearchException
class Missing404Exception extends NotFoundHttpException
{
}
11 changes: 8 additions & 3 deletions src/OpenSearch/Common/Exceptions/NoDocumentsToGetException.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@

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);
@trigger_error(
NoDocumentsToGetException::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0. Use \OpenSearch\Exception\NoDocumentsToGetException instead.',
E_USER_DEPRECATED
);

/**
* @deprecated in 2.3.2 and will be removed in 3.0.0.
* @deprecated in 2.3.2 and will be removed in 3.0.0. Use \OpenSearch\Exception\NoDocumentsToGetException instead.
*
* @see \OpenSearch\Exception\ScriptLangNotSupportedException
*/
class NoDocumentsToGetException extends ServerErrorResponseException implements OpenSearchException
class NoDocumentsToGetException extends \OpenSearch\Exception\NoDocumentsToGetException
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@

namespace OpenSearch\Common\Exceptions;

@trigger_error(
NoNodesAvailableException::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 NoNodesAvailableException extends ServerErrorResponseException implements OpenSearchException
{
}
12 changes: 11 additions & 1 deletion src/OpenSearch/Common/Exceptions/NoShardAvailableException.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@

namespace OpenSearch\Common\Exceptions;

class NoShardAvailableException extends ServerErrorResponseException implements OpenSearchException
@trigger_error(
NoShardAvailableException::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0. Use \OpenSearch\Exception\NoShardAvailableException instead.',
E_USER_DEPRECATED
);

/**
* @deprecated in 2.3.2 and will be removed in 3.0.0. Use \OpenSearch\Exception\NoShardAvailableException instead.
*
* @see \OpenSearch\Exception\NoShardAvailableException
*/
class NoShardAvailableException extends \OpenSearch\Exception\NoShardAvailableException
{
}
12 changes: 10 additions & 2 deletions src/OpenSearch/Common/Exceptions/OpenSearchException.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,16 @@

namespace OpenSearch\Common\Exceptions;

use Throwable;
use OpenSearch\Exception\OpenSearchExceptionInterface;

interface OpenSearchException extends Throwable
@trigger_error(
NoNodesAvailableException::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 OpenSearchException extends OpenSearchExceptionInterface
{
}
12 changes: 11 additions & 1 deletion src/OpenSearch/Common/Exceptions/RoutingMissingException.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@

namespace OpenSearch\Common\Exceptions;

class RoutingMissingException extends ServerErrorResponseException implements OpenSearchException
@trigger_error(
RoutingMissingException::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0. Use \OpenSearch\Exception\RoutingMissingException instead.',
E_USER_DEPRECATED
);

/**
* @deprecated in 2.3.2 and will be removed in 3.0.0. Use OpenSearch\Exception\UnauthorizedHttpException instead.
*
* @see \OpenSearch\Exception\ScriptLangNotSupportedException
*/
class RoutingMissingException extends \OpenSearch\Exception\RoutingMissingException
{
}
4 changes: 3 additions & 1 deletion src/OpenSearch/Common/Exceptions/RuntimeException.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

namespace OpenSearch\Common\Exceptions;

class RuntimeException extends \RuntimeException implements OpenSearchException
use OpenSearch\Exception\OpenSearchExceptionInterface;

class RuntimeException extends \RuntimeException implements OpenSearchExceptionInterface
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@

namespace OpenSearch\Common\Exceptions;

class ScriptLangNotSupportedException extends BadRequest400Exception implements OpenSearchException
@trigger_error(
ScriptLangNotSupportedException::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0. Use OpenSearch\Exception\ScriptLangNotSupportedException instead.',
E_USER_DEPRECATED
);

/**
* @deprecated in 2.3.2 and will be removed in 3.0.0. Use OpenSearch\Exception\UnauthorizedHttpException instead.
*
* @see \OpenSearch\Exception\ScriptLangNotSupportedException
*/
class ScriptLangNotSupportedException extends \OpenSearch\Exception\ScriptLangNotSupportedException
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 ServerErrorResponseException extends TransportException implements OpenSearchException
{
}
14 changes: 13 additions & 1 deletion src/OpenSearch/Common/Exceptions/Unauthorized401Exception.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@

namespace OpenSearch\Common\Exceptions;

class Unauthorized401Exception extends \Exception implements OpenSearchException
use OpenSearch\Exception\UnauthorizedHttpException;

@trigger_error(
Unauthorized401Exception::class . ' is deprecated in 2.3.2 and will be removed in 3.0.0. Use OpenSearch\Exception\UnauthorizedHttpException instead.',
E_USER_DEPRECATED
);

/**
* @deprecated in 2.3.2 and will be removed in 3.0.0. Use OpenSearch\Exception\UnauthorizedHttpException instead.
*
* @see \OpenSearch\Exception\UnauthorizedHttpException
*/
class Unauthorized401Exception extends UnauthorizedHttpException
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

namespace OpenSearch\Common\Exceptions;

class UnexpectedValueException extends \UnexpectedValueException implements OpenSearchException
use OpenSearch\Exception\OpenSearchExceptionInterface;

class UnexpectedValueException extends \UnexpectedValueException implements OpenSearchExceptionInterface
{
}
Loading

0 comments on commit d30188f

Please sign in to comment.