Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changes/nextrelease/awsexception-throwable.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{
"type": "enhancement",
"category": "Exception",
"description": "Loosens requirements for `$previous` `AwsException` parameter from `Exception` to `Throwable`."
}
]
39 changes: 16 additions & 23 deletions src/Exception/AwsException.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use JmesPath\Env as JmesPath;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\RequestInterface;
use Throwable;

/**
* Represents an AWS exception that is thrown when a command fails.
Expand Down Expand Up @@ -39,35 +40,29 @@ class AwsException extends \RuntimeException implements


/**
* @param string $message Exception message
* @param string $message Exception message
* @param CommandInterface $command
* @param array $context Exception context
* @param \Exception $previous Previous exception (if any)
* @param array $context Exception context
* @param Throwable|null $previous Previous exception (if any)
*/
public function __construct(
$message,
CommandInterface $command,
array $context = [],
?\Exception $previous = null
?Throwable $previous = null
) {
$this->data = isset($context['body']) ? $context['body'] : [];
$this->data = $context['body'] ?? [];
$this->command = $command;
$this->response = isset($context['response']) ? $context['response'] : null;
$this->request = isset($context['request']) ? $context['request'] : null;
$this->requestId = isset($context['request_id'])
? $context['request_id']
: null;
$this->errorType = isset($context['type']) ? $context['type'] : null;
$this->errorCode = isset($context['code']) ? $context['code'] : null;
$this->errorShape = isset($context['error_shape']) ? $context['error_shape'] : null;
$this->response = $context['response'] ?? null;
$this->request = $context['request'] ?? null;
$this->requestId = $context['request_id'] ?? null;
$this->errorType = $context['type'] ?? null;
$this->errorCode = $context['code'] ?? null;
$this->errorShape = $context['error_shape'] ?? null;
$this->connectionError = !empty($context['connection_error']);
$this->result = isset($context['result']) ? $context['result'] : null;
$this->transferInfo = isset($context['transfer_stats'])
? $context['transfer_stats']
: [];
$this->errorMessage = isset($context['message'])
? $context['message']
: null;
$this->result = $context['result'] ?? null;
$this->transferInfo = $context['transfer_stats'] ?? [];
$this->errorMessage = $context['message'] ?? null;
$this->monitoringEvents = [];
$this->maxRetriesExceeded = false;
parent::__construct($message, 0, $previous);
Expand Down Expand Up @@ -220,9 +215,7 @@ public function getTransferInfo($name = null)
return $this->transferInfo;
}

return isset($this->transferInfo[$name])
? $this->transferInfo[$name]
: null;
return $this->transferInfo[$name] ?? null;
}

