diff --git a/docs/manual/resources/order.md b/docs/manual/resources/order.md index b7c455e..49ae98f 100644 --- a/docs/manual/resources/order.md +++ b/docs/manual/resources/order.md @@ -175,6 +175,60 @@ foreach ($result->wait(60)->getTickets() as $ticket) { $ticket->getId(); $ticket->getStatus(); } + +// Fetch all Batches objects generated for the operation +foreach ($result->getBatches() as $batch) { + $response = $batch->getResponse(); + $batchId = $response->getBatchId(); + + // Fetch all tickets generated for the operation + foreach ($response->getTickets() as $ticket) { + $ticket->getId(); + $ticket->getStatus(); + } + + // Fetch all reports generated for the operation + foreach ($response->getReport() as $operationReport) { + $operationReport['id]']; + $operationReport['channelName']; + $operationReport['reference']; + $operationReport['state']; + $operationReport['message']; + } +} + +// Both writing will work +$tickets = $result->wait(60)->getTickets(); +$batchs = $result->wait(60)->getBatches(); + +// But if you want to check what append for each operation : +$ignoredOperations = []; +$failedOperations = []: +$succeedOperations = []; +$stillProcessingOperations = []; + +foreach ($result->wait(60)->getBatches() as $batch) { + $response = $batch->getResponse(); + + foreach ($response->getTickets() as $ticket) { + if (null !== $ticket->getFinishedAt()) { + if ('succeed' === $ticket->getStatus()) { + $succeedOperations[] = $ticket->getPayload()['id']; + } else { + $failedOperations[] = $ticket->getPayload()['id']; + } + } else { + $stillProcessingOperations = $ticket->getPayload()['id']; + } + } + + foreach ($response->getReport() as $operationReport) { + if ('ignored' === $operationReport['state']) { + $ignoredOperations[] = $operationReport['id']; + } + } +} + ``` ### Accept diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 6f295c5..98d21c6 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -262,7 +262,7 @@ parameters: message: '#^Call to an undefined method iterable\&Traversable\:\:wait\(\)\.$#' identifier: method.notFound count: 1 - path: src/Api/Order/OrderOperationResult.php + path: src/Api/Order/OrderOperationResponse.php - message: '#^Method ShoppingFeed\\Sdk\\Api\\Order\\OrderOperationResult\:\:__construct\(\) has parameter \$resources with no value type specified in iterable type array\.$#' @@ -270,24 +270,6 @@ parameters: count: 1 path: src/Api/Order/OrderOperationResult.php - - - message: '#^Method ShoppingFeed\\Sdk\\Api\\Order\\OrderOperationResult\:\:setBatches\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: src/Api/Order/OrderOperationResult.php - - - - message: '#^Method ShoppingFeed\\Sdk\\Api\\Order\\OrderOperationResult\:\:setBatches\(\) has parameter \$resources with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: src/Api/Order/OrderOperationResult.php - - - - message: '#^PHPDoc tag @var above a method has no effect\.$#' - identifier: varTag.misplaced - count: 1 - path: src/Api/Order/OrderOperationResult.php - - message: '#^Cannot call method withAddedHref\(\) on ShoppingFeed\\Sdk\\Hal\\HalLink\|null\.$#' identifier: method.nonObject diff --git a/src/Api/Order/OrderOperationBatch.php b/src/Api/Order/OrderOperationBatch.php new file mode 100644 index 0000000..bdff0b3 --- /dev/null +++ b/src/Api/Order/OrderOperationBatch.php @@ -0,0 +1,23 @@ +response = new OrderOperationResponse($resource); + } + + public function getResponse(): OrderOperationResponse + { + return $this->response; + } +} diff --git a/src/Api/Order/OrderOperationResponse.php b/src/Api/Order/OrderOperationResponse.php new file mode 100644 index 0000000..633e271 --- /dev/null +++ b/src/Api/Order/OrderOperationResponse.php @@ -0,0 +1,83 @@ +batchId = $resource->getProperty('id'); + $this->report = $resource->getProperty('report'); + $link = $resource->getLink('ticket'); + + if (! $link instanceof HalLink) { + throw new RuntimeException('Ticket link is missing from the OrderOperationResponse resource.'); + } + + $this->ticketDomain = new Task\TicketDomain($link); + } + + public function getBatchId(): string + { + return $this->batchId; + } + + /** + * Get the iterator for all tickets generated by the operation + * + * @return Task\TicketResource[] + */ + public function getTickets(): iterable + { + yield from $this->ticketDomain->getByBatch($this->batchId); + } + + /** + * Wait for all tickets to be processed. + * + * $timeout Seconds to wait for each batch until stop + * $sleepSecs Seconds to wait between to calls + * + * @return $this The current instance + */ + public function wait(?int $timeout = null, int $sleepSecs = 1): self + { + $this->ticketDomain->getByBatch($this->batchId)->wait($timeout, $sleepSecs); + + return $this; + } + + /** + * @return array{int, array{ + * id: int|null, + * channelName: string|null, + * reference: string|null, + * state: string, + * message: string + * }} + */ + public function getReport(): array + { + return $this->report; + } +} diff --git a/src/Api/Order/OrderOperationResult.php b/src/Api/Order/OrderOperationResult.php index 3523377..75e9cad 100644 --- a/src/Api/Order/OrderOperationResult.php +++ b/src/Api/Order/OrderOperationResult.php @@ -2,12 +2,10 @@ namespace ShoppingFeed\Sdk\Api\Order; -use ShoppingFeed\Sdk\Api\Task; - class OrderOperationResult { - /** @var Task\TicketDomain[] */ - private $batches; + /** @var OrderOperationBatch[] $batches */ + private array $batches; public function __construct(array $resources = []) { @@ -17,14 +15,12 @@ public function __construct(array $resources = []) /** * Get the iterator for all tickets generated by the operation * - * @return Task\TicketResource[]|\Traversable + * @return \ShoppingFeed\Sdk\Api\Task\TicketResource[]|\Traversable */ public function getTickets() { - foreach ($this->batches as $id => $domain) { - foreach ($domain->getByBatch($id) as $ticket) { - yield $ticket; - } + foreach ($this->batches as $batch) { + yield from $batch->getResponse()->getTickets(); } } @@ -33,39 +29,49 @@ public function getTickets() */ public function getBatchIds() { - return array_keys($this->batches); + $batchIds = []; + + foreach ($this->batches as $batch) { + $batchIds[] = $batch->getResponse()->getBatchId(); + } + + return $batchIds; } /** * Wait for all tickets to be processed. * - * @param int $timeout Seconds to wait for each batch until stop + * @param ?int $timeout Seconds to wait for each batch until stop * @param int $sleepSecs Seconds to wait between to calls * * @return $this The current instance */ public function wait($timeout = null, $sleepSecs = 1) { - foreach ($this->batches as $id => $domain) { - $domain->getByBatch($id)->wait($timeout, $sleepSecs); + foreach ($this->batches as $batch) { + $batch->getResponse()->wait((int) $timeout, (int) $sleepSecs); } return $this; } /** - * @var \ShoppingFeed\Sdk\Hal\HalResource[] $resources + * @param \ShoppingFeed\Sdk\Hal\HalResource[] $resources */ - private function setBatches(array $resources) + private function setBatches(array $resources): void { $this->batches = []; foreach ($resources as $resource) { - // Stored element is the domain in order to avoid early api calls - $batchId = $resource->getProperty('id'); - $domain = new Task\TicketDomain($resource->getLink('ticket')); - - $this->batches[$batchId] = $domain; + $this->batches[] = new OrderOperationBatch($resource); } } + + /** + * @return OrderOperationBatch[] + */ + public function getBatches(): array + { + return $this->batches; + } } diff --git a/tests/unit/Api/Order/OrderOperationResponseTest.php b/tests/unit/Api/Order/OrderOperationResponseTest.php new file mode 100644 index 0000000..5dbccbe --- /dev/null +++ b/tests/unit/Api/Order/OrderOperationResponseTest.php @@ -0,0 +1,59 @@ +resource = $this->createResourceMock('a', 1, 'REF123', $this->link); + $this->response = new OrderOperationResponse($this->resource); + } + + protected function getInstance(): OrderOperationResponse|OrderOperationResult + { + return $this->response; + } + + protected function getWaitCallCount(): int + { + return 1; + } + + public function testBatchId(): void + { + $this->assertSame('a', $this->response->getBatchId()); + } + + public function testGetReport(): void + { + $expectedReport = [ + [ + 'id' => 1, + 'channelName' => 'Channel A', + 'reference' => 'REF123', + 'state' => 'success', + 'message' => 'Order processed successfully.' + ] + ]; + + $this->assertSame($expectedReport, $this->response->getReport()); + } + + public function testExceptionOnMissingTicketLink(): void + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Ticket link is missing from the OrderOperationResponse resource.'); + + $resource = $this->createResourceMock('a', 1, 'REF123'); + new OrderOperationResponse($resource); + } +} diff --git a/tests/unit/Api/Order/OrderOperationResultTest.php b/tests/unit/Api/Order/OrderOperationResultTest.php index 1e46e5d..23b6c98 100644 --- a/tests/unit/Api/Order/OrderOperationResultTest.php +++ b/tests/unit/Api/Order/OrderOperationResultTest.php @@ -1,84 +1,52 @@ link = $this->createMock(HalLink::class); - - $resource = $this->createMock(HalResource::class); - $resource - ->expects($this->any()) - ->method('getLink') - ->with('ticket') - ->willReturn($this->link); + parent::setUp(); - $resource - ->expects($this->exactly(2)) - ->method('getProperty') - ->with('id') - ->willReturnOnConsecutiveCalls('a', 'b'); - - $this->resources['a'] = $resource; - $this->resources['b'] = $resource; + $this->resources = [ + 'a' => $this->createResourceMock('a', 1, 'REF123', $this->link), + 'b' => $this->createResourceMock('b', 2, 'REF456', $this->link), + ]; $this->instance = new OrderOperationResult($this->resources); } - public function testBatchIds() + protected function getInstance(): OrderOperationResponse|OrderOperationResult { - $this->assertSame(['a', 'b'], $this->instance->getBatchIds()); + return $this->instance; } - public function testWait() + protected function getWaitCallCount(): int { - $this->link - ->expects($this->exactly(2)) - ->method('get') - ->willReturn($this->createMock(HalResource::class)); + return 2; + } - $this->assertSame( - $this->instance, - $this->instance->wait(1, 0.1) - ); + public function testBatchIds(): void + { + $this->assertSame(['a', 'b'], $this->instance->getBatchIds()); } - public function testGetTickets() + public function testGetBatches(): void { - $resource = $this->createMock(HalResource::class); - $this->link - ->expects($this->exactly(2)) - ->method('get') - ->willReturn($resource); + $batches = $this->instance->getBatches(); - $resource - ->expects($this->any()) - ->method('getAllResources') - ->willReturn([$resource]); + $this->assertCount(2, $batches); - $this->assertContainsOnly( - TicketResource::class, - $this->instance->getTickets() - ); + foreach ($batches as $batch) { + $this->assertInstanceOf(OrderOperationBatch::class, $batch); + } } } - diff --git a/tests/unit/Api/Order/OrderOperationTestCase.php b/tests/unit/Api/Order/OrderOperationTestCase.php new file mode 100644 index 0000000..a027448 --- /dev/null +++ b/tests/unit/Api/Order/OrderOperationTestCase.php @@ -0,0 +1,84 @@ +link = $this->createMock(HalLink::class); + } + + protected function createResourceMock($batchId, $orderId, $reference, $link = null) + { + $resource = $this->createMock(HalResource::class); + + $resource + ->method('getLink') + ->with('ticket') + ->willReturn($link); + + $callCount = 0; + $resource + ->method('getProperty') + ->willReturnCallback(function () use (&$callCount, $batchId, $orderId, $reference) { + $callCount++; + return match ($callCount) { + 1, 3 => $batchId, + 2, 4 => [ + [ + 'id' => $orderId, + 'channelName' => 'Channel A', + 'reference' => $reference, + 'state' => 'success', + 'message' => 'Order processed successfully.' + ] + ], + default => null, + }; + }); + + return $resource; + } + + abstract protected function getInstance(): OrderOperationResponse|OrderOperationResult; + + abstract protected function getWaitCallCount(): int; + + public function testWait(): void + { + $this->link + ->expects($this->exactly($this->getWaitCallCount())) + ->method('get') + ->willReturn($this->createMock(HalResource::class)); + + $this->assertSame($this->getInstance(), $this->getInstance()->wait(1)); + } + + public function testGetTickets(): void + { + $resource = $this->createMock(HalResource::class); + $this->link + ->expects($this->exactly($this->getWaitCallCount())) + ->method('get') + ->willReturn($resource); + + $resource + ->method('getAllResources') + ->willReturn([$resource]); + + $this->assertContainsOnly( + TicketResource::class, + $this->getInstance()->getTickets() + ); + } +}