diff --git a/composer.json b/composer.json index f138b67..bd14b96 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,12 @@ "nyholm/psr7-server": "^1.0", "php-http/curl-client": "^2.2", "phpstan/phpstan": "^1.10.14", - "phpunit/phpunit": "^10.1" + "phpunit/phpunit": "^10.1", + "async-aws/dynamo-db": "~3.2", + "symfony/polyfill-uuid": "^1.31" + }, + "suggest": { + "async-aws/dynamo-db": "For using the DynamoDBRepository" }, "autoload": { "psr-4": { diff --git a/src/Adapter/DynamoDB/DynamoDBRepository.php b/src/Adapter/DynamoDB/DynamoDBRepository.php new file mode 100644 index 0000000..6157ba7 --- /dev/null +++ b/src/Adapter/DynamoDB/DynamoDBRepository.php @@ -0,0 +1,97 @@ + + */ +class DynamoDBRepository implements ShopRepositoryInterface +{ + public function __construct(private readonly DynamoDbClient $client, private readonly string $tableName) + { + } + + public function createShopStruct(string $shopId, string $shopUrl, string $shopSecret): ShopInterface + { + return new DynamoDBShop($shopId, $shopUrl, $shopSecret); + } + + public function createShop(ShopInterface $shop): void + { + $this->client->putItem(new PutItemInput([ + 'TableName' => $this->tableName, + 'Item' => [ + 'id' => ['S' => $shop->getShopId()], + 'active' => ['BOOL' => $shop->isShopActive() ? '1' : '0'], + 'url' => ['S' => $shop->getShopUrl()], + 'secret' => ['S' => $shop->getShopSecret()], + 'clientId' => ['S' => (string) $shop->getShopClientId()], + 'clientSecret' => ['S' => (string) $shop->getShopClientSecret()], + ], + ])); + } + + public function getShopFromId(string $shopId): ShopInterface|null + { + $item = $this->client->getItem(new GetItemInput([ + 'TableName' => $this->tableName, + 'Key' => [ + 'id' => ['S' => $shopId], + ], + ]))->getItem(); + + if (!$item) { + return null; + } + + $shopClientId = $item['clientId']->getS(); + $shopClientSecret = $item['clientSecret']->getS(); + + if ($shopClientSecret === '') { + $shopClientSecret = null; + } + + if ($shopClientId === '') { + $shopClientId = null; + } + + $active = $item['active']->getBool(); + + if ($active === null) { + $active = false; + } + + return new DynamoDBShop( + $item['id']->getS() ?? '', + $item['url']->getS() ?? '', + $item['secret']->getS() ?? '', + $shopClientId, + $shopClientSecret, + $active, + ); + } + + public function updateShop(ShopInterface $shop): void + { + $this->createShop($shop); + } + + public function deleteShop(string $shopId): void + { + $this->client->deleteItem(new DeleteItemInput([ + 'TableName' => $this->tableName, + 'Key' => [ + 'id' => ['S' => $shopId], + ], + ])); + } +} diff --git a/src/Adapter/DynamoDB/DynamoDBShop.php b/src/Adapter/DynamoDB/DynamoDBShop.php new file mode 100644 index 0000000..7879b06 --- /dev/null +++ b/src/Adapter/DynamoDB/DynamoDBShop.php @@ -0,0 +1,66 @@ +active; + } + + public function getShopId(): string + { + return $this->shopId; + } + + public function getShopUrl(): string + { + return $this->shopUrl; + } + + public function getShopSecret(): string + { + return $this->shopSecret; + } + + public function getShopClientId(): ?string + { + return $this->shopClientId; + } + + public function getShopClientSecret(): ?string + { + return $this->shopClientSecret; + } + + public function setShopApiCredentials(string $clientId, string $clientSecret): ShopInterface + { + $this->shopClientId = $clientId; + $this->shopClientSecret = $clientSecret; + + return $this; + } + + public function setShopUrl(string $url): ShopInterface + { + $this->shopUrl = $url; + + return $this; + } + + public function setShopActive(bool $active): ShopInterface + { + $this->active = $active; + + return $this; + } +} diff --git a/tests/Adapter/DynamoDB/DynamoDBRepositoryTest.php b/tests/Adapter/DynamoDB/DynamoDBRepositoryTest.php new file mode 100644 index 0000000..dd2cf21 --- /dev/null +++ b/tests/Adapter/DynamoDB/DynamoDBRepositoryTest.php @@ -0,0 +1,225 @@ +createMock(DynamoDbClient::class), 'tableName'); + + $shop = $repository->createShopStruct('shopId', 'shopUrl', 'shopSecret'); + + static::assertSame('shopId', $shop->getShopId()); + static::assertSame('shopUrl', $shop->getShopUrl()); + static::assertSame('shopSecret', $shop->getShopSecret()); + static::assertNull($shop->getShopClientId()); + static::assertNull($shop->getShopClientSecret()); + static::assertFalse($shop->isShopActive()); + } + + public function testCreateShop(): void + { + $client = $this->createMock(DynamoDbClient::class); + + $client + ->expects(static::exactly(2)) + ->method('putItem') + ->with(static::callback(function (PutItemInput $input) { + static::assertSame('tableName', $input->getTableName()); + + $attributes = $input->getItem(); + + static::assertArrayHasKey('id', $attributes); + static::assertArrayHasKey('url', $attributes); + static::assertArrayHasKey('secret', $attributes); + static::assertArrayHasKey('clientId', $attributes); + static::assertArrayHasKey('clientSecret', $attributes); + static::assertArrayHasKey('active', $attributes); + + static::assertSame('shopId', $attributes['id']->getS()); + static::assertSame('shopUrl', $attributes['url']->getS()); + static::assertSame('shopSecret', $attributes['secret']->getS()); + static::assertSame('shopClientId', $attributes['clientId']->getS()); + static::assertSame('shopClientSecret', $attributes['clientSecret']->getS()); + static::assertSame(true, $attributes['active']->getBool()); + + return true; + })); + + $repository = new DynamoDBRepository($client, 'tableName'); + + $shop = new DynamoDBShop('shopId', 'shopUrl', 'shopSecret', 'shopClientId', 'shopClientSecret', true); + $repository->createShop($shop); + $repository->updateShop($shop); + } + + public function testCreateShopNullClientId(): void + { + $client = $this->createMock(DynamoDbClient::class); + + $client + ->expects(static::once()) + ->method('putItem') + ->with(static::callback(function (PutItemInput $input) { + static::assertSame('tableName', $input->getTableName()); + + $attributes = $input->getItem(); + + static::assertArrayHasKey('id', $attributes); + static::assertArrayHasKey('url', $attributes); + static::assertArrayHasKey('secret', $attributes); + static::assertArrayHasKey('clientId', $attributes); + static::assertArrayHasKey('clientSecret', $attributes); + static::assertArrayHasKey('active', $attributes); + + static::assertSame('shopId', $attributes['id']->getS()); + static::assertSame('shopUrl', $attributes['url']->getS()); + static::assertSame('shopSecret', $attributes['secret']->getS()); + static::assertSame('', $attributes['clientId']->getS()); + static::assertSame('', $attributes['clientSecret']->getS()); + static::assertFalse($attributes['active']->getBool()); + + return true; + })); + + $repository = new DynamoDBRepository($client, 'tableName'); + + $shop = new DynamoDBShop('shopId', 'shopUrl', 'shopSecret'); + $repository->createShop($shop); + } + + public function testGetShopNotFound(): void + { + $client = $this->createMock(DynamoDbClient::class); + + $output = $this->createMock(GetItemOutput::class); + $output + ->expects(static::once()) + ->method('getItem') + ->willReturn([]); + + $client + ->expects(static::once()) + ->method('getItem') + ->with(static::callback(function (GetItemInput $input) { + static::assertSame('tableName', $input->getTableName()); + static::assertSame('shopId', $input->getKey()['id']->getS()); + + return true; + })) + ->willReturn($output); + + $repository = new DynamoDBRepository($client, 'tableName'); + + $shop = $repository->getShopFromId('shopId'); + + static::assertNull($shop); + } + + + public function testGetShopFound(): void + { + $client = $this->createMock(DynamoDbClient::class); + + $output = $this->createMock(GetItemOutput::class); + $output + ->expects(static::once()) + ->method('getItem') + ->willReturn([ + 'id' => new AttributeValue(['S' => 'shopId']), + 'url' => new AttributeValue(['S' => 'shopUrl']), + 'secret' => new AttributeValue(['S' => 'shopSecret']), + 'clientId' => new AttributeValue(['S' => '']), + 'clientSecret' => new AttributeValue(['S' => '']), + 'active' => new AttributeValue(['BOOL' => false]), + ]); + + $client + ->expects(static::once()) + ->method('getItem') + ->willReturn($output); + + $repository = new DynamoDBRepository($client, 'tableName'); + + $shop = $repository->getShopFromId('shopId'); + + static::assertNotNull($shop); + + static::assertSame('shopId', $shop->getShopId()); + static::assertSame('shopUrl', $shop->getShopUrl()); + static::assertSame('shopSecret', $shop->getShopSecret()); + static::assertNull($shop->getShopClientId()); + static::assertNull($shop->getShopClientSecret()); + static::assertFalse($shop->isShopActive()); + } + + public function testGetShopFoundWithoutActive(): void + { + $client = $this->createMock(DynamoDbClient::class); + + $output = $this->createMock(GetItemOutput::class); + $output + ->expects(static::once()) + ->method('getItem') + ->willReturn([ + 'id' => new AttributeValue(['S' => 'shopId']), + 'url' => new AttributeValue(['S' => 'shopUrl']), + 'secret' => new AttributeValue(['S' => 'shopSecret']), + 'clientId' => new AttributeValue(['S' => '']), + 'clientSecret' => new AttributeValue(['S' => '']), + 'active' => new AttributeValue(['BOOL' => null]), + ]); + + $client + ->expects(static::once()) + ->method('getItem') + ->willReturn($output); + + $repository = new DynamoDBRepository($client, 'tableName'); + + $shop = $repository->getShopFromId('shopId'); + + static::assertNotNull($shop); + + static::assertSame('shopId', $shop->getShopId()); + static::assertSame('shopUrl', $shop->getShopUrl()); + static::assertSame('shopSecret', $shop->getShopSecret()); + static::assertNull($shop->getShopClientId()); + static::assertNull($shop->getShopClientSecret()); + static::assertFalse($shop->isShopActive()); + } + + public function testDeleteShop(): void + { + $client = $this->createMock(DynamoDbClient::class); + + $client + ->expects(static::once()) + ->method('deleteItem') + ->with(static::callback(function (DeleteItemInput $input) { + static::assertSame('tableName', $input->getTableName()); + static::assertSame('shopId', $input->getKey()['id']->getS()); + + return true; + })); + + $repository = new DynamoDBRepository($client, 'tableName'); + + $repository->deleteShop('shopId'); + } +} diff --git a/tests/Adapter/DynamoDB/DynamoDBShopTest.php b/tests/Adapter/DynamoDB/DynamoDBShopTest.php new file mode 100644 index 0000000..c115d2d --- /dev/null +++ b/tests/Adapter/DynamoDB/DynamoDBShopTest.php @@ -0,0 +1,42 @@ +getShopId()); + static::assertSame('shopUrl', $shop->getShopUrl()); + static::assertSame('shopSecret', $shop->getShopSecret()); + static::assertSame('shopClientId', $shop->getShopClientId()); + static::assertSame('shopClientSecret', $shop->getShopClientSecret()); + static::assertTrue($shop->isShopActive()); + + $shop->setShopUrl('newShopUrl'); + + static::assertSame('newShopUrl', $shop->getShopUrl()); + + $shop->setShopActive(false); + + static::assertFalse($shop->isShopActive()); + + $shop->setShopApiCredentials('newClientId', 'newClientSecret'); + + static::assertSame('newClientId', $shop->getShopClientId()); + static::assertSame('newClientSecret', $shop->getShopClientSecret()); + + $shop = new DynamoDBShop('shopId', 'shopUrl', 'shopSecret'); + + static::assertFalse($shop->isShopActive()); + } +}