diff --git a/Async/ResolveCacheProcessor.php b/Async/ResolveCacheProcessor.php index 933dbf51b..a882c4db2 100644 --- a/Async/ResolveCacheProcessor.php +++ b/Async/ResolveCacheProcessor.php @@ -19,26 +19,20 @@ use Interop\Queue\PsrMessage; use Interop\Queue\PsrProcessor; use Enqueue\Util\JSON; -use Liip\ImagineBundle\Imagine\Cache\CacheManager; -use Liip\ImagineBundle\Imagine\Data\DataManager; use Liip\ImagineBundle\Imagine\Filter\FilterManager; +use Liip\ImagineBundle\Service\FilterService; final class ResolveCacheProcessor implements PsrProcessor, CommandSubscriberInterface, QueueSubscriberInterface { - /** - * @var CacheManager - */ - private $cacheManager; - /** * @var FilterManager */ private $filterManager; /** - * @var DataManager + * @var FilterService */ - private $dataManager; + private $filterService; /** * @var ProducerInterface @@ -46,20 +40,17 @@ final class ResolveCacheProcessor implements PsrProcessor, CommandSubscriberInte private $producer; /** - * @param CacheManager $cacheManager * @param FilterManager $filterManager - * @param DataManager $dataManager + * @param FilterService $filterService * @param ProducerInterface $producer */ public function __construct( - CacheManager $cacheManager, FilterManager $filterManager, - DataManager $dataManager, + FilterService $filterService, ProducerInterface $producer ) { - $this->cacheManager = $cacheManager; $this->filterManager = $filterManager; - $this->dataManager = $dataManager; + $this->filterService = $filterService; $this->producer = $producer; } @@ -75,20 +66,11 @@ public function process(PsrMessage $psrMessage, PsrContext $psrContext) $path = $message->getPath(); $results = []; foreach ($filters as $filter) { - if ($this->cacheManager->isStored($path, $filter) && $message->isForce()) { - $this->cacheManager->remove($path, $filter); + if ($message->isForce()) { + $this->filterService->bustCache($path, $filter); } - if (false == $this->cacheManager->isStored($path, $filter)) { - $binary = $this->dataManager->find($filter, $path); - $this->cacheManager->store( - $this->filterManager->applyFilter($binary, $filter), - $path, - $filter - ); - } - - $results[$filter] = $this->cacheManager->resolve($path, $filter); + $results[$filter] = $this->filterService->getUrlOfFilteredImage($path, $filter); } $this->producer->sendEvent(Topics::CACHE_RESOLVED, new CacheResolved($path, $results)); @@ -97,7 +79,6 @@ public function process(PsrMessage $psrMessage, PsrContext $psrContext) 'status' => true, 'results' => $results, ]))); - } catch (\Exception $e) { return Result::reply($psrContext->createMessage(JSON::encode([ 'status' => false, diff --git a/Command/ResolveCacheCommand.php b/Command/ResolveCacheCommand.php index 8aa69a8ba..ae519dd57 100644 --- a/Command/ResolveCacheCommand.php +++ b/Command/ResolveCacheCommand.php @@ -11,9 +11,8 @@ namespace Liip\ImagineBundle\Command; -use Liip\ImagineBundle\Imagine\Cache\CacheManager; -use Liip\ImagineBundle\Imagine\Data\DataManager; use Liip\ImagineBundle\Imagine\Filter\FilterManager; +use Liip\ImagineBundle\Service\FilterService; use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -63,12 +62,11 @@ protected function execute(InputInterface $input, OutputInterface $output) $paths = $input->getArgument('paths'); $filters = $input->getOption('filters'); - /* @var FilterManager filterManager */ + /* @var FilterManager $filterManager */ $filterManager = $this->getContainer()->get('liip_imagine.filter.manager'); - /* @var CacheManager cacheManager */ - $cacheManager = $this->getContainer()->get('liip_imagine.cache.manager'); - /* @var DataManager dataManager */ - $dataManager = $this->getContainer()->get('liip_imagine.data.manager'); + + /* @var FilterService $filterService */ + $filterService = $this->getContainer()->get('liip_imagine.service.filter'); if (empty($filters)) { $filters = array_keys($filterManager->getFilterConfiguration()->all()); @@ -76,17 +74,7 @@ protected function execute(InputInterface $input, OutputInterface $output) foreach ($paths as $path) { foreach ($filters as $filter) { - if (!$cacheManager->isStored($path, $filter)) { - $binary = $dataManager->find($filter, $path); - - $cacheManager->store( - $filterManager->applyFilter($binary, $filter), - $path, - $filter - ); - } - - $output->writeln($cacheManager->resolve($path, $filter)); + $output->writeln($filterService->getUrlOfFilteredImage($path, $filter)); } } } diff --git a/Controller/ImagineController.php b/Controller/ImagineController.php index 0a74a33be..ce47f7d73 100644 --- a/Controller/ImagineController.php +++ b/Controller/ImagineController.php @@ -14,11 +14,9 @@ use Imagine\Exception\RuntimeException; use Liip\ImagineBundle\Exception\Binary\Loader\NotLoadableException; use Liip\ImagineBundle\Exception\Imagine\Filter\NonExistingFilterException; -use Liip\ImagineBundle\Imagine\Cache\CacheManager; use Liip\ImagineBundle\Imagine\Cache\SignerInterface; use Liip\ImagineBundle\Imagine\Data\DataManager; -use Liip\ImagineBundle\Imagine\Filter\FilterManager; -use Psr\Log\LoggerInterface; +use Liip\ImagineBundle\Service\FilterService; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; @@ -27,165 +25,114 @@ class ImagineController { /** - * @var DataManager - */ - protected $dataManager; - - /** - * @var FilterManager + * @var FilterService */ - protected $filterManager; + private $filterService; /** - * @var CacheManager + * @var DataManager */ - protected $cacheManager; + private $dataManager; /** * @var SignerInterface */ - protected $signer; - - /** - * @var LoggerInterface - */ - protected $logger; + private $signer; /** + * @param FilterService $filterService * @param DataManager $dataManager - * @param FilterManager $filterManager - * @param CacheManager $cacheManager * @param SignerInterface $signer */ - public function __construct( - DataManager $dataManager, - FilterManager $filterManager, - CacheManager $cacheManager, - SignerInterface $signer, - LoggerInterface $logger = null - ) { + public function __construct(FilterService $filterService, DataManager $dataManager, SignerInterface $signer) + { + $this->filterService = $filterService; $this->dataManager = $dataManager; - $this->filterManager = $filterManager; - $this->cacheManager = $cacheManager; $this->signer = $signer; - $this->logger = $logger; } /** - * This action applies a given filter to a given image, optionally saves the image and outputs it to the browser at the same time. + * This action applies a given filter to a given image, saves the image and redirects the browser to the stored + * image. + * + * The resulting image is cached so subsequent requests will redirect to the cached image instead applying the + * filter and storing the image again. * * @param Request $request * @param string $path * @param string $filter * - * @throws \RuntimeException - * @throws BadRequestHttpException + * @throws RuntimeException + * @throws NotFoundHttpException * * @return RedirectResponse */ public function filterAction(Request $request, $path, $filter) { - // decoding special characters and whitespaces from path obtained from url $path = urldecode($path); $resolver = $request->get('resolver'); try { - if (!$this->cacheManager->isStored($path, $filter, $resolver)) { - try { - $binary = $this->dataManager->find($filter, $path); - } catch (NotLoadableException $e) { - if ($defaultImageUrl = $this->dataManager->getDefaultImageUrl($filter)) { - return new RedirectResponse($defaultImageUrl); - } - - throw new NotFoundHttpException('Source image could not be found', $e); - } - - $this->cacheManager->store( - $this->filterManager->applyFilter($binary, $filter), - $path, - $filter, - $resolver - ); + return new RedirectResponse($this->filterService->getUrlOfFilteredImage($path, $filter, $resolver), 301); + } catch (NotLoadableException $e) { + if ($this->dataManager->getDefaultImageUrl($filter) !== null) { + return new RedirectResponse($this->dataManager->getDefaultImageUrl($filter)); } - return new RedirectResponse($this->cacheManager->resolve($path, $filter, $resolver), 301); + throw new NotFoundHttpException(sprintf('Source image for path "%s" could not be found', $path)); } catch (NonExistingFilterException $e) { - $message = sprintf('Could not locate filter "%s" for path "%s". Message was "%s"', $filter, $path, $e->getMessage()); - - if (null !== $this->logger) { - $this->logger->debug($message); - } - - throw new NotFoundHttpException($message, $e); + throw new NotFoundHttpException(sprintf('Requested non-existing filter "%s"', $filter)); } catch (RuntimeException $e) { throw new \RuntimeException(sprintf('Unable to create image for path "%s" and filter "%s". Message was "%s"', $path, $filter, $e->getMessage()), 0, $e); } } /** - * This action applies a given filter to a given image, optionally saves the image and outputs it to the browser at the same time. + * This action applies a given filter -merged with additional runtime filters- to a given image, saves the image and + * redirects the browser to the stored image. + * + * The resulting image is cached so subsequent requests will redirect to the cached image instead applying the + * filter and storing the image again. * * @param Request $request * @param string $hash * @param string $path * @param string $filter * - * @throws \RuntimeException + * @throws RuntimeException * @throws BadRequestHttpException + * @throws NotFoundHttpException * * @return RedirectResponse */ public function filterRuntimeAction(Request $request, $hash, $path, $filter) { $resolver = $request->get('resolver'); + $runtimeConfig = $request->query->get('filters', []); - try { - $filters = $request->query->get('filters', []); - - if (!is_array($filters)) { - throw new NotFoundHttpException(sprintf('Filters must be an array. Value was "%s"', $filters)); - } - - if (true !== $this->signer->check($hash, $path, $filters)) { - throw new BadRequestHttpException(sprintf( - 'Signed url does not pass the sign check for path "%s" and filter "%s" and runtime config %s', - $path, - $filter, - json_encode($filters) - )); - } - - try { - $binary = $this->dataManager->find($filter, $path); - } catch (NotLoadableException $e) { - if ($defaultImageUrl = $this->dataManager->getDefaultImageUrl($filter)) { - return new RedirectResponse($defaultImageUrl); - } - - throw new NotFoundHttpException(sprintf('Source image could not be found for path "%s" and filter "%s"', $path, $filter), $e); - } - - $rcPath = $this->cacheManager->getRuntimePath($path, $filters); + if (!is_array($runtimeConfig)) { + throw new NotFoundHttpException(sprintf('Filters must be an array. Value was "%s"', $runtimeConfig)); + } - $this->cacheManager->store( - $this->filterManager->applyFilter($binary, $filter, [ - 'filters' => $filters, - ]), - $rcPath, + if (true !== $this->signer->check($hash, $path, $runtimeConfig)) { + throw new BadRequestHttpException(sprintf( + 'Signed url does not pass the sign check for path "%s" and filter "%s" and runtime config %s', + $path, $filter, - $resolver - ); - - return new RedirectResponse($this->cacheManager->resolve($rcPath, $filter, $resolver), 301); - } catch (NonExistingFilterException $e) { - $message = sprintf('Could not locate filter "%s" for path "%s". Message was "%s"', $filter, $hash.'/'.$path, $e->getMessage()); + json_encode($runtimeConfig) + )); + } - if (null !== $this->logger) { - $this->logger->debug($message); + try { + return new RedirectResponse($this->filterService->getUrlOfFilteredImageWithRuntimeFilters($path, $filter, $runtimeConfig, $resolver), 301); + } catch (NotLoadableException $e) { + if ($this->dataManager->getDefaultImageUrl($filter) !== null) { + return new RedirectResponse($this->dataManager->getDefaultImageUrl($filter)); } - throw new NotFoundHttpException($message, $e); + throw new NotFoundHttpException(sprintf('Source image for path "%s" could not be found', $path)); + } catch (NonExistingFilterException $e) { + throw new NotFoundHttpException(sprintf('Requested non-existing filter "%s"', $filter)); } catch (RuntimeException $e) { throw new \RuntimeException(sprintf('Unable to create image for path "%s" and filter "%s". Message was "%s"', $hash.'/'.$path, $filter, $e->getMessage()), 0, $e); } diff --git a/Imagine/Data/DataManager.php b/Imagine/Data/DataManager.php index 90a12fa00..889b1d7d8 100644 --- a/Imagine/Data/DataManager.php +++ b/Imagine/Data/DataManager.php @@ -149,7 +149,7 @@ public function find($filter, $path) * * @param string $filter * - * @return string + * @return string|null */ public function getDefaultImageUrl($filter) { diff --git a/README.md b/README.md index fa938027d..164e3fd96 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,9 @@ |:----------------------:|:-----------------------:|:-----------------------:|:-----------------------:|:-----------------------:| | [![Travis](https://src.run/shield/liip/LiipImagineBundle/2.0/travis.svg)](https://src.run/service/liip/LiipImagineBundle/2.0/travis) | [![Style CI](https://src.run/shield/liip/LiipImagineBundle/2.0/styleci.svg)](https://src.run/service/liip/LiipImagineBundle/2.0/styleci) | [![Coverage](https://src.run/shield/liip/LiipImagineBundle/2.0/coveralls.svg)](https://src.run/service/liip/LiipImagineBundle/2.0/coveralls) | [![Downloads](https://src.run/shield/liip/LiipImagineBundle/packagist_dt.svg)](https://src.run/service/liip/LiipImagineBundle/packagist) | [![Latest Stable Version](https://src.run/shield/liip/LiipImagineBundle/packagist_v.svg)](https://src.run/service/liip/LiipImagineBundle/packagist) | -*This bundle provides an image manipulation abstraction toolkit for [Symfony](http://symfony.com/)-based projects.* - +*This bundle provides an image manipulation abstraction toolkit for +[Symfony](http://symfony.com/)-based projects.* + ## Overview - [Filter Sets](http://symfony.com/doc/master/bundles/LiipImagineBundle/basic-usage.html): @@ -312,8 +313,8 @@ from our documentation. ## Use as a Service If you need to use your defined "filter sets" from within your controller, you -can fetch this bundle's controller from the service container and handle -the response yourself. +can fetch this bundle's FilterService from the service container to do the heavy +lifting for you. ```php container - ->get('liip_imagine.controller'); - - /** @var RedirectResponse */ - $imagemanagerResponse = $imagine - ->filterAction( - $this->request, // http request - 'uploads/foo.jpg', // original image you want to apply a filter to - 'my_thumb' // filter defined in config.yml - ); - - /** @var CacheManager */ - $cacheManager = $this - ->container - ->get('liip_imagine.cache.manager'); - - /** @var string */ - $sourcePath = $cacheManager - ->getBrowserPath( - 'uploads/foo.jpg', - 'my_thumb' - ); + ->get('liip_imagine.service.filter'); + + // 1) Simple filter, OR + $resourcePath = $imagine->getUrlOfFilteredImage('uploads/foo.jpg', 'my_thumb'); + + // 2) Runtime configuration + $runtimeConfig = [ + 'thumbnail' => [ + 'size' => [200, 200] + ], + ]; + $resourcePath = $imagine->getUrlOfFilteredImageWithRuntimeFilters( + 'uploads/foo.jpg', + 'my_thumb', + $runtimeConfig + ); // .. } @@ -354,32 +350,6 @@ class MyController extends Controller ?> ``` -If you need to add more logic, the recommended solution is to either -extend `ImagineController.php` or use it as the basis for your own -implementation. - -To use the controller in another service, you have to simulate a new request. - -```php -container - ->get('liip_imagine.controller'); - -/** @var Response */ -$response = $imagine - ->filterAction( - new Symfony\Component\HttpFoundation\Request(), - 'uploads/foo.jpg', - 'my_thumb' - ); - -?> -``` - - ## Data Roots By default, Symfony's `web/` directory is registered as a data root to load diff --git a/Resources/config/enqueue.xml b/Resources/config/enqueue.xml index fc5e04f0a..82c972b2f 100644 --- a/Resources/config/enqueue.xml +++ b/Resources/config/enqueue.xml @@ -5,9 +5,8 @@ - - + diff --git a/Resources/config/imagine.xml b/Resources/config/imagine.xml index 292a507f6..de60af7f9 100644 --- a/Resources/config/imagine.xml +++ b/Resources/config/imagine.xml @@ -60,14 +60,19 @@ %liip_imagine.filter_sets% + + + + + + + + - - - diff --git a/Service/FilterService.php b/Service/FilterService.php new file mode 100644 index 000000000..f58c65b84 --- /dev/null +++ b/Service/FilterService.php @@ -0,0 +1,160 @@ +dataManager = $dataManager; + $this->filterManager = $filterManager; + $this->cacheManager = $cacheManager; + $this->logger = $logger ?: new NullLogger(); + } + + /** + * @param string $path + * @param string $filter + */ + public function bustCache($path, $filter) + { + if (!$this->cacheManager->isStored($path, $filter)) { + return; + } + + $this->cacheManager->remove($path, $filter); + } + + /** + * @param string $path + * @param string $filter + * @param string $resolver + * + * @return string + */ + public function getUrlOfFilteredImage($path, $filter, $resolver = null) + { + if ($this->cacheManager->isStored($path, $filter, $resolver)) { + return $this->cacheManager->resolve($path, $filter, $resolver); + } + + $filteredBinary = $this->createFilteredBinary( + $path, + $filter + ); + + $this->cacheManager->store( + $filteredBinary, + $path, + $filter, + $resolver + ); + + return $this->cacheManager->resolve($path, $filter, $resolver); + } + + /** + * @param string $path + * @param string $filter + * @param array $runtimeFilters + * @param string|null $resolver + * + * @return string + */ + public function getUrlOfFilteredImageWithRuntimeFilters($path, $filter, array $runtimeFilters = [], $resolver = null) + { + $runtimePath = $this->cacheManager->getRuntimePath($path, $runtimeFilters); + if ($this->cacheManager->isStored($runtimePath, $filter, $resolver)) { + return $this->cacheManager->resolve($runtimePath, $filter, $resolver); + } + + $filteredBinary = $this->createFilteredBinary( + $path, + $filter, + $runtimeFilters + ); + + $this->cacheManager->store( + $filteredBinary, + $runtimePath, + $filter, + $resolver + ); + + return $this->cacheManager->resolve($runtimePath, $filter, $resolver); + } + + /** + * @param string $path + * @param string $filter + * @param array $runtimeFilters + * + * @throws NonExistingFilterException + + * @return BinaryInterface + */ + private function createFilteredBinary($path, $filter, array $runtimeFilters = []) + { + $binary = $this->dataManager->find($filter, $path); + + try { + return $this->filterManager->applyFilter($binary, $filter, [ + 'filters' => $runtimeFilters, + ]); + } catch (NonExistingFilterException $e) { + $message = sprintf('Could not locate filter "%s" for path "%s". Message was "%s"', $filter, $path, $e->getMessage()); + + $this->logger->debug($message); + + throw $e; + } + } +} diff --git a/Tests/AbstractTest.php b/Tests/AbstractTest.php index 421672005..f8bd34d29 100644 --- a/Tests/AbstractTest.php +++ b/Tests/AbstractTest.php @@ -23,6 +23,7 @@ use Liip\ImagineBundle\Imagine\Filter\FilterConfiguration; use Liip\ImagineBundle\Imagine\Filter\FilterManager; use Liip\ImagineBundle\Imagine\Filter\PostProcessor\PostProcessorInterface; +use Liip\ImagineBundle\Service\FilterService; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Filesystem\Filesystem; @@ -213,6 +214,14 @@ protected function createFilterManagerMock() return $this->createObjectMock(FilterManager::class, array(), false); } + /** + * @return \PHPUnit_Framework_MockObject_MockObject|FilterService + */ + protected function createFilterServiceMock() + { + return $this->createObjectMock('\Liip\ImagineBundle\Service\FilterService'); + } + /** * @return \PHPUnit_Framework_MockObject_MockObject|DataManager */ diff --git a/Tests/Async/ResolveCacheProcessorTest.php b/Tests/Async/ResolveCacheProcessorTest.php index 9cebb31aa..e559f7059 100644 --- a/Tests/Async/ResolveCacheProcessorTest.php +++ b/Tests/Async/ResolveCacheProcessorTest.php @@ -13,6 +13,7 @@ use Liip\ImagineBundle\Async\Topics; use Liip\ImagineBundle\Imagine\Filter\FilterConfiguration; use Liip\ImagineBundle\Model\Binary; +use Liip\ImagineBundle\Service\FilterService; use Liip\ImagineBundle\Tests\AbstractTest; class ResolveCacheProcessorTest extends AbstractTest @@ -69,9 +70,8 @@ public function testShouldSubscribeToExpectedQueue() public function testCouldBeConstructedWithExpectedArguments() { new ResolveCacheProcessor( - $this->createCacheManagerMock(), $this->createFilterManagerMock(), - $this->createDataManagerMock(), + $this->createFilterServiceMock(), $this->createProducerMock() ); } @@ -79,9 +79,8 @@ public function testCouldBeConstructedWithExpectedArguments() public function testShouldRejectMessagesWithInvalidJsonBody() { $processor = new ResolveCacheProcessor( - $this->createCacheManagerMock(), $this->createFilterManagerMock(), - $this->createDataManagerMock(), + $this->createFilterServiceMock(), $this->createProducerMock() ); @@ -98,9 +97,8 @@ public function testShouldRejectMessagesWithInvalidJsonBody() public function testShouldSendFailedReplyOnException() { $processor = new ResolveCacheProcessor( - $this->createCacheManagerMock(), $this->createFilterManagerMock(), - $this->createDataManagerMock(), + $this->createFilterServiceMock(), $this->createProducerMock() ); @@ -123,9 +121,8 @@ public function testShouldSendFailedReplyOnException() public function testShouldRejectMessagesWithoutPass() { $processor = new ResolveCacheProcessor( - $this->createCacheManagerMock(), $this->createFilterManagerMock(), - $this->createDataManagerMock(), + $this->createFilterServiceMock(), $this->createProducerMock() ); @@ -139,64 +136,171 @@ public function testShouldRejectMessagesWithoutPass() $this->assertEquals('The message does not contain "path" but it is required.', $result->getReason()); } - public function testShouldResolveCacheIfNotStored() + public function testShouldCreateFilteredImage() { - $originalBinary = $this->createDummyBinary(); - $filteredBinary = $this->createDummyBinary(); + $filterName = 'fooFilter'; + $imagePath = 'theImagePath'; $filterManagerMock = $this->createFilterManagerMock(); $filterManagerMock ->expects($this->once()) ->method('getFilterConfiguration') ->willReturn(new FilterConfiguration(array( - 'fooFilter' => array('fooFilterConfig'), + $filterName => array('fooFilterConfig'), ))) ; + + $filterServiceMock = $this->createFilterServiceMock(); + $filterServiceMock + ->expects($this->once()) + ->method('getUrlOfFilteredImage') + ->with($imagePath, $filterName); + + $processor = new ResolveCacheProcessor( + $filterManagerMock, + $filterServiceMock, + $this->createProducerMock() + ); + + $message = new NullMessage(); + $message->setBody(json_encode(['path' => $imagePath])); + + $result = $processor->process($message, new NullContext()); + + $this->assertEquals(Result::ACK, $result); + } + + public function testShouldCreateOneImagePerFilter() + { + $filterName1 = 'fooFilter'; + $filterName2 = 'barFilter'; + $imagePath = 'theImagePath'; + + $filterManagerMock = $this->createFilterManagerMock(); $filterManagerMock ->expects($this->once()) - ->method('applyFilter') - ->with($this->identicalTo($originalBinary), 'fooFilter') - ->willReturn($filteredBinary) + ->method('getFilterConfiguration') + ->willReturn(new FilterConfiguration(array( + $filterName1 => array('fooFilterConfig'), + $filterName2 => array('fooFilterConfig'), + ))) ; - $cacheManagerMock = $this->createCacheManagerMock(); - $cacheManagerMock - ->expects($this->atLeastOnce()) - ->method('isStored') - ->willReturn(false) - ; - $cacheManagerMock + $filterServiceMock = $this->createFilterServiceMock(); + $filterServiceMock + ->expects($this->exactly(2)) + ->method('getUrlOfFilteredImage') + ->withConsecutive( + [$imagePath, $filterName1], + [$imagePath, $filterName2] + ); + + $processor = new ResolveCacheProcessor( + $filterManagerMock, + $filterServiceMock, + $this->createProducerMock() + ); + + $message = new NullMessage(); + $message->setBody(json_encode(['path' => $imagePath])); + + $result = $processor->process($message, new NullContext()); + + $this->assertEquals(Result::ACK, $result); + } + + public function testShouldOnlyCreateImageForRequestedFilter() + { + $relevantFilter = 'fooFilter'; + $imagePath = 'theImagePath'; + + $filterManagerMock = $this->createFilterManagerMock(); + $filterManagerMock + ->expects($this->never()) + ->method('getFilterConfiguration'); + + $filterServiceMock = $this->createFilterServiceMock(); + $filterServiceMock ->expects($this->once()) - ->method('store') - ->with( - $this->identicalTo($filteredBinary), - 'theImagePath', - 'fooFilter' - ) - ; - $cacheManagerMock + ->method('getUrlOfFilteredImage') + ->with($imagePath, $relevantFilter); + + $processor = new ResolveCacheProcessor( + $filterManagerMock, + $filterServiceMock, + $this->createProducerMock() + ); + + $message = new NullMessage(); + $message->setBody(json_encode(['path' => $imagePath, 'filters' => [$relevantFilter]])); + + $result = $processor->process($message, new NullContext()); + + $this->assertEquals(Result::ACK, $result); + } + + public function testShouldCreateOneImagePerRequestedFilter() + { + $relevantFilter1 = 'fooFilter'; + $relevantFilter2 = 'fooFilter'; + $imagePath = 'theImagePath'; + + $filterManagerMock = $this->createFilterManagerMock(); + $filterManagerMock + ->expects($this->never()) + ->method('getFilterConfiguration'); + + $filterServiceMock = $this->createFilterServiceMock(); + $filterServiceMock + ->expects($this->exactly(2)) + ->method('getUrlOfFilteredImage') + ->withConsecutive( + [$imagePath, $relevantFilter1], + [$imagePath, $relevantFilter2] + ); + + $processor = new ResolveCacheProcessor( + $filterManagerMock, + $filterServiceMock, + $this->createProducerMock() + ); + + $message = new NullMessage(); + $message->setBody(json_encode(['path' => $imagePath, 'filters' => [$relevantFilter1, $relevantFilter2]])); + + $result = $processor->process($message, new NullContext()); + + $this->assertEquals(Result::ACK, $result); + } + + public function testShouldBurstCacheWhenResolvingForced() + { + $filterName = 'fooFilter'; + $imagePath = 'theImagePath'; + + $filterManagerMock = $this->createFilterManagerMock(); + $filterManagerMock ->expects($this->once()) - ->method('resolve') - ->with('theImagePath', 'fooFilter') + ->method('getFilterConfiguration') + ->willReturn(new FilterConfiguration(array( + $filterName => array('fooFilterConfig'), + ))) ; - $dataManagerMock = $this->createDataManagerMock(); - $dataManagerMock + $filterServiceMock = $this->createFilterServiceMock(); + $filterServiceMock ->expects($this->once()) - ->method('find') - ->with('fooFilter', 'theImagePath') - ->willReturn($originalBinary) - ; + ->method('bustCache') + ->with($imagePath, $filterName); $processor = new ResolveCacheProcessor( - $cacheManagerMock, $filterManagerMock, - $dataManagerMock, + $filterServiceMock, $this->createProducerMock() ); $message = new NullMessage(); - $message->setBody('{"path": "theImagePath"}'); + $message->setBody(json_encode(['path' => $imagePath, 'force' => true])); $result = $processor->process($message, new NullContext()); @@ -204,7 +308,7 @@ public function testShouldResolveCacheIfNotStored() $this->assertEquals(Result::ACK, (string) $result); } - public function testShouldNotResolveCacheIfStoredAndNotForce() + public function testShouldNotBurstCacheWhenResolvingNotForced() { $filterManagerMock = $this->createFilterManagerMock(); $filterManagerMock @@ -214,43 +318,20 @@ public function testShouldNotResolveCacheIfStoredAndNotForce() 'fooFilter' => array('fooFilterConfig'), ))) ; - $filterManagerMock - ->expects($this->never()) - ->method('applyFilter') - ; - $cacheManagerMock = $this->createCacheManagerMock(); - $cacheManagerMock - ->expects($this->atLeastOnce()) - ->method('isStored') - ->willReturn(true) - ; - $cacheManagerMock + $filterServiceMock = $this->createFilterServiceMock(); + $filterServiceMock ->expects($this->never()) - ->method('store') - ; - $cacheManagerMock - ->expects($this->once()) - ->method('resolve') - ->with('theImagePath', 'fooFilter') - ->willReturn('fooFilterUri') - ; - - $dataManagerMock = $this->createDataManagerMock(); - $dataManagerMock - ->expects($this->never()) - ->method('find') - ; + ->method('bustCache'); $processor = new ResolveCacheProcessor( - $cacheManagerMock, $filterManagerMock, - $dataManagerMock, + $filterServiceMock, $this->createProducerMock() ); $message = new NullMessage(); - $message->setBody('{"path": "theImagePath"}'); + $message->setBody(json_encode(['path' => 'theImagePath'])); $result = $processor->process($message, new NullContext()); @@ -270,46 +351,23 @@ public function testShouldSendMessageOnSuccessResolve() 'bazFilter' => array('bazFilterConfig'), ))) ; - $filterManagerMock - ->expects($this->atLeastOnce()) - ->method('applyFilter') - ->willReturn($this->createDummyBinary()) - ; - $cacheManagerMock = $this->createCacheManagerMock(); - $cacheManagerMock - ->expects($this->atLeastOnce()) - ->method('isStored') - ->willReturn(false) - ; - $cacheManagerMock - ->expects($this->atLeastOnce()) - ->method('store') - ; - $cacheManagerMock - ->expects($this->atLeastOnce()) - ->method('resolve') - ->willReturnCallback(function ($path, $filter) { + $filterServiceMock = $this->createFilterServiceMock(); + $filterServiceMock + ->expects($this->exactly(3)) + ->method('getUrlOfFilteredImage') + ->willReturnCallback(function($path, $filter) { return $path.$filter.'Uri'; - }) - ; + }); - $dataManagerMock = $this->createDataManagerMock(); - $dataManagerMock - ->expects($this->atLeastOnce()) - ->method('find') - ->willReturn($this->createDummyBinary()) - ; - - $testCase = $this; $producerMock = $this->createProducerMock(); $producerMock ->expects($this->once()) ->method('sendEvent') ->with(Topics::CACHE_RESOLVED, $this->isInstanceOf('Liip\ImagineBundle\Async\CacheResolved')) - ->willReturnCallback(function ($topic, CacheResolved $message) use ($testCase) { - $testCase->assertEquals('theImagePath', $message->getPath()); - $testCase->assertEquals(array( + ->willReturnCallback(function ($topic, CacheResolved $message) { + $this->assertEquals('theImagePath', $message->getPath()); + $this->assertEquals(array( 'fooFilter' => 'theImagePathfooFilterUri', 'barFilter' => 'theImagePathbarFilterUri', 'bazFilter' => 'theImagePathbazFilterUri', @@ -317,14 +375,13 @@ public function testShouldSendMessageOnSuccessResolve() }); $processor = new ResolveCacheProcessor( - $cacheManagerMock, $filterManagerMock, - $dataManagerMock, + $filterServiceMock, $producerMock ); $message = new NullMessage(); - $message->setBody('{"path": "theImagePath"}'); + $message->setBody(json_encode(['path' => 'theImagePath'])); $result = $processor->process($message, new NullContext()); @@ -375,10 +432,15 @@ public function testShouldReturnReplyOnSuccessResolve() ->willReturn($this->createDummyBinary()) ; + $filterService = new FilterService( + $dataManagerMock, + $filterManagerMock, + $cacheManagerMock + ); + $processor = new ResolveCacheProcessor( - $cacheManagerMock, $filterManagerMock, - $dataManagerMock, + $filterService, $this->createProducerMock() ); diff --git a/Tests/Controller/ImagineControllerTest.php b/Tests/Controller/ImagineControllerTest.php index d93da8486..e637d57ee 100644 --- a/Tests/Controller/ImagineControllerTest.php +++ b/Tests/Controller/ImagineControllerTest.php @@ -22,11 +22,9 @@ class ImagineControllerTest extends AbstractTest public function testConstruction() { new ImagineController( + $this->createFilterServiceMock(), $this->createDataManagerMock(), - $this->createFilterManagerMock(), - $this->createCacheManagerMock(), - $this->createSignerInterfaceMock(), - $this->createLoggerInterfaceMock() + $this->createSignerInterfaceMock() ); } } diff --git a/Tests/DependencyInjection/LiipImagineExtensionTest.php b/Tests/DependencyInjection/LiipImagineExtensionTest.php index 1a77b3ba2..a370203ed 100644 --- a/Tests/DependencyInjection/LiipImagineExtensionTest.php +++ b/Tests/DependencyInjection/LiipImagineExtensionTest.php @@ -53,11 +53,9 @@ public function testLoadWithDefaults() $this->assertDICConstructorArguments( $this->containerBuilder->getDefinition('liip_imagine.controller'), array( + new Reference('liip_imagine.service.filter'), new Reference('liip_imagine.data.manager'), - new Reference('liip_imagine.filter.manager'), - new Reference('liip_imagine.cache.manager'), new Reference('liip_imagine.cache.signer'), - new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE), ) ); } diff --git a/Tests/Functional/Controller/ImagineControllerTest.php b/Tests/Functional/Controller/ImagineControllerTest.php index 7b7c6426c..4e3f43ea0 100644 --- a/Tests/Functional/Controller/ImagineControllerTest.php +++ b/Tests/Functional/Controller/ImagineControllerTest.php @@ -88,7 +88,7 @@ public function testShouldThrowNotFoundHttpExceptionIfFiltersNotArray() /** * @expectedException \Symfony\Component\HttpKernel\Exception\NotFoundHttpException - * @expectedExceptionMessage Source image could not be found + * @expectedExceptionMessage Source image for path "images/shrodinger_cats_which_not_exist.jpeg" could not be found */ public function testShouldThrowNotFoundHttpExceptionIfFileNotExists() {