Skip to content

Commit

Permalink
Dispatch events on import success / failure (#33)
Browse files Browse the repository at this point in the history
* dispatch events on import success / failure
  • Loading branch information
dhirtzbruch authored Nov 20, 2023
1 parent f73b2a1 commit 4e7e055
Show file tree
Hide file tree
Showing 10 changed files with 319 additions and 6 deletions.
50 changes: 44 additions & 6 deletions src/EntityImporterManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,16 @@

namespace Fastbolt\EntityImporter;

use DateTime;
use Exception;
use Fastbolt\EntityImporter\Events\ImportFailureEvent;
use Fastbolt\EntityImporter\Events\ImportSuccessEvent;
use Fastbolt\EntityImporter\Exceptions\ImporterDefinitionNotFoundException;
use Fastbolt\EntityImporter\Exceptions\InvalidInputFormatException;
use Fastbolt\EntityImporter\Exceptions\SourceUnavailableException;
use Fastbolt\EntityImporter\Types\ImportResult;
use InvalidArgumentException;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Throwable;

class EntityImporterManager
Expand All @@ -24,13 +32,23 @@ class EntityImporterManager
*/
private EntityImporter $importer;

/**
* @var EventDispatcherInterface
*/
private EventDispatcherInterface $eventDispatcher;

/**
* @param EntityImporter $importer
* @param EventDispatcherInterface $eventDispatcher
* @param iterable<EntityImporterDefinition> $importerDefinitions
*/
public function __construct(EntityImporter $importer, iterable $importerDefinitions)
{
$this->importer = $importer;
public function __construct(
EntityImporter $importer,
EventDispatcherInterface $eventDispatcher,
iterable $importerDefinitions
) {
$this->importer = $importer;
$this->eventDispatcher = $eventDispatcher;

foreach ($importerDefinitions as $importerDefinition) {
$this->definitions[$importerDefinition->getName()] = $importerDefinition;
Expand All @@ -52,13 +70,33 @@ public function getImporterDefinitions(): array
* @param int|null $limit
*
* @return ImportResult
*
* @throws InvalidInputFormatException
* @throws SourceUnavailableException
* @throws InvalidArgumentException
*/
public function import(string $name, callable $statusCallback, callable $errorCallback, ?int $limit): ImportResult
{
if (!$name || null === ($definition = $this->definitions[$name] ?? null)) {
throw new ImporterDefinitionNotFoundException($name);
$start = new DateTime();
$definition = null;
try {
if (!$name) {
throw new InvalidArgumentException('Name must not be empty');
}

if (null === ($definition = $this->definitions[$name] ?? null)) {
throw new ImporterDefinitionNotFoundException($name);
}

$return = $this->importer->import($definition, $statusCallback, $errorCallback, $limit);
} catch (Exception $exception) {
$this->eventDispatcher->dispatch(new ImportFailureEvent($definition, $start, $exception));

throw $exception;
}

return $this->importer->import($definition, $statusCallback, $errorCallback, $limit);
$this->eventDispatcher->dispatch(new ImportSuccessEvent($definition, $start, $return));

return $return;
}
}
70 changes: 70 additions & 0 deletions src/Events/ImportFailureEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

/**
* Copyright © Fastbolt Schraubengroßhandels GmbH.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Fastbolt\EntityImporter\Events;

use DateTimeInterface;
use Exception;
use Fastbolt\EntityImporter\EntityImporterDefinition;

class ImportFailureEvent
{
/**
* @var Exception
*/
private Exception $exception;

/**
* @var EntityImporterDefinition|null
*/
private ?EntityImporterDefinition $definition;

/**
* @var DateTimeInterface
*/
private DateTimeInterface $importStart;

/**
* @param EntityImporterDefinition|null $definition
* @param DateTimeInterface $importStart
* @param Exception $exception
*/
public function __construct(
?EntityImporterDefinition $definition,
DateTimeInterface $importStart,
Exception $exception
) {
$this->definition = $definition;
$this->importStart = $importStart;
$this->exception = $exception;
}

/**
* @return Exception
*/
public function getException(): Exception
{
return $this->exception;
}

/**
* @return EntityImporterDefinition|null
*/
public function getDefinition(): ?EntityImporterDefinition
{
return $this->definition;
}

/**
* @return DateTimeInterface
*/
public function getImportStart(): DateTimeInterface
{
return $this->importStart;
}
}
70 changes: 70 additions & 0 deletions src/Events/ImportSuccessEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

/**
* Copyright © Fastbolt Schraubengroßhandels GmbH.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Fastbolt\EntityImporter\Events;

use DateTimeInterface;
use Fastbolt\EntityImporter\EntityImporterDefinition;
use Fastbolt\EntityImporter\Types\ImportResult;

class ImportSuccessEvent
{
/**
* @var ImportResult
*/
private ImportResult $importResult;

/**
* @var EntityImporterDefinition
*/
private EntityImporterDefinition $definition;

/**
* @var DateTimeInterface
*/
private DateTimeInterface $importStart;

/**
* @param EntityImporterDefinition $definition
* @param DateTimeInterface $importStart
* @param ImportResult $importResult
*/
public function __construct(
EntityImporterDefinition $definition,
DateTimeInterface $importStart,
ImportResult $importResult
) {
$this->definition = $definition;
$this->importStart = $importStart;
$this->importResult = $importResult;
}

/**
* @return ImportResult
*/
public function getImportResult(): ImportResult
{
return $this->importResult;
}

/**
* @return EntityImporterDefinition
*/
public function getDefinition(): EntityImporterDefinition
{
return $this->definition;
}

/**
* @return DateTimeInterface
*/
public function getImportStart(): DateTimeInterface
{
return $this->importStart;
}
}
1 change: 1 addition & 0 deletions src/Reader/Factory/CsvReaderFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class CsvReaderFactory implements ReaderFactoryInterface
* @param array<string,mixed> $options
*
* @return CsvReader
* @throws SourceUnavailableException
*/
public function getReader(EntityImporterDefinition $importerDefinition, array $options): CsvReader
{
Expand Down
3 changes: 3 additions & 0 deletions src/Reader/Factory/ReaderFactoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
namespace Fastbolt\EntityImporter\Reader\Factory;

use Fastbolt\EntityImporter\EntityImporterDefinition;
use Fastbolt\EntityImporter\Exceptions\SourceUnavailableException;
use Fastbolt\EntityImporter\Reader\ReaderInterface;

interface ReaderFactoryInterface
Expand All @@ -18,6 +19,8 @@ interface ReaderFactoryInterface
* @param array<string,mixed> $options Array containing implementation-specific options
*
* @return ReaderInterface
*
* @throws SourceUnavailableException
*/
public function getReader(EntityImporterDefinition $importerDefinition, array $options): ReaderInterface;

Expand Down
2 changes: 2 additions & 0 deletions src/Reader/Factory/XlsxReaderFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class XlsxReaderFactory implements ReaderFactoryInterface
* @param array<string,mixed> $options Array containing implementation-specific options
*
* @return XlsxReader
*
* @throws SourceUnavailableException
*/
public function getReader(EntityImporterDefinition $importerDefinition, array $options): XlsxReader
{
Expand Down
1 change: 1 addition & 0 deletions src/Resources/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ services:
Fastbolt\EntityImporter\EntityImporterManager:
$importer: '@Fastbolt\EntityImporter\EntityImporter'
$importerDefinitions: !tagged fastbolt.entity_importer.definition
$eventDispatcher: '@Symfony\Contracts\EventDispatcher\EventDispatcherInterface'

Fastbolt\EntityImporter\Reader\Factory\ReaderFactoryManager:
$factories: !tagged fastbolt.entity_importer.reader_factory
Expand Down
34 changes: 34 additions & 0 deletions tests/unit/EntityImporterManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@
use Fastbolt\EntityImporter\EntityImporter;
use Fastbolt\EntityImporter\EntityImporterDefinition;
use Fastbolt\EntityImporter\EntityImporterManager;
use Fastbolt\EntityImporter\Events\ImportFailureEvent;
use Fastbolt\EntityImporter\Events\ImportSuccessEvent;
use Fastbolt\EntityImporter\Exceptions\ImporterDefinitionNotFoundException;
use Fastbolt\TestHelpers\BaseTestCase;
use InvalidArgumentException;
use PHPUnit\Framework\MockObject\MockObject;
use stdClass;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

/**
* @covers \Fastbolt\EntityImporter\EntityImporterManager
Expand Down Expand Up @@ -46,10 +50,16 @@ class EntityImporterManagerTest extends BaseTestCase
*/
private $errorCallback;

/**
* @var EventDispatcherInterface|MockObject
*/
private $dispatcher;

public function testImport(): void
{
$manager = new EntityImporterManager(
$this->importer,
$this->dispatcher,
[
$this->definition1,
$this->definition2,
Expand All @@ -65,6 +75,9 @@ public function testImport(): void
$this->importer->expects(self::once())
->method('import')
->with($this->definition2, $this->statusCallback, $this->errorCallback);
$this->dispatcher->expects(self::once())
->method('dispatch')
->with(self::isInstanceOf(ImportSuccessEvent::class));

$result = $manager->import('importer:def2:name', $this->statusCallback, $this->errorCallback, null);
}
Expand All @@ -75,17 +88,38 @@ public function testImportInvalidType(): void
$this->expectExceptionMessage('Importer importer:def2:name not found.');
$manager = new EntityImporterManager(
$this->importer,
$this->dispatcher,
[]
);
$this->dispatcher->expects(self::once())
->method('dispatch')
->with(self::isInstanceOf(ImportFailureEvent::class));
self::assertSame([], $manager->getImporterDefinitions());
$manager->import('importer:def2:name', $this->statusCallback, $this->errorCallback, null);
}

public function testImportEmptyType(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Name must not be empty');
$manager = new EntityImporterManager(
$this->importer,
$this->dispatcher,
[]
);
$this->dispatcher->expects(self::once())
->method('dispatch')
->with(self::isInstanceOf(ImportFailureEvent::class));
self::assertSame([], $manager->getImporterDefinitions());
$manager->import('', $this->statusCallback, $this->errorCallback, null);
}

protected function setUp(): void
{
parent::setUp();

$this->importer = $this->getMock(EntityImporter::class, ['import']);
$this->dispatcher = $this->getMock(EventDispatcherInterface::class);
$this->definition1 = $this->getMock(EntityImporterDefinition::class);
$this->definition2 = $this->getMock(EntityImporterDefinition::class);
$this->statusCallback = $this->getCallable();
Expand Down
47 changes: 47 additions & 0 deletions tests/unit/Events/ImportFailureEventTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

/**
* Copyright © Fastbolt Schraubengroßhandels GmbH.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Fastbolt\EntityImporter\Tests\Unit\Events;

use DateTime;
use Exception;
use Fastbolt\EntityImporter\EntityImporterDefinition;
use Fastbolt\EntityImporter\Events\ImportFailureEvent;
use Fastbolt\TestHelpers\BaseTestCase;
use PHPUnit\Framework\MockObject\MockObject;

/**
* @covers \Fastbolt\EntityImporter\Events\ImportFailureEvent
*/
class ImportFailureEventTest extends BaseTestCase
{
/**
* @var Exception|MockObject
*/
private $exception;

/**
* @var EntityImporterDefinition|MockObject
*/
private $definition;

public function testEvent(): void
{
$event = new ImportFailureEvent($this->definition, $start = new DateTime(), $this->exception);

self::assertSame($this->definition, $event->getDefinition());
self::assertSame($start, $event->getImportStart());
self::assertSame($this->exception, $event->getException());
}

protected function setUp(): void
{
$this->exception = $this->getMock(Exception::class);
$this->definition = $this->getMock(EntityImporterDefinition::class);
}
}
Loading

0 comments on commit 4e7e055

Please sign in to comment.