From 5cf26579fcd3051d0b48c00c95965f78f4af480d Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 14 Apr 2024 21:49:23 +0200 Subject: [PATCH] TASK-40: Add ability to lock-up clients --- features/accept-order.feature | 23 +++++++++++++++++++-- features/crm-user-sync.feature | 27 +++++++++++++++++++++++-- src/Contract/LockClientRequestDto.php | 8 ++++++++ src/Controller/ClientController.php | 15 ++++++++++++++ src/Controller/OrderController.php | 3 +++ src/Entity/Client.php | 18 +++++++++++++++++ src/Exception/ClientLockedException.php | 7 +++++++ src/Service/ClientService.php | 22 +++++++++++++++++--- src/Service/OrderService.php | 8 ++++++++ tests/Behat/ClientsContext.php | 19 ++++++++++++++++- 10 files changed, 142 insertions(+), 8 deletions(-) create mode 100644 src/Contract/LockClientRequestDto.php create mode 100644 src/Exception/ClientLockedException.php diff --git a/features/accept-order.feature b/features/accept-order.feature index c43c0ed..20aa58f 100644 --- a/features/accept-order.feature +++ b/features/accept-order.feature @@ -3,8 +3,9 @@ Feature: Background: Given there exist following clients: - | clientId | name | initialBalance | currentBalance | - | 018edd2e-894a-78d7-b10c-16e05ca933a3 | Kacper | 10000 | 200000 | + | clientId | name | initialBalance | currentBalance | locked | + | 018edd2e-894a-78d7-b10c-16e05ca933a3 | Kacper | 10000 | 200000 | no | + | 018ede1e-a587-76a8-88f3-23987a8dade9 | Jacek | 10000 | 60000000 | yes | Scenario: Frontend is able to send new orders Given the request has the following body: @@ -52,3 +53,21 @@ Feature: """ When I send a POST request to "/orders" Then the response status should be 422 + + Scenario: Locked clients should not be allowed + Given the request has the following body: + """ + { + "orderId": "018eddae-ff52-7813-9f88-ada9e61a76f3", + "clientId": "018ede1e-a587-76a8-88f3-23987a8dade9", + "products": [ + { "productId": "p1", "quantity": 2, "price": 20.0, "weight": 100 }, + { "productId": "p2", "quantity": 1, "price": 100.0, "weight": 200 }, + { "productId": "p3", "quantity": 5, "price": 200.0, "weight": 100 }, + { "productId": "p4", "quantity": 1, "price": 50.0, "weight": 400 }, + { "productId": "p5", "quantity": 10, "price": 10.0, "weight": 100 } + ] + } + """ + When I send a POST request to "/orders" + Then the response status should be 403 diff --git a/features/crm-user-sync.feature b/features/crm-user-sync.feature index 8003d09..3147f18 100644 --- a/features/crm-user-sync.feature +++ b/features/crm-user-sync.feature @@ -3,8 +3,9 @@ Feature: Background: Given there exist following clients: - | clientId | name | initialBalance | currentBalance | - | 018edd2e-894a-78d7-b10c-16e05ca933a3 | Kacper | 10000 | 5000 | + | clientId | name | initialBalance | currentBalance | locked | + | 018edd2e-894a-78d7-b10c-16e05ca933a3 | Kacper | 10000 | 5000 | no | + | 018ede1e-a587-76a8-88f3-23987a8dade9 | Jacek | 10000 | 60000000 | yes | Scenario: CRM is able to create new clients Given the request has the following body: @@ -43,3 +44,25 @@ Feature: When I send a POST request to "/clients" Then the response status should be 409 And client with id "018edd2e-894a-78d7-b10c-16e05ca933a3" should exist + + Scenario: CRM is able to lock existing client + Given the request has the following body: + """ + { + "lock": true + } + """ + When I send a POST request to "/clients/018edd2e-894a-78d7-b10c-16e05ca933a3/_lock" + Then the response status should be 200 + And client with id "018edd2e-894a-78d7-b10c-16e05ca933a3" should be locked + + Scenario: CRM is able to unlock existing client + Given the request has the following body: + """ + { + "lock": false + } + """ + When I send a POST request to "/clients/018ede1e-a587-76a8-88f3-23987a8dade9/_lock" + Then the response status should be 200 + And client with id "018ede1e-a587-76a8-88f3-23987a8dade9" should not be locked diff --git a/src/Contract/LockClientRequestDto.php b/src/Contract/LockClientRequestDto.php new file mode 100644 index 0000000..2b52ac0 --- /dev/null +++ b/src/Contract/LockClientRequestDto.php @@ -0,0 +1,8 @@ +lock) { + $this->clientService->lock($client); + } else { + $this->clientService->unlock($client); + } + + return new Response(); + } } diff --git a/src/Controller/OrderController.php b/src/Controller/OrderController.php index 34699c2..d0f77b9 100644 --- a/src/Controller/OrderController.php +++ b/src/Controller/OrderController.php @@ -6,6 +6,7 @@ namespace App\Controller; use App\Aggregate\AcceptOrderCommand; use App\Contract\Order; +use App\Exception\ClientLockedException; use App\Exception\ClientNotExistsException; use App\Service\ClientService; use App\Service\OrderService; @@ -39,6 +40,8 @@ readonly class OrderController return new Response(); } catch (ClientNotExistsException) { return new Response(status: Response::HTTP_UNPROCESSABLE_ENTITY); + } catch (ClientLockedException) { + return new Response(status: Response::HTTP_FORBIDDEN); } } diff --git a/src/Entity/Client.php b/src/Entity/Client.php index ed15926..7f3a8d3 100644 --- a/src/Entity/Client.php +++ b/src/Entity/Client.php @@ -23,6 +23,9 @@ class Client #[ORM\Column] private ?int $currentBalance = null, + + #[ORM\Column(nullable: true)] + private ?\DateTimeImmutable $lockedAt = null, ) { $this->currentBalance ??= $this->initialBalance; } @@ -66,4 +69,19 @@ class Client { $this->currentBalance += $amount; } + + public function setLockedAt(?\DateTimeImmutable $dateTime) + { + $this->lockedAt = $dateTime; + } + + public function getLockedAt(): ?\DateTimeImmutable + { + return $this->lockedAt; + } + + public function isLocked(): bool + { + return $this->lockedAt !== null; + } } diff --git a/src/Exception/ClientLockedException.php b/src/Exception/ClientLockedException.php new file mode 100644 index 0000000..9477ca3 --- /dev/null +++ b/src/Exception/ClientLockedException.php @@ -0,0 +1,7 @@ +clientRepository->save($client); } - public function deduceFromBalance(Client $client, int $amount) + public function deduceFromBalance(Client $client, int $amount): void { $client->deduceFromBalance($amount); $this->clientRepository->save($client); } - public function topUpBalance(Client $client, int $amount) + public function topUpBalance(Client $client, int $amount): void { $client->topUpBalance($amount); $this->clientRepository->save($client); } + + public function lock(Client $client): void + { + $client->setLockedAt($this->clock->now()); + + $this->clientRepository->save($client); + } + + public function unlock(Client $client): void + { + $client->setLockedAt(null); + + $this->clientRepository->save($client); + } } diff --git a/src/Service/OrderService.php b/src/Service/OrderService.php index 3bd33c6..ecc151f 100644 --- a/src/Service/OrderService.php +++ b/src/Service/OrderService.php @@ -3,6 +3,7 @@ namespace App\Service; use App\Aggregate\AcceptOrderCommand; +use App\Exception\ClientLockedException; use Symfony\Component\Serializer\SerializerInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -16,8 +17,15 @@ readonly class OrderService ) { } + /** + * @throws \App\Exception\ClientLockedException + */ public function acceptOrder(AcceptOrderCommand $command) { + if ($command->client->isLocked()) { + throw new ClientLockedException(sprintf("Client '%s' is locked.", $command->client->getName())); + } + $this->crmClient->request('POST', '/order', [ 'body' => $this->serializer->serialize($command->order, format: 'json') ]); diff --git a/tests/Behat/ClientsContext.php b/tests/Behat/ClientsContext.php index 4916768..d85705a 100644 --- a/tests/Behat/ClientsContext.php +++ b/tests/Behat/ClientsContext.php @@ -47,6 +47,7 @@ class ClientsContext implements Context name: $row['name'], initialBalance: intval($row['initialBalance']), currentBalance: intval($row['currentBalance']), + lockedAt: $row['locked'] === 'yes' ? new \DateTimeImmutable() : null, ); $this->clientRepository->save($client); @@ -69,6 +70,22 @@ class ClientsContext implements Context Assert::assertNotNull($this->getClient($clientId)); } + /** + * @Given /^client with id "([^"]+)" should be locked$/ + */ + public function clientWithIdIsLocked(string $clientId) + { + Assert::assertTrue($this->getClient($clientId)->isLocked()); + } + + /** + * @Given /^client with id "([^"]+)" should not be locked$/ + */ + public function clientWithIdIsNotLocked(string $clientId) + { + Assert::assertFalse($this->getClient($clientId)->isLocked()); + } + /** * @Given /^client with id "([^"]+)" should have balance of (\d+)$/ */ @@ -76,7 +93,7 @@ class ClientsContext implements Context { $client = $this->getClient($clientId); - Assert::assertEquals($client->getBalance(), $balance); + Assert::assertEquals($balance, $client->getBalance()); } }