/**
Expand Down
108 changes: 94 additions & 14 deletions tests/Exception/AwsExceptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class AwsExceptionTest extends TestCase
{
use UsesServiceTrait;

public function testProvidesContextShortcuts()
public function testProvidesContextShortcuts(): void
{
$ctx = [
'request_id' => '10',
Expand All @@ -37,15 +37,15 @@ public function testProvidesContextShortcuts()
$this->assertNull($e->getResult());
}

public function testReturnsStatusCode()
public function testReturnsStatusCode(): void
{
$ctx = ['response' => new Response(400)];
$command = new Command('foo');
$e = new AwsException('Foo', $command, $ctx);
$this->assertSame(400, $e->getStatusCode());
}

public function testSetsMaxRetriesExceeded()
public function testSetsMaxRetriesExceeded(): void
{
$command = new Command('foo');
$e = new AwsException('Foo', $command);
Expand All @@ -54,52 +54,52 @@ public function testSetsMaxRetriesExceeded()
$this->assertTrue($e->isMaxRetriesExceeded());
}

public function testProvidesResult()
public function testProvidesResult(): void
{
$command = new Command('foo');
$result = new Result();
$e = new AwsException('Foo', $command, ['result' => $result]);
$this->assertSame($result, $e->getResult());
}

public function testProvidesResponse()
public function testProvidesResponse(): void
{
$command = new Command('foo');
$response = new Response();
$e = new AwsException('Foo', $command, ['response' => $response]);
$this->assertSame($response, $e->getResponse());
}

public function testProvidesErrorMessage()
public function testProvidesErrorMessage(): void
{
$command = new Command('foo');
$e = new AwsException('Foo', $command, ['message' => "test error message"]);
$this->assertSame("test error message", $e->getAwsErrorMessage());
}

public function testProvidesFalseConnectionErrorFlag()
public function testProvidesFalseConnectionErrorFlag(): void
{
$command = new Command('foo');
$e = new AwsException('Foo', $command, ['connection_error' => false]);
$this->assertFalse($e->isConnectionError());
}

public function testProvidesTrueConnectionErrorFlag()
public function testProvidesTrueConnectionErrorFlag(): void
{
$command = new Command('foo');
$e = new AwsException('Foo', $command, ['connection_error' => true]);
$this->assertTrue($e->isConnectionError());
}

public function testProvidesRequest()
public function testProvidesRequest(): void
{
$command = new Command('foo');
$request = new Request('GET', 'http://www.foo.com');
$e = new AwsException('Foo', $command, ['request' => $request]);
$this->assertSame($request, $e->getRequest());
}

public function testProvidesExceptionToStringWithNoPrevious()
public function testProvidesExceptionToStringWithNoPrevious(): void
{
$command = new Command('foo');
$e = new AwsException('Foo', $command);
Expand All @@ -109,7 +109,7 @@ public function testProvidesExceptionToStringWithNoPrevious()
$this->assertStringStartsWith($exceptionString, $e->__toString());
}

public function testProvidesExceptionToStringWithPreviousLast()
public function testProvidesExceptionToStringWithPreviousLast(): void
{
$prev = new \Exception("Last! '.");
$command = new Command('foo');
Expand All @@ -120,7 +120,7 @@ public function testProvidesExceptionToStringWithPreviousLast()
);
}

public function testHasData()
public function testHasData(): void
{
$e = new AwsException(
'foo-message',
Expand All @@ -135,7 +135,7 @@ public function testHasData()
$this->assertSame('b', $e->search('a'));
}

public function testProvidesErrorShape()
public function testProvidesErrorShape(): void
{
$command = new Command('foo');
$response = new Response();
Expand All @@ -157,7 +157,7 @@ public function testProvidesErrorShape()
$this->assertSame($errorShape->toArray(), $e->getAwsErrorShape()->toArray());
}

public function testCanIndirectlyModifyLikeAnArray()
public function testCanIndirectlyModifyLikeAnArray(): void
{
$e = new AwsException(
'foo-message',
Expand All @@ -179,4 +179,84 @@ public function testCanIndirectlyModifyLikeAnArray()
$q = 100;
$this->assertSame(0, $e['qux']);
}

public function testAcceptsExceptionAsPrevious(): void
{
$previous = new \Exception('Previous exception');
$command = new Command('foo');
$e = new AwsException('Foo', $command, [], $previous);

$this->assertSame($previous, $e->getPrevious());
$this->assertInstanceOf(\Throwable::class, $e->getPrevious());
}

public function testAcceptsErrorAsPrevious(): void
{
$previous = new \Error('Previous error');
$command = new Command('foo');
$e = new AwsException('Foo', $command, [], $previous);

$this->assertSame($previous, $e->getPrevious());
$this->assertInstanceOf(\Throwable::class, $e->getPrevious());
}

public function testAcceptsCustomErrorAsPrevious(): void
{
$previous = new class extends \Error {
public function __construct() {
parent::__construct('Custom error');
}
};

$command = new Command('foo');
$e = new AwsException('Foo', $command, [], $previous);

$this->assertSame($previous, $e->getPrevious());
$this->assertInstanceOf(\Throwable::class, $e->getPrevious());
}

public function testAcceptsNullAsPrevious(): void
{
$command = new Command('foo');
$e = new AwsException('Foo', $command, [], null);

$this->assertNull($e->getPrevious());
}

/**
* @dataProvider previousThrowableProvider
*/
public function testAcceptsVariousThrowableTypes(\Throwable $previous): void
{
$command = new Command('foo');
$e = new AwsException('Foo', $command, [], $previous);

$this->assertSame($previous, $e->getPrevious());
$this->assertInstanceOf(\Throwable::class, $e->getPrevious());
}

public function previousThrowableProvider(): array
{
return [
'standard exception' => [new \Exception('Standard exception')],
'runtime exception' => [new \RuntimeException('Runtime exception')],
'error' => [new \Error('Error')],
'type error' => [new \TypeError('Type error')],
'arithmetic error' => [new \ArithmeticError('Arithmetic error')],
'division by zero error' => [new \DivisionByZeroError('Division by zero')]
];
}

public function testExceptionMessageWithErrorAsPrevious(): void
{
$previous = new \Error('Previous error');
$command = new Command('foo');
$e = new AwsException('Foo', $command, [], $previous);

$exceptionString = $e->__toString();

$this->assertStringContainsString('Foo', $exceptionString);
$this->assertStringContainsString('Previous error', $exceptionString);
$this->assertStringContainsString('Previous error', $e->__toString());
}
}