From d7150e10b805b0e7893389b0fc09e16f7cd3921c Mon Sep 17 00:00:00 2001 From: Tinashe Musonza Date: Wed, 18 Oct 2023 18:50:54 -0400 Subject: [PATCH] delete InteractsWithDynamoDB trait --- composer.json | 3 +- src/Commands/SetupDynamoDbTables.php | 19 +++--- src/InteractsWithDynamoDB.php | 87 ---------------------------- tests/Feature/ExampleFeatureTest.php | 77 +++++++++++++++++++++++- tests/FeatureTestCase.php | 7 --- tests/Traits/Helpers.php | 50 +++++++++++++++- tests/config/gsis.php | 17 ++++++ 7 files changed, 154 insertions(+), 106 deletions(-) delete mode 100644 src/InteractsWithDynamoDB.php diff --git a/composer.json b/composer.json index 3c58555..3b00cc8 100644 --- a/composer.json +++ b/composer.json @@ -15,8 +15,7 @@ "nunomaduro/larastan": "^2.6", "vimeo/psalm": "^5.4", "rector/rector": "^0.15.2", - "laravel/pint": "^1.3", - "tuupola/ksuid": "^2.1" + "laravel/pint": "^1.3" }, "autoload": { "psr-4": { diff --git a/src/Commands/SetupDynamoDbTables.php b/src/Commands/SetupDynamoDbTables.php index 2cba5ad..edaebd3 100644 --- a/src/Commands/SetupDynamoDbTables.php +++ b/src/Commands/SetupDynamoDbTables.php @@ -72,20 +72,22 @@ public function transformGlobalSecondaryIndexes(array $gsiConfig): array $gsiAttributeDefinitions = []; foreach ($gsiConfig as $gsi) { + $projectionType = $gsi['projection']['ProjectionType'] ?? 'ALL'; + $readCapacityUnits = $gsi['provisioned_throughput']['ReadCapacityUnits'] ?? 5; + $writeCapacityUnits = $gsi['provisioned_throughput']['WriteCapacityUnits'] ?? 5; + $transformedGSIs[] = [ 'IndexName' => $gsi['index_name'], 'KeySchema' => $this->getKeySchema( $gsi['key_schema']['partition_key'], $gsi['key_schema']['sort_key'] ), - // Assuming all attributes are to be included in the GSI 'Projection' => [ - 'ProjectionType' => 'ALL', + 'ProjectionType' => $projectionType, ], - // Assuming a provisioned throughput of 5 read and 5 write capacity units for each GSI 'ProvisionedThroughput' => [ - 'ReadCapacityUnits' => 5, - 'WriteCapacityUnits' => 5, + 'ReadCapacityUnits' => $readCapacityUnits, + 'WriteCapacityUnits' => $writeCapacityUnits, ], ]; @@ -106,9 +108,10 @@ public function transformGlobalSecondaryIndexes(array $gsiConfig): array protected function getAttributeDefinitions(array $attributes, array $globalSecondaryIndexes = []): array { $gsiAttributes = Collection::make($globalSecondaryIndexes)->reduce(function ($carry, $gsi) { - foreach ($gsi['key_schema'] as $attributeName) { - // TODO Assuming all GSI attributes are of type 'S' (string) - $carry[$attributeName] = 'S'; + $gsiAttributes = $gsi['attributes'] ?? []; + + foreach ($gsiAttributes as $attributeName => $type) { + $carry[$attributeName] = $type; } return $carry; diff --git a/src/InteractsWithDynamoDB.php b/src/InteractsWithDynamoDB.php deleted file mode 100644 index ee1cde3..0000000 --- a/src/InteractsWithDynamoDB.php +++ /dev/null @@ -1,87 +0,0 @@ -dynamoBreezeService)) { - $this->dynamoBreezeService = app(DynamoBreezeService::class); - } - - return $this->dynamoBreezeService; - } - - /** - * Ensure the using class has defined `getTable` method. - */ - protected function ensureTableMethodExists() - { - if (! method_exists($this, 'getTable')) { - throw new \RuntimeException( - sprintf('You must define a `getTable` method in %s to use the InteractsWithDynamoDB trait.', get_class($this)) - ); - } - } - - /** - * Fetch data based on provided parameters. - * - * @return mixed - */ - public function fetchData(array $parameters) - { - $this->ensureTableMethodExists(); - - return $this->getDynamoBreezeService()->retrieveRecordsWithConditions($parameters); - } - - /** - * Example method to retrieve data using a defined access pattern. - * - * @return mixed - */ - public function getByAccessPattern(string $patternName, array $keyConditions) - { - $this->ensureTableMethodExists(); - $config = config('dynamo-breeze.tables.'.$this->getTable()); - - $pattern = $config['access_patterns'][$patternName] ?? null; - - if (! $pattern) { - throw new \InvalidArgumentException("Access pattern [$patternName] is not defined."); - } - - $parameters = [ - 'TableName' => $config['table_name'], - 'KeyConditionExpression' => $pattern['key_condition_expression'], - 'ExpressionAttributeValues' => $this->prepareExpressionAttributeValues($keyConditions), - ]; - - if (isset($pattern['gsi_name'])) { - $parameters['IndexName'] = $pattern['gsi_name']; - } - - return $this->fetchData($parameters); - } - - /** - * Prepare the ExpressionAttributeValues parameter for DynamoDB. - */ - private function prepareExpressionAttributeValues(array $keyConditions): array - { - $expressionAttributeValues = []; - - foreach ($keyConditions as $key => $value) { - $expressionAttributeValues[":{$key}_val"] = $value; - } - - return $expressionAttributeValues; - } -} diff --git a/tests/Feature/ExampleFeatureTest.php b/tests/Feature/ExampleFeatureTest.php index 99f743f..786e3a5 100644 --- a/tests/Feature/ExampleFeatureTest.php +++ b/tests/Feature/ExampleFeatureTest.php @@ -26,7 +26,82 @@ public function setUp(): void public function testCreatesTablesFromConfiguration() { - $this->assertTableExists(self::TABLE_NAME); + $data = [ + 'AttributeDefinitions' => [ + [ + 'AttributeName' => 'PK', + 'AttributeType' => 'S', + ], + [ + 'AttributeName' => 'SK', + 'AttributeType' => 'S', + ], + [ + 'AttributeName' => 'GSI1PK', + 'AttributeType' => 'S', + ], + [ + 'AttributeName' => 'GSI1SK', + 'AttributeType' => 'S', + ], + [ + 'AttributeName' => 'GSI2PK', + 'AttributeType' => 'S', + ], + [ + 'AttributeName' => 'GSI2SK', + 'AttributeType' => 'N', + ], + ], + 'GlobalSecondaryIndexes' => [ + [ + 'IndexName' => 'GSI1', + 'KeySchema' => [ + [ + 'AttributeName' => 'GSI1PK', + 'KeyType' => 'HASH', + ], + [ + 'AttributeName' => 'GSI1SK', + 'KeyType' => 'RANGE', + ], + ], + 'Projection' => [ + 'ProjectionType' => 'ALL', + ], + 'IndexStatus' => 'ACTIVE', + 'ProvisionedThroughput' => [ + 'ReadCapacityUnits' => 5, + 'WriteCapacityUnits' => 5, + ], + 'IndexSizeBytes' => 0, + ], + [ + 'IndexName' => 'GSI2', + 'KeySchema' => [ + [ + 'AttributeName' => 'GSI2PK', + 'KeyType' => 'HASH', + ], + [ + 'AttributeName' => 'GSI2SK', + 'KeyType' => 'RANGE', + ], + ], + 'Projection' => [ + 'ProjectionType' => 'KEYS_ONLY', + ], + 'IndexStatus' => 'ACTIVE', + 'ProvisionedThroughput' => [ + 'ReadCapacityUnits' => 10, + 'WriteCapacityUnits' => 5, + ], + 'IndexSizeBytes' => 0, + ], + ], + ]; + + $this->assertTableExists(self::TABLE_NAME, $data); } public function testFetchUserPosts(): void diff --git a/tests/FeatureTestCase.php b/tests/FeatureTestCase.php index f89f5a0..f238946 100644 --- a/tests/FeatureTestCase.php +++ b/tests/FeatureTestCase.php @@ -3,10 +3,8 @@ namespace Musonza\DynamoBreeze\Tests; use Aws\DynamoDb\DynamoDbClient; -use Carbon\Carbon; use Musonza\DynamoBreeze\Commands\SetupDynamoDbTables; use Musonza\DynamoBreeze\Tests\Database\Seeders\DynamoDbTableSeeder; -use Tuupola\KsuidFactory; class FeatureTestCase extends TestCase { @@ -30,9 +28,4 @@ public function seedDynamoDbTable(array $data, string $tableName): void $seeder = app(DynamoDbTableSeeder::class); $seeder->seed($data, $tableName); } - - public function generateKSUID(Carbon $date): string - { - return KsuidFactory::fromTimestamp($date->getTimestamp())->string(); - } } diff --git a/tests/Traits/Helpers.php b/tests/Traits/Helpers.php index 2dec7b0..20c4f9a 100644 --- a/tests/Traits/Helpers.php +++ b/tests/Traits/Helpers.php @@ -10,16 +10,64 @@ trait Helpers { - private function assertTableExists(string $tableName): void + private function assertTableExists(string $tableName, array $data = []): void { try { $result = $this->client->describeTable(['TableName' => $tableName]); + $this->assertEquals($tableName, $result['Table']['TableName']); + + if (isset($data['AttributeDefinitions'])) { + $this->assertAttributeDefinitions($data['AttributeDefinitions'], $result['Table']['AttributeDefinitions']); + } + + if (isset($data['GlobalSecondaryIndexes'])) { + $this->assertGlobalSecondaryIndexes($data['GlobalSecondaryIndexes'], $result['Table']['GlobalSecondaryIndexes'] ?? []); + } } catch (DynamoDbException $e) { $this->fail("Table [$tableName] was not created. ".$e->getMessage()); } } + private function assertAttributeDefinitions(array $expectedAttributes, array $actualAttributes): void + { + $expectedAttributeDefinitions = array_map(function ($attr) { + return [ + 'AttributeName' => $attr['AttributeName'], + 'AttributeType' => $attr['AttributeType'], + ]; + }, $expectedAttributes); + + $this->assertEquals($expectedAttributeDefinitions, $actualAttributes); + } + + private function assertGlobalSecondaryIndexes(array $expectedGSIs, array $actualGSIs): void + { + foreach ($expectedGSIs as $expectedGSI) { + $foundGSI = false; + + foreach ($actualGSIs as $actualGSI) { + if ($expectedGSI['IndexName'] === $actualGSI['IndexName']) { + $foundGSI = true; + + // Compare only the keys that are present in expected GSI + foreach ($expectedGSI as $key => $value) { + if (array_key_exists($key, $actualGSI)) { + $this->assertEquals($value, $actualGSI[$key], "Index: {$actualGSI['IndexName']}"); + } + } + + // Break out of the inner loop if we've found a match + break; + } + } + + if (! $foundGSI) { + $this->fail("GSI with IndexName {$expectedGSI['IndexName']} not found."); + } + } + } + private function fetchUserPosts(int $userId): DynamoBreezeResult { return DynamoBreeze::table(self::TABLE_IDENTIFIER) diff --git a/tests/config/gsis.php b/tests/config/gsis.php index 181d071..0329030 100644 --- a/tests/config/gsis.php +++ b/tests/config/gsis.php @@ -12,5 +12,22 @@ 'GSI1SK' => 'S', ], ], + 'GSI2' => [ + 'index_name' => 'GSI2', + 'key_schema' => [ + 'partition_key' => 'GSI2PK', + 'sort_key' => 'GSI2SK', + ], + 'attributes' => [ + 'GSI2PK' => 'S', + 'GSI2SK' => 'N', + ], + 'provisioned_throughput' => [ + 'ReadCapacityUnits' => 10, + ], + 'projection' => [ + 'ProjectionType' => 'KEYS_ONLY', + ], + ], // ... other GSIs ... ];