Skip to content

Commit

Permalink
Add an item writer that dispatch events before and after writing (#89)
Browse files Browse the repository at this point in the history
  • Loading branch information
yann-eugone authored Jul 10, 2023
1 parent d526418 commit 5e7857f
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 1 deletion.
2 changes: 2 additions & 0 deletions docs/domain/item-job/item-writer.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ It can be any class implementing [ItemWriterInterface](../../../src/Job/Item/Ite
write items on multiple item writers.
- [ConditionalWriter](../../../src/Job/Item/Writer/ConditionalWriter.php):
will only write items that are matching your conditions.
- [DispatchEventsWriter](../../../src/Job/Item/Writer/DispatchEventsWriter.php):
will dispatch events before and after writing.
- [LaunchJobForEachItemWriter](../../../src/Job/Item/Writer/LaunchJobForEachItemWriter.php):
launch another job for each items.
- [LaunchJobForItemsBatchWriter](../../../src/Job/Item/Writer/LaunchJobForItemsBatchWriter.php):
Expand Down
18 changes: 18 additions & 0 deletions src/Event/PostWriteEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Yokai\Batch\Event;

use Yokai\Batch\Job\Item\ItemJob;
use Yokai\Batch\Job\Item\ItemWriterInterface;
use Yokai\Batch\Job\Item\Writer\DispatchEventsWriter;

/**
* This event is triggered by {@see DispatchEventsWriter}
* whenever an {@see ItemJob} is calling the {@see ItemWriterInterface} to write
* after actual write is performed.
*/
final class PostWriteEvent extends JobEvent
{
}
18 changes: 18 additions & 0 deletions src/Event/PreWriteEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Yokai\Batch\Event;

use Yokai\Batch\Job\Item\ItemJob;
use Yokai\Batch\Job\Item\ItemWriterInterface;
use Yokai\Batch\Job\Item\Writer\DispatchEventsWriter;

/**
* This event is triggered by {@see DispatchEventsWriter}
* whenever an {@see ItemJob} is calling the {@see ItemWriterInterface} to write
* before actual write is performed.
*/
final class PreWriteEvent extends JobEvent
{
}
50 changes: 50 additions & 0 deletions src/Job/Item/Writer/DispatchEventsWriter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace Yokai\Batch\Job\Item\Writer;

use Psr\EventDispatcher\EventDispatcherInterface;
use Yokai\Batch\Event\PostWriteEvent;
use Yokai\Batch\Event\PreWriteEvent;
use Yokai\Batch\Job\Item\AbstractElementDecorator;
use Yokai\Batch\Job\Item\ItemWriterInterface;

/**
* This {@see ItemWriterInterface} act as decorator,
* and will dispatch {@see PreWriteEvent} before, and {@see PostWriteEvent} after.
*/
final class DispatchEventsWriter extends AbstractElementDecorator implements ItemWriterInterface
{
public function __construct(
private EventDispatcherInterface $eventDispatcher,
private ItemWriterInterface $writer,
) {
}

public function write(iterable $items): void
{
$this->dispatch(new PreWriteEvent($this->getJobExecution()));

$this->writer->write($items);

$this->dispatch(new PostWriteEvent($this->getJobExecution()));
}

protected function getDecoratedElements(): iterable
{
return [$this->writer];
}

private function dispatch(object $event): void
{
try {
$this->eventDispatcher->dispatch($event);
} catch (\Throwable $error) {
$this->getJobExecution()->getLogger()->error(
'An error occurred while dispatching event.',
['event' => $event::class, 'error' => (string)$error],
);
}
}
}
10 changes: 9 additions & 1 deletion src/Job/JobExecutionAwareTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,18 @@ public function setJobExecution(JobExecution $jobExecution): void
$this->jobExecution = $jobExecution;
}

/**
* Get current job execution.
*/
protected function getJobExecution(): JobExecution
{
return $this->jobExecution;
}

/**
* Get root execution of current job execution.
*/
public function getRootExecution(): JobExecution
protected function getRootExecution(): JobExecution
{
return $this->jobExecution->getRootExecution();
}
Expand Down
5 changes: 5 additions & 0 deletions tests/Job/ConfigurableElement.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ final class ConfigurableElement implements
use JobParametersAwareTrait;
use SummaryAwareTrait;

public function getRootExecution(): JobExecution
{
return $this->jobExecution->getRootExecution();
}

public function getJobExecution(): JobExecution
{
return $this->jobExecution;
Expand Down
44 changes: 44 additions & 0 deletions tests/Job/Item/Writer/DispatchEventsWriterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Yokai\Batch\Tests\Job\Item\Writer;

use PHPUnit\Framework\TestCase;
use Yokai\Batch\Event\PostWriteEvent;
use Yokai\Batch\Event\PreWriteEvent;
use Yokai\Batch\Job\Item\Writer\DispatchEventsWriter;
use Yokai\Batch\Job\Item\Writer\NullWriter;
use Yokai\Batch\JobExecution;
use Yokai\Batch\Test\Job\Item\Writer\TestDebugWriter;
use Yokai\Batch\Tests\Dummy\DebugEventDispatcher;

class DispatchEventsWriterTest extends TestCase
{
public function test(): void
{
$writer = new DispatchEventsWriter(
$dispatcher = new DebugEventDispatcher(),
$decorated = new TestDebugWriter(new NullWriter()),
);
$dispatcher->addListener(PostWriteEvent::class, function () {
throw new \RuntimeException('Test exception');
});

$writer->setJobExecution($execution = JobExecution::createRoot('123', 'foo'));
$writer->initialize();
$writer->write(['irrelevant']);
$writer->flush();

$decorated->assertWasConfigured();
$decorated->assertWasUsed();
$events = $dispatcher->getEvents();
self::assertCount(2, $events);
self::assertInstanceOf(PreWriteEvent::class, $events[0] ?? null);
self::assertInstanceOf(PostWriteEvent::class, $events[1] ?? null);
self::assertStringContainsString(
'ERROR: An error occurred while dispatching event. {"event":"Yokai\\\\Batch\\\\Event\\\\PostWriteEvent","error":"RuntimeException: Test exception',
(string)$execution->getLogs(),
);
}
}

0 comments on commit 5e7857f

Please sign in to comment.