diff --git a/.ci/Jenkinsfile b/.ci/Jenkinsfile index 6ef060d3..1f43117a 100644 --- a/.ci/Jenkinsfile +++ b/.ci/Jenkinsfile @@ -1,11 +1,15 @@ #!groovy import org.csanchez.jenkins.plugins.kubernetes.pipeline.PodTemplateAction +import groovy.transform.Field String launchUnitTests = "yes" String launchIntegrationTests = "yes" -String[] pimVersions = ["1.7", "2.0"] +String[] pimVersions = ["1.7", "2.0", "2.1"] String[] supportedPhpVersions = ["5.6", "7.0", "7.1"] +@Field def String verboseOutputs = "yes" +@Field def String dotsPerLine = "50" + def clientConfig = [ "php-http/guzzle6-adapter": ["phpVersion": supportedPhpVersions, "psrImplem": ["guzzlehttp/psr7"]], "php-http/guzzle5-adapter": ["phpVersion": supportedPhpVersions, "psrImplem": ["guzzlehttp/psr7", "zendframework/zend-diactoros", "slim/slim"]], @@ -16,7 +20,6 @@ imageRepo = "eu.gcr.io/akeneo-ci/php-api-client" imageTag = "pull-request-${env.CHANGE_ID}-build-${env.BUILD_NUMBER}" gcrImages = [] - def clients = clientConfig.keySet() as String[] def buildResult= 'SUCCESS' @@ -28,13 +31,19 @@ try { string(defaultValue: pimVersions.join(','), description: 'PIM edition the tests should run on', name: 'requiredPimVersions'), choice(choices: 'yes\nno', description: 'Run unit tests and code style checks', name: 'launchUnitTests'), choice(choices: 'yes\nno', description: 'Run integration tests', name: 'launchIntegrationTests'), + choice(choices: 'no\nyes', description: 'Enable Verbose mode', name: 'verboseOutputs'), + string(defaultValue: '50', description: 'Number of dots per line', name: 'dotsperline'), string(defaultValue: clients.join(','), description: 'Clients used to run integration tests (comma separated values)', name: 'clients'), + choice(choices: 'no\nyes', description: 'Enable Verbose mode', name: 'verboseOutputs'), + string(defaultValue: '50', description: 'Number of dots per line', name: 'dotsperline'), ]) pimVersions = userInput['requiredPimVersions'].tokenize(',') launchUnitTests = userInput['launchUnitTests'] launchIntegrationTests = userInput['launchIntegrationTests'] clients = userInput['clients'].tokenize(',') + verboseOutputs = userInput['verboseOutputs'] + dotsPerLine = userInput['dotsperline'] } milestone 2 @@ -60,6 +69,7 @@ try { checkouts["pim_community_dev_${currentPimVersion}"] = {buildPim(currentPimVersion, "5.6")} break case "2.0": + case "2.1": checkouts["pim_community_dev_${currentPimVersion}"] = {buildPim(currentPimVersion, "7.1")} break default: @@ -168,10 +178,10 @@ void buildPim(String pimVersion, String phpVersion) { ]) container("php") { - sh "composer --ansi require \"akeneo/catalogs\":\"dev-master\" --optimize-autoloader --no-interaction --no-progress --prefer-dist" + sh "php -d memory_limit=-1 /usr/local/bin/composer --ansi require \"akeneo/catalogs\":\"dev-master\" --optimize-autoloader --no-interaction --no-progress --prefer-dist" sh "cp app/config/parameters.yml.dist app/config/parameters.yml" sh "sed -i \"s#database_host: .*#database_host: 127.0.0.1#g\" app/config/parameters.yml" - if ("2.0" == pimVersion) { + if ("2.0" == pimVersion || "2.1" == pimVersion) { sh "sed -i \"s#index_hosts: .*#index_hosts: 'elastic:changeme@127.0.0.1:9200'#g\" app/config/parameters.yml" } sh "sed -i \"s#installer_data: .*#installer_data: '%kernel.root_dir%/../vendor/akeneo/catalogs/${pimVersion}/community/api/fixtures'#\" app/config/pim_parameters.yml" @@ -280,7 +290,8 @@ void runIntegrationTest(String phpVersion, String client, String psrImplem, Stri runPim17IntegrationTest(phpVersion, client, psrImplem) break case "2.0": - runPim20IntegrationTest(phpVersion, client, psrImplem) + case "2.1": + runPim2IntegrationTest(phpVersion, client, psrImplem, pimVersion) break default: error("pimVersion \"${pimVersion}\" is not a valid version managed by this script..") @@ -319,7 +330,7 @@ def runPim17IntegrationTest(String phpVersion, String client, String psrImplem) files += sh (returnStdout: true, script: 'find /home/jenkins/php-api-client/tests/Common/Api -name "*Integration.php"').tokenize('\n') for (file in files) { - messages.add([ + def commands = [ // Export "php" container id into shared file (We use ''' has we don't want groovy interpolation for $) // And clean kubernetes' docker prefix "docker://" (Take care, pubsub uses Busybox's sed != GNU sed) [container: "pubsub", script: '''sh -c "kubectl get pod \\${POD_NAME} -o jsonpath='{$.status.containerStatuses[?(@.name==\\"php\\")].containerID}' | sed 's#docker://##g' > /home/jenkins/php-container-id" '''], @@ -338,10 +349,14 @@ def runPim17IntegrationTest(String phpVersion, String client, String psrImplem) junit: [in: "/home/jenkins/php-api-client/", name: "junit_output.xml"], script: 'sudo php -d error_reporting="E_ALL" ./bin/phpunit -c phpunit.xml.dist '+file+' --log-junit junit_output.xml' ] - ]) - } - return messages - }) + ] + def message = new net.sf.json.JSONObject() + message.put("name",file) + message.put("commands",commands) + messages.add(message) + } + return messages + }, verboseOutputs, dotsPerLine) } /** @@ -361,8 +376,7 @@ def runPim17IntegrationTest(String phpVersion, String client, String psrImplem) * @param client Name of the HTTP client package to use to run the test with * @param psrImplem Name of the PSR 7 implementation package to run the test with */ -def runPim20IntegrationTest(String phpVersion, String client, String psrImplem) { - String pimVersion = "2.0" +def runPim2IntegrationTest(String phpVersion, String client, String psrImplem, String pimVersion) { String phpApiImageName = getApiClientGCRImageName(phpVersion, client, psrImplem) String pimImageName = getPimGCRImageName(pimVersion) @@ -373,9 +387,8 @@ def runPim20IntegrationTest(String phpVersion, String client, String psrImplem) // Find and store PHP test integration files to launch them in parallels files += sh (returnStdout: true, script: 'find /home/jenkins/php-api-client/tests/v2_0/Api -name "*Integration.php"').tokenize('\n') files += sh (returnStdout: true, script: 'find /home/jenkins/php-api-client/tests/Common/Api -name "*Integration.php"').tokenize('\n') - for (file in files) { - messages.add([ + def commands = [ // Export "php" container id into shared file (We use ''' has we don't want groovy interpolation for $) // And clean kubernetes' docker prefix "docker://" (Take care, pubsub uses Busybox's sed != GNU sed) [container: "pubsub", script: '''sh -c "kubectl get pod \\${POD_NAME} -o jsonpath='{$.status.containerStatuses[?(@.name==\\"php\\")].containerID}' | sed 's#docker://##g' > /home/jenkins/php-container-id" '''], @@ -397,10 +410,14 @@ def runPim20IntegrationTest(String phpVersion, String client, String psrImplem) junit: [in: "/home/jenkins/php-api-client/", name: "junit_output.xml"], script: 'php -d error_reporting="E_ALL" ./bin/phpunit -c phpunit.xml.dist '+file+' --log-junit junit_output.xml' ] - ]) + ] + def message = new net.sf.json.JSONObject() + message.put("name",file) + message.put("commands",commands) + messages.add(message) } return messages - }) + }, verboseOutputs, dotsPerLine) } /** @@ -433,12 +450,10 @@ def withBuildNode(String phpVersion, body) { command: 'cat', alwaysPullImage: true, envVars: [ - envVar(key: "COMPOSER_HOME", value: "/shared/.composer"), envVar(key: "COMPOSER_AUTH", value: "{\"github-oauth\":{\"github.com\": \"$token\"}}")], resourceRequestCpu: '500m', resourceRequestMemory: '1000Mi') ], volumes: [ - nfsVolume(mountPath: '/shared', serverAddress: "${env.NFS_IP}", serverPath: '/exports', readOnly: false), hostPathVolume(hostPath: "/var/run/docker.sock", mountPath: "/var/run/docker.sock") ]) { node("build-" + uuid) { @@ -543,7 +558,10 @@ def withPhpApi(String phpApiImageName, String phpVersion, body) { * - (Init) php-api-client : Copy php-api-client sources to /home/jenkins/php-api-client (Used for K8s PIM's template) * - (Run) gcloud : Used to manage pubsub queues and to create PIM's Kubernetes pods (Based on template) */ -def queue(String phpApiImageName, String pimImageName, String pimVersion, String phpVersion, body) { + +def queue(String phpApiImageName, String pimImageName, String pimVersion, String phpVersion, body, verboseOutputs, dotsPerLine) { + def verbosity = (verboseOutputs == "yes") ? "-v" : "" + def linesize = (dotsPerLine.isNumber())? dotsPerLine :"50" clearTemplateNames() def uuid = UUID.randomUUID().toString() // Maximum pods in parallel. Default set to number of messages @@ -556,6 +574,7 @@ def queue(String phpApiImageName, String pimImageName, String pimVersion, String k8s_template = "pim_17_ce.yaml" break case "2.0": + case "2.1": k8s_template = "pim_20_ce.yaml" break default: @@ -564,7 +583,7 @@ def queue(String phpApiImageName, String pimImageName, String pimVersion, String } podTemplate(name: "php-api-client-pubsub", label: "pubsub-" + uuid, containers: [ - containerTemplate(name: "gcloud", ttyEnabled: true, command: 'cat', image: "eu.gcr.io/akeneo-ci/gcloud:1.0.17", resourceRequestCpu: '100m', resourceRequestMemory: '200Mi', envVars: [envVar(key: "PUBSUB_PROJECT_ID", value: "akeneo-ci")]) + containerTemplate(name: "gcloud", ttyEnabled: true, command: 'cat', image: "eu.gcr.io/akeneo-ci/gcloud:1.0", alwaysPullImage: true, resourceRequestCpu: '100m', resourceRequestMemory: '200Mi', envVars: [envVar(key: "PUBSUB_PROJECT_ID", value: "akeneo-ci")]) ], annotations: [ podAnnotation(key: "pod.beta.kubernetes.io/init-containers", value: """ @@ -614,7 +633,7 @@ def queue(String phpApiImageName, String pimImageName, String pimVersion, String try { sh "cat /home/jenkins/php-api-client/.ci/k8s/${k8s_template}" sh "kubectl apply -f /home/jenkins/php-api-client/.ci/k8s/${k8s_template}" - sh "gcloud.phar job:wait ${env.NODE_NAME}-results-subscription ${size} ${env.WORKSPACE} --ansi" + sh "gcloud.phar ${verbosity} job:wait --dotsperline ${linesize} ${env.NODE_NAME}-results-subscription ${size} ${env.WORKSPACE} --ansi" } finally { sh "kubectl delete job ${env.NODE_NAME}" sh "gcloud.phar pubsub:topic:delete ${env.NODE_NAME}" diff --git a/.ci/k8s/pim_17_ce.dev.yaml b/.ci/k8s/pim_17_ce.dev.yaml index ad756347..fefd63d7 100644 --- a/.ci/k8s/pim_17_ce.dev.yaml +++ b/.ci/k8s/pim_17_ce.dev.yaml @@ -103,7 +103,8 @@ spec: resources: requests: {cpu: "300m", memory: "300Mi"} - name: pubsub - image: eu.gcr.io/akeneo-ci/gcloud:1.0.17 + image: eu.gcr.io/akeneo-ci/gcloud:1.0 + imagePullPolicy: Always command: ["/bin/sh", "-c"] args: - | diff --git a/.ci/k8s/pim_17_ce.yaml b/.ci/k8s/pim_17_ce.yaml index 61dd6b89..b7580f44 100644 --- a/.ci/k8s/pim_17_ce.yaml +++ b/.ci/k8s/pim_17_ce.yaml @@ -85,7 +85,9 @@ spec: resources: requests: {cpu: "500m", memory: "750Mi"} - name: pubsub - image: eu.gcr.io/akeneo-ci/gcloud:1.0.17 + image: eu.gcr.io/akeneo-ci/gcloud:1.0 + imagePullPolicy: Always + command: ["/bin/sh", "-c"] args: - | diff --git a/.ci/k8s/pim_20_ce.dev.yaml b/.ci/k8s/pim_20_ce.dev.yaml index daa20ab2..25252e84 100644 --- a/.ci/k8s/pim_20_ce.dev.yaml +++ b/.ci/k8s/pim_20_ce.dev.yaml @@ -140,7 +140,9 @@ spec: timeoutSeconds: 1 initialDelaySeconds: 5 - name: pubsub - image: eu.gcr.io/akeneo-ci/gcloud:1.0.17 + image: eu.gcr.io/akeneo-ci/gcloud:1.0 + imagePullPolicy: Always + command: ["/bin/sh", "-c"] args: - | diff --git a/.ci/k8s/pim_20_ce.yaml b/.ci/k8s/pim_20_ce.yaml index 87c6545e..d5ec3a9d 100644 --- a/.ci/k8s/pim_20_ce.yaml +++ b/.ci/k8s/pim_20_ce.yaml @@ -122,7 +122,8 @@ spec: timeoutSeconds: 1 initialDelaySeconds: 5 - name: pubsub - image: eu.gcr.io/akeneo-ci/gcloud:1.0.17 + image: eu.gcr.io/akeneo-ci/gcloud:1.0 + imagePullPolicy: Always command: ["/bin/sh", "-c"] args: - | 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. *