diff --git a/spec/Api/ProductMediaFileApiSpec.php b/spec/Api/ProductMediaFileApiSpec.php index ca3c2724..46cca3f7 100644 --- a/spec/Api/ProductMediaFileApiSpec.php +++ b/spec/Api/ProductMediaFileApiSpec.php @@ -9,11 +9,13 @@ use Akeneo\Pim\ApiClient\Api\MediaFileApiInterface; use Akeneo\Pim\ApiClient\Client\ResourceClientInterface; use Akeneo\Pim\ApiClient\Exception\RuntimeException; +use Akeneo\Pim\ApiClient\FileSystem\FileSystemInterface; use Akeneo\Pim\ApiClient\Pagination\PageInterface; use Akeneo\Pim\ApiClient\Pagination\PageFactoryInterface; use Akeneo\Pim\ApiClient\Pagination\ResourceCursorFactoryInterface; use Akeneo\Pim\ApiClient\Pagination\ResourceCursorInterface; use PhpSpec\ObjectBehavior; +use Prophecy\Argument; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamInterface; @@ -22,9 +24,10 @@ class ProductMediaFileApiSpec extends ObjectBehavior function let( ResourceClientInterface $resourceClient, PageFactoryInterface $pageFactory, - ResourceCursorFactoryInterface $cursorFactory + ResourceCursorFactoryInterface $cursorFactory, + FileSystemInterface $fileSystem ) { - $this->beConstructedWith($resourceClient, $pageFactory, $cursorFactory); + $this->beConstructedWith($resourceClient, $pageFactory, $cursorFactory, $fileSystem); } function it_is_initializable() @@ -103,9 +106,11 @@ function it_returns_a_list_of_media_files_with_additional_query_parameters($reso $this->listPerPage(null, null, ['foo' => 'bar'])->shouldReturn($page); } - function it_creates_a_media_file($resourceClient, ResponseInterface $response) + function it_creates_a_media_file_from_a_path($resourceClient, $fileSystem, ResponseInterface $response) { $fileResource = fopen('php://stdin', 'r'); + $fileSystem->getResourceFromPath('/images/akeneo.png')->willReturn($fileResource); + $product = [ 'identifier' => 'foo', 'attribute' => 'picture', @@ -132,23 +137,43 @@ function it_creates_a_media_file($resourceClient, ResponseInterface $response) ->createMultipartResource(ProductMediaFileApi::MEDIA_FILES_URI, [], $requestParts) ->willReturn($response); - $this->create($fileResource, $product) + $this->create('/images/akeneo.png', $product) ->shouldReturn('1/e/e/d/1eed10f108bde68b279d6f903f17b4b053e9d89d_akeneo.png'); } - function it_throws_an_exception_if_the_file_is_unreadable_when_creating_a_media_file() + function it_creates_a_media_file_from_a_resource($resourceClient, $fileSystem, ResponseInterface $response) { - $this - ->shouldThrow(new RuntimeException('The file "/foo.bar" could not be read.')) - ->during('create', [ - '/foo.bar', - [ - 'identifier' => 'foo', - 'attribute' => 'picture', - 'scope' => 'e-commerce', - 'locale' => 'en_US', - ] - ]); + $fileResource = fopen('php://stdin', 'r'); + $fileSystem->getResourceFromPath(Argument::any())->shouldNotBeCalled(); + + $product = [ + 'identifier' => 'foo', + 'attribute' => 'picture', + 'scope' => 'e-commerce', + 'locale' => 'en_US', + ]; + + $requestParts = [ + [ + 'name' => 'product', + 'contents' => json_encode($product), + ], + [ + 'name' => 'file', + 'contents' => $fileResource, + ] + ]; + + $response->getHeaders()->willReturn(['Location' => [ + 'http://localhost/api/rest/v1/media-files/1/e/e/d/1eed10f108bde68b279d6f903f17b4b053e9d89d_akeneo.png' + ]]); + + $resourceClient + ->createMultipartResource(ProductMediaFileApi::MEDIA_FILES_URI, [], $requestParts) + ->willReturn($response); + + $this->create($fileResource, $product) + ->shouldReturn('1/e/e/d/1eed10f108bde68b279d6f903f17b4b053e9d89d_akeneo.png'); } function it_throws_an_exception_if_the_response_does_not_contain_the_uri_of_the_created_media_file($resourceClient, ResponseInterface $response) diff --git a/src/AkeneoPimClientBuilder.php b/src/AkeneoPimClientBuilder.php index def3b95a..d18b1bb5 100644 --- a/src/AkeneoPimClientBuilder.php +++ b/src/AkeneoPimClientBuilder.php @@ -20,6 +20,8 @@ use Akeneo\Pim\ApiClient\Client\AuthenticatedHttpClient; use Akeneo\Pim\ApiClient\Client\HttpClient; use Akeneo\Pim\ApiClient\Client\ResourceClient; +use Akeneo\Pim\ApiClient\FileSystem\FileSystemInterface; +use Akeneo\Pim\ApiClient\FileSystem\LocalFileSystem; use Akeneo\Pim\ApiClient\Pagination\PageFactory; use Akeneo\Pim\ApiClient\Pagination\ResourceCursorFactory; use Akeneo\Pim\ApiClient\Routing\UriGenerator; @@ -55,6 +57,9 @@ class AkeneoPimClientBuilder /** @var StreamFactory */ protected $streamFactory; + /** @var FileSystemInterface */ + protected $fileSystem; + /** * @param string $baseUri Base uri to request the API */ @@ -105,6 +110,20 @@ public function setStreamFactory($streamFactory) return $this; } + /** + * Allows to define another implementation than LocalFileSystem + * + * @param FileSystemInterface $fileSystem + * + * @return AkeneoPimClientBuilder + */ + public function setFileSystem($fileSystem) + { + $this->fileSystem = $fileSystem; + + return $this; + } + /** * Build the Akeneo PIM client authenticated by user name and password. * @@ -146,7 +165,7 @@ public function buildAuthenticatedByToken($clientId, $secret, $token, $refreshTo */ protected function buildAuthenticatedClient(Authentication $authentication) { - list($resourceClient, $pageFactory, $cursorFactory) = $this->setUp($authentication); + list($resourceClient, $pageFactory, $cursorFactory, $fileSystem) = $this->setUp($authentication); $client = new AkeneoPimClient( $authentication, @@ -156,7 +175,7 @@ protected function buildAuthenticatedClient(Authentication $authentication) new AttributeOptionApi($resourceClient, $pageFactory, $cursorFactory), new AttributeGroupApi($resourceClient, $pageFactory, $cursorFactory), new FamilyApi($resourceClient, $pageFactory, $cursorFactory), - new ProductMediaFileApi($resourceClient, $pageFactory, $cursorFactory), + new ProductMediaFileApi($resourceClient, $pageFactory, $cursorFactory, $fileSystem), new LocaleApi($resourceClient, $pageFactory, $cursorFactory), new ChannelApi($resourceClient, $pageFactory, $cursorFactory), new CurrencyApi($resourceClient, $pageFactory, $cursorFactory), @@ -193,8 +212,9 @@ protected function setUp(Authentication $authentication) $pageFactory = new PageFactory($authenticatedHttpClient); $cursorFactory = new ResourceCursorFactory(); + $fileSystem = null !== $this->fileSystem ? $this->fileSystem : new LocalFileSystem(); - return [$resourceClient, $pageFactory, $cursorFactory]; + return [$resourceClient, $pageFactory, $cursorFactory, $fileSystem]; } /** diff --git a/src/Api/ProductMediaFileApi.php b/src/Api/ProductMediaFileApi.php index 5888939a..c07c5155 100644 --- a/src/Api/ProductMediaFileApi.php +++ b/src/Api/ProductMediaFileApi.php @@ -4,6 +4,7 @@ use Akeneo\Pim\ApiClient\Client\ResourceClientInterface; use Akeneo\Pim\ApiClient\Exception\RuntimeException; +use Akeneo\Pim\ApiClient\FileSystem\FileSystemInterface; use Akeneo\Pim\ApiClient\Pagination\PageFactoryInterface; use Akeneo\Pim\ApiClient\Pagination\ResourceCursorFactoryInterface; use Psr\Http\Message\ResponseInterface; @@ -31,19 +32,25 @@ class ProductMediaFileApi implements MediaFileApiInterface /** @var ResourceCursorFactoryInterface */ protected $cursorFactory; + /** @var FileSystemInterface */ + private $fileSystem; + /** * @param ResourceClientInterface $resourceClient * @param PageFactoryInterface $pageFactory * @param ResourceCursorFactoryInterface $cursorFactory + * @param FileSystemInterface $fileSystem */ public function __construct( ResourceClientInterface $resourceClient, PageFactoryInterface $pageFactory, - ResourceCursorFactoryInterface $cursorFactory + ResourceCursorFactoryInterface $cursorFactory, + FileSystemInterface $fileSystem ) { $this->resourceClient = $resourceClient; $this->pageFactory = $pageFactory; $this->cursorFactory = $cursorFactory; + $this->fileSystem = $fileSystem; } /** @@ -80,11 +87,7 @@ public function all($pageSize = 10, array $queryParameters = []) public function create($mediaFile, array $productData) { if (is_string($mediaFile)) { - if (!is_readable($mediaFile)) { - throw new RuntimeException(sprintf('The file "%s" could not be read.', $mediaFile)); - } - - $mediaFile = fopen($mediaFile, 'rb'); + $mediaFile = $this->fileSystem->getResourceFromPath($mediaFile); } $requestParts = [ diff --git a/src/Exception/UnreadableFileException.php b/src/Exception/UnreadableFileException.php new file mode 100644 index 00000000..957eb1a7 --- /dev/null +++ b/src/Exception/UnreadableFileException.php @@ -0,0 +1,14 @@ + + * @copyright 2017 Akeneo SAS (http://www.akeneo.com) + * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) + */ +class UnreadableFileException extends RuntimeException +{ +} diff --git a/src/FileSystem/FileSystemInterface.php b/src/FileSystem/FileSystemInterface.php new file mode 100644 index 00000000..60f75e23 --- /dev/null +++ b/src/FileSystem/FileSystemInterface.php @@ -0,0 +1,26 @@ + + * @copyright 2017 Akeneo SAS (http://www.akeneo.com) + * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) + */ +interface FileSystemInterface +{ + /** + * Gets the resource of a file from its path. + * + * @param string $filePath Path of the file + * + * @throws UnreadableFileException if the file doesn't exists or is not readable + * + * @return resource + */ + public function getResourceFromPath($filePath); +} diff --git a/src/FileSystem/LocalFileSystem.php b/src/FileSystem/LocalFileSystem.php new file mode 100644 index 00000000..17ba0a35 --- /dev/null +++ b/src/FileSystem/LocalFileSystem.php @@ -0,0 +1,33 @@ + + * @copyright 2017 Akeneo SAS (http://www.akeneo.com) + * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) + */ +class LocalFileSystem implements FileSystemInterface +{ + /** + * {@inheritdoc} + */ + public function getResourceFromPath($filePath) + { + if (!is_readable($filePath)) { + throw new UnreadableFileException(sprintf('The file "%s" could not be read.', $filePath)); + } + + $fileResource = fopen($filePath, 'rb'); + + if (!is_resource($fileResource)) { + throw new \RuntimeException(sprintf('The file "%s" could not be opened.', $filePath)); + } + + return $fileResource; + } +} diff --git a/tests/Common/Api/ProductMediaFile/CreateProductMediaFileApiIntegration.php b/tests/Common/Api/ProductMediaFile/CreateProductMediaFileApiIntegration.php index 2d4a5afa..812b27eb 100644 --- a/tests/Common/Api/ProductMediaFile/CreateProductMediaFileApiIntegration.php +++ b/tests/Common/Api/ProductMediaFile/CreateProductMediaFileApiIntegration.php @@ -87,6 +87,21 @@ public function testCreateWithAnInvalidRequest() ]); } + /** + * @expectedException \Akeneo\Pim\ApiClient\Exception\UnreadableFileException + */ + public function testCreateWithAnInvalidFile() + { + $api = $this->createClient()->getProductMediaFileApi(); + + $api->create('foo.jpg', [ + 'identifier' => 'medium_boot', + 'attribute' => 'side_view', + 'scope' => null, + 'locale' => null, + ]); + } + /** * Sanitize the code and links of a media file, because the code is generated randomly. *