diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1a81af4975b9..ffe3c5a7b2e4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -63,11 +63,20 @@ jobs: max_attempts: 5 command: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress + - name: Setup DynamoDB Local + uses: rrainn/dynamodb-action@v2.0.0 + with: + port: 8888 + - name: Execute tests run: vendor/bin/phpunit --verbose env: DB_PORT: ${{ job.services.mysql.ports[3306] }} DB_USERNAME: root + DYNAMODB_CACHE_TABLE: laravel_dynamodb_test + DYNAMODB_ENDPOINT: "http://localhost:8888" + AWS_ACCESS_KEY_ID: random_key + AWS_SECRET_ACCESS_KEY: random_secret windows_tests: runs-on: windows-latest diff --git a/src/Illuminate/Cache/CacheManager.php b/src/Illuminate/Cache/CacheManager.php index 33d1027bce1a..42bfc7271d59 100755 --- a/src/Illuminate/Cache/CacheManager.php +++ b/src/Illuminate/Cache/CacheManager.php @@ -2,12 +2,10 @@ namespace Illuminate\Cache; -use Aws\DynamoDb\DynamoDbClient; use Closure; use Illuminate\Contracts\Cache\Factory as FactoryContract; use Illuminate\Contracts\Cache\Store; use Illuminate\Contracts\Events\Dispatcher as DispatcherContract; -use Illuminate\Support\Arr; use InvalidArgumentException; /** @@ -226,21 +224,9 @@ protected function createDatabaseDriver(array $config) */ protected function createDynamodbDriver(array $config) { - $dynamoConfig = [ - 'region' => $config['region'], - 'version' => 'latest', - 'endpoint' => $config['endpoint'] ?? null, - ]; - - if ($config['key'] && $config['secret']) { - $dynamoConfig['credentials'] = Arr::only( - $config, ['key', 'secret', 'token'] - ); - } - return $this->repository( new DynamoDbStore( - new DynamoDbClient($dynamoConfig), + $this->app['cache.dynamodb.client'], $config['table'], $config['attributes']['key'] ?? 'key', $config['attributes']['value'] ?? 'value', diff --git a/src/Illuminate/Cache/CacheServiceProvider.php b/src/Illuminate/Cache/CacheServiceProvider.php index 46fa0ae2615c..90d1d019f4be 100755 --- a/src/Illuminate/Cache/CacheServiceProvider.php +++ b/src/Illuminate/Cache/CacheServiceProvider.php @@ -2,7 +2,9 @@ namespace Illuminate\Cache; +use Aws\DynamoDb\DynamoDbClient; use Illuminate\Contracts\Support\DeferrableProvider; +use Illuminate\Support\Arr; use Illuminate\Support\ServiceProvider; use Symfony\Component\Cache\Adapter\Psr16Adapter; @@ -30,6 +32,19 @@ public function register() $this->app->singleton('memcached.connector', function () { return new MemcachedConnector; }); + + $this->app->singleton('cache.dynamodb.client', function ($app) { + $config = $app['config']->get('cache.stores.dynamodb'); + + return new DynamoDbClient([ + 'region' => $config['region'], + 'version' => 'latest', + 'endpoint' => $config['endpoint'] ?? null, + 'credentials' => Arr::only( + $config, ['key', 'secret', 'token'] + ), + ]); + }); } /** @@ -40,7 +55,7 @@ public function register() public function provides() { return [ - 'cache', 'cache.store', 'cache.psr6', 'memcached.connector', + 'cache', 'cache.store', 'cache.psr6', 'memcached.connector', 'cache.dynamodb.client', ]; } } diff --git a/tests/Integration/Cache/DynamoDbStoreTest.php b/tests/Integration/Cache/DynamoDbStoreTest.php index 74897fbde8cb..f7aeae6a3deb 100644 --- a/tests/Integration/Cache/DynamoDbStoreTest.php +++ b/tests/Integration/Cache/DynamoDbStoreTest.php @@ -2,6 +2,8 @@ namespace Illuminate\Tests\Integration\Cache; +use Aws\DynamoDb\DynamoDbClient; +use Aws\Exception\AwsException; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Str; use Orchestra\Testbench\TestCase; @@ -13,11 +15,11 @@ class DynamoDbStoreTest extends TestCase { protected function setUp(): void { - parent::setUp(); - if (! env('DYNAMODB_CACHE_TABLE')) { $this->markTestSkipped('DynamoDB not configured.'); } + + parent::setUp(); } public function testItemsCanBeStoredAndRetrieved() @@ -74,15 +76,63 @@ public function testLocksCanBeAcquired() */ protected function getEnvironmentSetUp($app) { + if (! env('DYNAMODB_CACHE_TABLE')) { + $this->markTestSkipped('DynamoDB not configured.'); + } + $app['config']->set('cache.default', 'dynamodb'); - $app['config']->set('cache.stores.dynamodb', [ - 'driver' => 'dynamodb', - 'key' => env('AWS_ACCESS_KEY_ID'), - 'secret' => env('AWS_SECRET_ACCESS_KEY'), - 'region' => 'us-east-1', - 'table' => env('DYNAMODB_CACHE_TABLE', 'laravel_test'), - 'endpoint' => env('DYNAMODB_ENDPOINT'), + $config = $app['config']->get('cache.stores.dynamodb'); + + /** @var \Aws\DynamoDb\DynamoDbClient $client */ + $client = $app['cache.dynamodb.client']; + + if ($this->dynamoTableExists($client, $config['table'])) { + return; + } + + $client->createTable([ + 'TableName' => $config['table'], + 'KeySchema' => [ + [ + 'AttributeName' => $config['attributes']['key'] ?? 'key', + 'KeyType' => 'HASH', + ], + ], + 'AttributeDefinitions' => [ + [ + 'AttributeName' => $config['attributes']['key'] ?? 'key', + 'AttributeType' => 'S', + ], + ], + 'ProvisionedThroughput' => [ + 'ReadCapacityUnits' => 1, + 'WriteCapacityUnits' => 1, + ], ]); } + + /** + * Determine if the given DynamoDB table exists. + * + * @param \Aws\DynamoDb\DynamoDbClient $client + * @param string $table + * @return bool + */ + public function dynamoTableExists(DynamoDbClient $client, $table) + { + try { + $client->describeTable([ + 'TableName' => $table, + ]); + + return true; + } catch (AwsException $e) { + if (Str::contains($e->getAwsErrorMessage(), ['resource not found', 'Cannot do operations on a non-existent table'])) { + return false; + } + + throw $e; + } + } }