From c660577c6e842f89c1cf04775fe6f6f167e05e8c Mon Sep 17 00:00:00 2001 From: Bilge Date: Mon, 2 Apr 2018 21:33:29 +0100 Subject: [PATCH] Added provider fetch exception handler to ImportConnector. --- src/Connector/ConnectionContext.php | 33 +++++++++++++++++++ src/Connector/ImportConnector.php | 10 ++++++ test/Integration/Porter/PorterTest.php | 31 +++++++++++++++++ .../Porter/Connector/ImportConnectorTest.php | 16 +++++++++ 4 files changed, 90 insertions(+) diff --git a/src/Connector/ConnectionContext.php b/src/Connector/ConnectionContext.php index 07e5eed..d5e6e0b 100644 --- a/src/Connector/ConnectionContext.php +++ b/src/Connector/ConnectionContext.php @@ -8,8 +8,20 @@ final class ConnectionContext { private $mustCache; + /** + * User-defined exception handler called when a recoverable exception is thrown by Connector::fetch(). + * + * @var callable + */ private $fetchExceptionHandler; + /** + * Provider-defined exception handler called when a recoverable exception is thrown by Connector::fetch(). + * + * @var callable + */ + private $providerFetchExceptionHandler; + private $maxFetchAttempts; public function __construct($mustCache, callable $fetchExceptionHandler, $maxFetchAttempts) @@ -47,9 +59,30 @@ function (\Exception $exception) { throw $exception; } + // Call provider's exception handler, if defined. + if ($this->providerFetchExceptionHandler) { + call_user_func($this->providerFetchExceptionHandler, $exception); + } + // TODO Clone exception handler to avoid persisting state between calls. call_user_func($this->fetchExceptionHandler, $exception); } ); } + + /** + * Sets an exception handler to be called when a recoverable exception is thrown by Connector::fetch(). + * + * This handler is intended to be set by provider resources only and is called before the user-defined handler. + * + * @param callable $providerFetchExceptionHandler Exception handler. + */ + public function setProviderFetchExceptionHandler(callable $providerFetchExceptionHandler) + { + if ($this->providerFetchExceptionHandler !== null) { + throw new \LogicException('Cannot set provider fetch exception handler: already set!'); + } + + $this->providerFetchExceptionHandler = $providerFetchExceptionHandler; + } } diff --git a/src/Connector/ImportConnector.php b/src/Connector/ImportConnector.php index 59b4a7f..144c231 100644 --- a/src/Connector/ImportConnector.php +++ b/src/Connector/ImportConnector.php @@ -39,4 +39,14 @@ public function getWrappedConnector() { return $this->connector; } + + /** + * Sets the exception handler to be called when a recoverable exception is thrown by Connector::fetch(). + * + * @param callable $exceptionHandler Exception handler. + */ + public function setExceptionHandler(callable $exceptionHandler) + { + $this->context->setProviderFetchExceptionHandler($exceptionHandler); + } } diff --git a/test/Integration/Porter/PorterTest.php b/test/Integration/Porter/PorterTest.php index 5fdcb19..a555ca6 100644 --- a/test/Integration/Porter/PorterTest.php +++ b/test/Integration/Porter/PorterTest.php @@ -12,6 +12,7 @@ use ScriptFUSION\Porter\Connector\ConnectionContext; use ScriptFUSION\Porter\Connector\Connector; use ScriptFUSION\Porter\Connector\ConnectorOptions; +use ScriptFUSION\Porter\Connector\ImportConnector; use ScriptFUSION\Porter\Connector\RecoverableConnectorException; use ScriptFUSION\Porter\ImportException; use ScriptFUSION\Porter\Porter; @@ -303,6 +304,36 @@ public function testCustomFetchExceptionHandler() $this->porter->import($this->specification); } + /** + * Tests that when a provider fetch exception handler is specified and the connector throws a recoverable + * exception, the handler is called before the user handler. + */ + public function testCustomProviderFetchExceptionHandler() + { + $this->specification->setFetchExceptionHandler(function () { + throw new \LogicException('This exception must not be thrown!'); + }); + + $this->arrangeConnectorException($connectorException = + new RecoverableConnectorException('This exception is caught by the provider handler.')); + + $this->resource + ->shouldReceive('fetch') + ->andReturnUsing(function (ImportConnector $connector) use ($connectorException) { + $connector->setExceptionHandler(function (\Exception $exception) use ($connectorException) { + self::assertSame($connectorException, $exception); + + throw new \RuntimeException('This exception is thrown by the provider handler.'); + }); + + yield $connector->fetch('foo'); + }) + ; + + $this->setExpectedException(\RuntimeException::class); + $this->porter->importOne($this->specification); + } + #endregion public function testFilter() diff --git a/test/Unit/Porter/Connector/ImportConnectorTest.php b/test/Unit/Porter/Connector/ImportConnectorTest.php index c9eed19..c6f81d3 100644 --- a/test/Unit/Porter/Connector/ImportConnectorTest.php +++ b/test/Unit/Porter/Connector/ImportConnectorTest.php @@ -89,4 +89,20 @@ public function testGetWrappedConnector() self::assertSame($wrappedConnector, $connector->getWrappedConnector()); } + + /** + * Tests that setting the provider exception handler twice produces an exception the second time. + */ + public function testSetExceptionHandlerTwice() + { + $connector = new ImportConnector( + $wrappedConnector = \Mockery::mock(Connector::class), + FixtureFactory::buildConnectionContext() + ); + + $connector->setExceptionHandler([$this, __FUNCTION__]); + + $this->setExpectedException(\LogicException::class); + $connector->setExceptionHandler([$this, __FUNCTION__]); + } }