From d17fc82d5c952520fb5214bf41f2ae3ba5defcd9 Mon Sep 17 00:00:00 2001 From: Matthieu Napoli Date: Wed, 17 Apr 2019 19:59:22 +0200 Subject: [PATCH] Better exception handling --- composer.json | 1 + src/Dynamap.php | 31 +++++++++++++++++++++++-------- src/Exception/ItemNotFound.php | 14 ++++++++++++++ src/Exception/TableNotFound.php | 19 +++++++++++++++++++ src/Mapping.php | 7 ++++++- tests/DynamapTest.php | 32 ++++++++++++++++++++++++++++++++ 6 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 src/Exception/ItemNotFound.php create mode 100644 src/Exception/TableNotFound.php diff --git a/composer.json b/composer.json index 720327f..14625b9 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ }, "require": { "php": "^7.2", + "ext-json": "*", "aws/aws-sdk-php": "^3.91", "symfony/property-info": "^4.2" }, diff --git a/src/Dynamap.php b/src/Dynamap.php index 0446b7d..1b508fb 100644 --- a/src/Dynamap.php +++ b/src/Dynamap.php @@ -3,6 +3,9 @@ namespace Dynamap; use Aws\DynamoDb\DynamoDbClient; +use Aws\DynamoDb\Exception\DynamoDbException; +use Dynamap\Exception\ItemNotFound; +use Dynamap\Exception\TableNotFound; class Dynamap { @@ -38,16 +41,28 @@ public function getAll(string $table): array * Get item by primary key. * * @param int|string|array $key - * @throws \Exception + * @throws \InvalidArgumentException If the key is invalid. + * @throws ItemNotFound If the item cannot be found. */ public function get(string $table, $key): object { $tableMapping = $this->mappingObj->getTableMapping($table); - $item = $this->dynamoDb->getItem([ - 'TableName' => $table, - 'Key' => $this->buildKeyQuery($key, $tableMapping), - ])['Item']; + try { + $item = $this->dynamoDb->getItem([ + 'TableName' => $table, + 'Key' => $this->buildKeyQuery($key, $tableMapping), + ])['Item']; + } catch (DynamoDbException $e) { + if ($e->getAwsErrorCode() === 'ResourceNotFoundException') { + throw TableNotFound::tableMissingInDynamoDb($table, $e); + } + throw $e; + } + + if ($item === null) { + throw ItemNotFound::fromKey($tableMapping->getClassName(), $key); + } return $this->map($item, $table); } @@ -148,14 +163,14 @@ private function map(array $item, string $table): object /** * @param int|string|array $key - * @throws \Exception + * @throws \InvalidArgumentException */ private function buildKeyQuery($key, TableMapping $tableMapping): array { $keyQuery = []; if (! is_array($key)) { if ($tableMapping->isCompositeKey()) { - throw new \Exception('The key is a composite key and only a single value was provided'); + throw new \InvalidArgumentException('The key is a composite key and only a single value was provided'); } foreach ($tableMapping->getKeyMapping() as $fieldMapping) { $keyQuery[$fieldMapping->name()] = $fieldMapping->dynamoDbQueryValue($key); @@ -164,7 +179,7 @@ private function buildKeyQuery($key, TableMapping $tableMapping): array foreach ($tableMapping->getKeyMapping() as $fieldMapping) { $fieldName = $fieldMapping->name(); if (! isset($key[$fieldName])) { - throw new \Exception('The key is incomplete'); + throw new \InvalidArgumentException('The key is incomplete'); } $keyQuery[$fieldName] = $fieldMapping->dynamoDbQueryValue($key[$fieldName]); } diff --git a/src/Exception/ItemNotFound.php b/src/Exception/ItemNotFound.php new file mode 100644 index 0000000..be8f6cf --- /dev/null +++ b/src/Exception/ItemNotFound.php @@ -0,0 +1,14 @@ +getAwsErrorMessage(); + + return new self($message, 0, $previous); + } +} diff --git a/src/Mapping.php b/src/Mapping.php index 26128fd..695f708 100644 --- a/src/Mapping.php +++ b/src/Mapping.php @@ -2,6 +2,7 @@ namespace Dynamap; +use Dynamap\Exception\TableNotFound; use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; use Symfony\Component\PropertyInfo\PropertyInfoExtractor; @@ -35,6 +36,10 @@ public function __construct(array $mappingConfig) public function getTableMapping(string $table): TableMapping { + if (! isset($this->tables[$table])) { + throw new TableNotFound("The table `$table` is not configured in Dynamap"); + } + return $this->tables[$table]; } @@ -46,6 +51,6 @@ public function getTableFromClassName(string $className): TableMapping } } - throw new \Exception("No table mapping for for class $className"); + throw new TableNotFound("No table mapping found for class `$className`"); } } diff --git a/tests/DynamapTest.php b/tests/DynamapTest.php index f34e2cc..ff89e3c 100644 --- a/tests/DynamapTest.php +++ b/tests/DynamapTest.php @@ -5,6 +5,8 @@ use Aws\DynamoDb\DynamoDbClient; use Aws\DynamoDb\Exception\DynamoDbException; use Dynamap\Dynamap; +use Dynamap\Exception\ItemNotFound; +use Dynamap\Exception\TableNotFound; use Dynamap\Test\Fixture\Article; use PHPUnit\Framework\TestCase; @@ -61,6 +63,12 @@ public function setUp(): void 'id', ], ], + 'foo' => [ // This is a table that doesn't exist + 'class' => Article::class, + 'keys' => [ + 'id', + ], + ], ]; $this->dynamap = new Dynamap($dynamoDb, $mapping); } @@ -89,6 +97,30 @@ public function test get all(): void $this->assertCount(3, $this->dynamap->getAll('articles')); } + public function test get unknown object(): void + { + $this->expectException(ItemNotFound::class); + $this->expectExceptionMessage('Item `Dynamap\Test\Fixture\Article` not found for key 123'); + + $this->dynamap->get('articles', 123); + } + + public function test get table that is mapped but doesnt exist in DynamoDB(): void + { + $this->expectException(TableNotFound::class); + $this->expectExceptionMessage('Cannot find the table `foo` in DynamoDB: make sure it exists and that the code has permissions to access it'); + + $this->dynamap->get('foo', 123); + } + + public function test get unknown table(): void + { + $this->expectException(TableNotFound::class); + $this->expectExceptionMessage('The table `bar` is not configured in Dynamap'); + + $this->dynamap->get('bar', 123); + } + public function test string attribute(): void { $article = new Article(123);