Commit c1b97428 authored by Mickaël Bourgier's avatar Mickaël Bourgier
Browse files

🔥 🚚 Remove code that has been moved to core library

- Repositories has been transformed to projection storages
- TimeShiftedEventProjection has been replaced by a more general EventsProjection
parent 1483228c
<?php
declare(strict_types=1);
namespace Webf\Projections\Bundle\Exception;
use Throwable;
interface ExceptionInterface extends Throwable
{
}
<?php
declare(strict_types=1);
namespace Webf\Projections\Bundle\Exception;
class RepositoryBackendException extends RuntimeException
{
}
<?php
declare(strict_types=1);
namespace Webf\Projections\Bundle\Exception;
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}
<?php
declare(strict_types=1);
namespace Webf\Projections\Bundle\Projection;
use Webf\Projections\Bundle\Repository\TimeShiftedEventRepository;
use Webf\Projections\Core\Event\TimeShiftedEventInterface;
use Webf\Projections\Core\Projection\ProjectionInterface;
use Webf\Projections\Core\Stream\FilterStream;
use Webf\Projections\Core\Stream\StreamInterface;
class TimeShiftedEventsProjection implements ProjectionInterface
{
private TimeShiftedEventRepository $repository;
public function __construct(TimeShiftedEventRepository $repository)
{
$this->repository = $repository;
}
public function processStream(StreamInterface $stream): void
{
$stream = new FilterStream($stream, [TimeShiftedEventInterface::class]);
/** @var TimeShiftedEventInterface $event */
foreach ($stream->iter() as $event) {
$this->repository->persist($event);
}
}
public function flush(): void
{
$this->repository->flush();
}
}
<?php
declare(strict_types=1);
namespace Webf\Projections\Bundle\Repository;
use DateTimeImmutable;
use League\Flysystem\FilesystemException;
use League\Flysystem\FilesystemOperator;
use Webf\Projections\Bundle\Exception\RepositoryBackendException;
use Webf\Projections\Bundle\Serializer\Serializer;
use Webf\Projections\Core\Event\TimeShiftedEventInterface;
class FlysystemTimeShiftedEventRepository implements TimeShiftedEventRepository
{
public const FILENAME = 'events';
private FilesystemOperator $filesystem;
private Serializer $serializer;
/**
* @var TimeShiftedEventInterface[]
*/
private array $eventsToPersist = [];
public function __construct(
FilesystemOperator $filesystem,
Serializer $serializer
) {
$this->filesystem = $filesystem;
$this->serializer = $serializer;
}
public function persist(TimeShiftedEventInterface $event): void
{
$this->eventsToPersist[] = $event;
}
public function flush(): void
{
$events = $this->readEvents();
foreach ($this->eventsToPersist as $event) {
$events[] = $event;
}
$this->writeEvents($events);
}
public function popEventsBefore(DateTimeImmutable $date): iterable
{
$events = $this->readEvents();
$eventsBeforeDate = [];
foreach ($events as $key => $event) {
if ($event->getApplicationDate()->getTimestamp() <= $date->getTimestamp()) {
$eventsBeforeDate[] = $event;
unset($events[$key]);
}
}
$this->writeEvents($events);
usort($eventsBeforeDate, function (TimeShiftedEventInterface $a, TimeShiftedEventInterface $b) {
return $a->getApplicationDate()->getTimestamp() - $b->getApplicationDate()->getTimestamp();
});
return $eventsBeforeDate;
}
/**
* @return TimeShiftedEventInterface[]
*/
private function readEvents(): array
{
try {
if (!$this->filesystem->fileExists(self::FILENAME)) {
return [];
}
/** @var TimeShiftedEventInterface[] $events */
$events = $this->serializer->deserialize(
$this->filesystem->read(self::FILENAME)
);
} catch (FilesystemException $e) {
throw new RepositoryBackendException($e->getMessage(), 0, $e);
}
return $events;
}
/**
* @param TimeShiftedEventInterface[] $events
*/
private function writeEvents(array $events): void
{
try {
$this->filesystem->write(self::FILENAME, $this->serializer->serialize($events));
} catch (FilesystemException $e) {
throw new RepositoryBackendException($e->getMessage(), 0, $e);
}
}
}
<?php
declare(strict_types=1);
namespace Webf\Projections\Bundle\Repository;
use DateTimeImmutable;
use Webf\Projections\Bundle\Exception\RepositoryBackendException;
use Webf\Projections\Core\Event\TimeShiftedEventInterface;
interface TimeShiftedEventRepository
{
public function persist(TimeShiftedEventInterface $event): void;
/**
* @throws RepositoryBackendException if an error occured when accessing the underlaying backend
*/
public function flush(): void;
/**
* Returns events that apply before the given date, ordered by ascending
* application date.
*
* @throws RepositoryBackendException if an error occured when accessing the underlaying backend
*/
public function popEventsBefore(DateTimeImmutable $date): iterable;
}
<?php
declare(strict_types=1);
namespace Webf\Projections\Bundle\Serializer;
class PhpSerializer implements Serializer
{
public function serialize(mixed $data): string
{
return serialize($data);
}
public function deserialize(string $string): mixed
{
return unserialize($string);
}
}
<?php
declare(strict_types=1);
namespace Webf\Projections\Bundle\Serializer;
interface Serializer
{
public function serialize(mixed $data): string;
public function deserialize(string $string): mixed;
}
<?php
declare(strict_types=1);
namespace Webf\Projections\Bundle\Test\Repository;
use DateTimeImmutable;
use Webf\Projections\Bundle\Repository\TimeShiftedEventRepository;
use Webf\Projections\Core\Event\TimeShiftedEventInterface;
class InMemoryTimeShiftedEventRepository implements TimeShiftedEventRepository
{
/** @var TimeShiftedEventInterface[] */
private array $persistedEvents = [];
/** @var TimeShiftedEventInterface[] */
private array $flushedEvents = [];
public function persist(TimeShiftedEventInterface $event): void
{
$this->persistedEvents[] = $event;
}
public function flush(): void
{
foreach ($this->persistedEvents as $key => $event) {
$this->flushedEvents[] = $event;
unset($this->persistedEvents[$key]);
}
}
public function popEventsBefore(DateTimeImmutable $date): iterable
{
throw new \RuntimeException('Not implemented');
}
/**
* @return TimeShiftedEventInterface[]
*/
public function getPersistedEvents(): array
{
return $this->persistedEvents;
}
/**
* @return TimeShiftedEventInterface[]
*/
public function getFlushedEvents(): array
{
return $this->flushedEvents;
}
}
<?php
declare(strict_types=1);
namespace Tests\Event\Stub;
use DateTimeImmutable;
use Webf\Projections\Core\Event\EventInterface;
class EventStub implements EventInterface
{
private DateTimeImmutable $date;
public function __construct(DateTimeImmutable $date = null)
{
$this->date = $date ?? new DateTimeImmutable();
}
public function getDate(): DateTimeImmutable
{
return $this->date;
}
}
<?php
declare(strict_types=1);
namespace Tests\Event\Stub;
use DateTimeImmutable;
use Webf\Projections\Core\Event\TimeShiftedEventInterface;
class TimeShiftedEventStub extends EventStub implements TimeShiftedEventInterface
{
private DateTimeImmutable $applicationDate;
public function __construct(
DateTimeImmutable $date = null,
DateTimeImmutable $applicationDate = null
) {
parent::__construct($date);
$this->applicationDate = $applicationDate ?? $this->getDate();
}
public function getApplicationDate(): DateTimeImmutable
{
return $this->applicationDate;
}
}
<?php
declare(strict_types=1);
namespace Tests\Projection;
use PHPUnit\Framework\TestCase;
use Tests\Event\Stub\EventStub;
use Tests\Event\Stub\TimeShiftedEventStub;
use Webf\Projections\Bundle\Projection\TimeShiftedEventsProjection;
use Webf\Projections\Bundle\Test\Repository\InMemoryTimeShiftedEventRepository;
use Webf\Projections\Core\Stream\ArrayStream;
/**
* @internal
* @covers \Webf\Projections\Bundle\Projection\TimeShiftedEventsProjection
*/
class TimeShiftedEventsProjectionTest extends TestCase
{
public function test_handle_event_persists_in_repository()
{
$repository = new InMemoryTimeShiftedEventRepository();
$projection = new TimeShiftedEventsProjection($repository);
$projection->processStream(new ArrayStream([
$e1 = new TimeShiftedEventStub(),
$e2 = new EventStub(),
$e3 = new TimeShiftedEventStub(),
]));
$this->assertSame([$e1, $e3], $repository->getPersistedEvents());
$this->assertEmpty($repository->getFlushedEvents());
}
public function test_flush_flushes_repository()
{
$repository = new InMemoryTimeShiftedEventRepository();
$projection = new TimeShiftedEventsProjection($repository);
$projection->processStream(new ArrayStream([
$e1 = new TimeShiftedEventStub(),
$e2 = new TimeShiftedEventStub(),
]));
$projection->flush();
$this->assertEmpty($repository->getPersistedEvents());
$this->assertSame([$e1, $e2], $repository->getFlushedEvents());
}
}
<?php
declare(strict_types=1);
namespace Tests\Repository;
use DateTimeImmutable;
use League\Flysystem\Filesystem;
use League\Flysystem\FilesystemOperator;
use League\Flysystem\InMemory\InMemoryFilesystemAdapter;
use League\Flysystem\UnableToReadFile;
use League\Flysystem\UnableToWriteFile;
use PHPUnit\Framework\TestCase;
use Tests\Event\Stub\TimeShiftedEventStub;
use Webf\Projections\Bundle\Exception\RepositoryBackendException;
use Webf\Projections\Bundle\Repository\FlysystemTimeShiftedEventRepository;
use Webf\Projections\Bundle\Serializer\PhpSerializer;
/**
* @internal
* @covers \Webf\Projections\Bundle\Repository\FlysystemTimeShiftedEventRepository
*/
class FlysystemTimeShiftedEventRepositoryTest extends TestCase
{
public function test_pop_events_before_returns_events_before_given_date_in_application_date_order()
{
$repository = new FlysystemTimeShiftedEventRepository(
$filesystem = new Filesystem(new InMemoryFilesystemAdapter()),
$serializer = new PhpSerializer()
);
$repository->persist(
$e1 = new TimeShiftedEventStub(
new DateTimeImmutable(),
new DateTimeImmutable('+2 hours'),
)
);
$repository->persist(
$e2 = new TimeShiftedEventStub(
new DateTimeImmutable(),
new DateTimeImmutable('+1 hour'),
)
);
$repository->persist(
$e3 = new TimeShiftedEventStub(
new DateTimeImmutable(),
$inThreeHours = new DateTimeImmutable('+3 hours'),
)
);
$repository->persist(
$e4 = new TimeShiftedEventStub(
new DateTimeImmutable(),
$inFourHours = new DateTimeImmutable('+4 hours'),
)
);
$repository->flush();
$this->assertEquals(
[$e2, $e1, $e3],
$repository->popEventsBefore($inThreeHours)
);
$this->assertEquals(
[$e4],
$repository->popEventsBefore($inFourHours)
);
}
public function test_flysystem_exceptions_are_translated_on_read()
{
$filesystem = $this->createMock(FilesystemOperator::class);
$filesystem
->expects($this->any())
->method('fileExists')
->willReturn(true)
;
$filesystem
->expects($this->any())
->method('read')
->willThrowException(new UnableToReadFile())
;
$filesystem
->expects($this->any())
->method('write')
->willThrowException(new UnableToWriteFile())
;
$repository = new FlysystemTimeShiftedEventRepository(
$filesystem,
$serializer = new PhpSerializer()
);
$this->expectException(RepositoryBackendException::class);
$this->expectExceptionCode(0);
$repository->flush();
}
public function test_flysystem_exceptions_are_translated_on_write()
{
$filesystem = $this->createMock(FilesystemOperator::class);
$filesystem
->expects($this->any())
->method('fileExists')
->willReturn(false)
;
$filesystem
->expects($this->any())
->method('write')
->willThrowException(new UnableToWriteFile())
;
$repository = new FlysystemTimeShiftedEventRepository(
$filesystem,
$serializer = new PhpSerializer()
);
$this->expectException(RepositoryBackendException::class);
$this->expectExceptionCode(0);
$repository->popEventsBefore(new DateTimeImmutable());
}
}
<?php
declare(strict_types=1);
namespace Tests\Serializer;
use PHPUnit\Framework\TestCase;
use Tests\Event\Stub\EventStub;
use Webf\Projections\Bundle\Serializer\PhpSerializer;
/**
* @internal
* @covers \Webf\Projections\Bundle\Serializer\PhpSerializer
*/
class PhpSerializerTest extends TestCase
{
public function test_deserialize_a_serialized_value_returns_equivalent_data()
{
$serializer = new PhpSerializer();
$this->assertEquals(
$event = new EventStub(),
$serializer->deserialize($serializer->serialize($event))
);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment