Skip to content

Commit

Permalink
Merge pull request #8 from akeneo/PIM-8320
Browse files Browse the repository at this point in the history
PIM-8320: Upload media files during import
  • Loading branch information
SamirBoulil authored May 3, 2019
2 parents ee3d10a + 051dfa1 commit eb012ef
Show file tree
Hide file tree
Showing 21 changed files with 230 additions and 98 deletions.
8 changes: 0 additions & 8 deletions config/data_converters.yml

This file was deleted.

18 changes: 17 additions & 1 deletion config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,30 @@ services:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.

# Tags
_instanceof:
App\Processor\Converter\DataConverterInterface:
tags: ['app.data_converter']

# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'

Box\Spout\Writer\CSV\Writer: ~

Akeneo\PimEnterprise\ApiClient\AkeneoPimEnterpriseClientBuilder:
arguments:
$baseUri: '%env(AKENEO_API_BASE_URI)%'

Box\Spout\Writer\CSV\Writer: ~
Akeneo\PimEnterprise\ApiClient\AkeneoPimEnterpriseClientInterface:
factory: 'Akeneo\PimEnterprise\ApiClient\AkeneoPimEnterpriseClientBuilder:buildAuthenticatedByPassword'
arguments:
- '%env(AKENEO_API_CLIENT_ID)%'
- '%env(AKENEO_API_CLIENT_SECRET)%'
- '%env(AKENEO_API_USERNAME)%'
- '%env(AKENEO_API_PASSWORD)%'

App\Processor\Converter\DataConverter:
arguments: [!tagged app.data_converter]
49 changes: 26 additions & 23 deletions src/Command/ImportCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace App\Command;

use Akeneo\PimEnterprise\ApiClient\AkeneoPimEnterpriseClientInterface;
use App\ApiClientFactory;
use App\FileLogger;
use App\Processor\Converter\DataConverter;
use App\Processor\RecordProcessor;
Expand Down Expand Up @@ -42,9 +43,6 @@ class ImportCommand extends Command
/** @var StructureGenerator */
private $structureGenerator;

/** @var AkeneoPimEnterpriseClientBuilder */
private $clientBuilder;

/** @var DataConverter */
private $converter;

Expand All @@ -68,20 +66,20 @@ class ImportCommand extends Command

public function __construct(
StructureGenerator $structureGenerator,
AkeneoPimEnterpriseClientBuilder $clientBuilder,
DataConverter $converter,
RecordProcessor $processor,
FileLogger $logger,
InvalidFileGenerator $invalidFileGenerator
InvalidFileGenerator $invalidFileGenerator,
AkeneoPimEnterpriseClientInterface $apiClient
) {
parent::__construct(static::$defaultName);

$this->structureGenerator = $structureGenerator;
$this->clientBuilder = $clientBuilder;
$this->converter = $converter;
$this->processor = $processor;
$this->logger = $logger;
$this->invalidFileGenerator = $invalidFileGenerator;
$this->apiClient = $apiClient;
}

protected function configure()
Expand Down Expand Up @@ -126,8 +124,6 @@ protected function execute(InputInterface $input, OutputInterface $output)
'If you want to automate this process or don\'t want to use default values, add the --no-interaction flag when you call this command.'
]);

$this->initializeApiClient($input);

$this->io->newLine(2);
$this->io->title(
sprintf(
Expand Down Expand Up @@ -168,16 +164,6 @@ protected function execute(InputInterface $input, OutputInterface $output)
}
}

private function initializeApiClient(InputInterface $input): void
{
$this->apiClient = $this->clientBuilder->buildAuthenticatedByPassword(
$input->getOption('apiClientId'),
$input->getOption('apiClientSecret'),
$input->getOption('apiUsername'),
$input->getOption('apiPassword')
);
}

private function fetchReferenceEntityAttributes(string $referenceEntityCode): array
{
$attributes = $this->apiClient->getReferenceEntityAttributeApi()->all($referenceEntityCode);
Expand Down Expand Up @@ -259,12 +245,12 @@ private function importRecords(
continue;
}

if (count($this->reader->getHeaders()) !== count($row)) {
$this->logger->skip(sprintf(
if (!$this->isHeaderValid($row)) {
$message = sprintf(
'Skipped line %s: the number of values is not equal to the number of headers',
$lineNumber
));
$this->invalidFileGenerator->fromRow($row, $filePath, $this->reader->getHeaders());
);
$this->skipRowWithMessage($filePath, $row, $message);

continue;
}
Expand All @@ -275,7 +261,13 @@ private function importRecords(
$structure = $this->structureGenerator->generate($attributes, $channels);
$validStructure = array_intersect_key($structure, array_flip($validHeaders));

$recordsToWrite[] = $this->processor->process($line, $validStructure);
try {
$recordsToWrite[] = $this->processor->process($line, $validStructure, $filePath);
} catch (\Exception $e) {
$this->skipRowWithMessage($filePath, $row, $e->getMessage());

continue;
}
$linesToWrite[] = $line;

if (count($recordsToWrite) === self::BATCH_SIZE) {
Expand Down Expand Up @@ -307,4 +299,15 @@ private function writeRecords(
$this->logger->logResponses($responses);
$this->invalidFileGenerator->fromResponses($responses, $linesToWrite, $filePath, $this->reader->getHeaders());
}

private function isHeaderValid($row): bool
{
return count($this->reader->getHeaders()) === count($row);
}

private function skipRowWithMessage(string $filePath, $row, string $message): void
{
$this->logger->skip($message);
$this->invalidFileGenerator->fromRow($row, $filePath, $this->reader->getHeaders());
}
}
3 changes: 0 additions & 3 deletions src/InvalidFileGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

namespace App;

use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Box\Spout\Writer\WriterFactory;
use Box\Spout\Common\Type;
use Box\Spout\Writer\WriterInterface;
use Box\Spout\Writer\CSV\Writer;
use Box\Spout\Common\Helper\GlobalFunctionsHelper;
Expand Down
25 changes: 8 additions & 17 deletions src/Processor/Converter/DataConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,34 @@

namespace App\Processor\Converter;

use App\YamlReader;

/**
* Prepare the data to be sent to the API, using registered dedicated converters
*
* @author Adrien Pétremann <[email protected]>
* @copyright 2019 Akeneo SAS (https://www.akeneo.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
class DataConverter implements DataConverterInterface
class DataConverter
{
/** @var DataConverterInterface[] */
private $converters;

/** @var YamlReader */
private $yamlReader;
private $converters = [];

public function __construct(YamlReader $yamlReader)
public function __construct(iterable $dataConverters)
{
$this->yamlReader = $yamlReader;
$dataConvertersConfig = $this->yamlReader->parseFile('config/data_converters.yml');
$dataConverterClasses = $dataConvertersConfig['data_converters'];

foreach ($dataConverterClasses as $dataConverterClass) {
$this->converters[] = new $dataConverterClass();
}
$array = [];
array_push($array, ...$dataConverters);
$this->converters = $array;
}

/**
* {@inheritdoc}
*/
public function convert(array $attribute, string $data)
public function convert(array $attribute, string $data, array $context)
{
/** @var DataConverterInterface $converter */
foreach ($this->converters as $converter) {
if ($converter->support($attribute)) {
return $converter->convert($attribute, $data);
return $converter->convert($attribute, $data, $context);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Processor/Converter/DataConverterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ public function support(array $attribute): bool;
/**
* Convert the given $data for the given $attribute to the correct format expected by the Akeneo API
*/
public function convert(array $attribute, string $data);
public function convert(array $attribute, string $data, array $context);
}
75 changes: 75 additions & 0 deletions src/Processor/Converter/MediaAttributeConverter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

declare(strict_types=1);

namespace App\Processor\Converter;

use Akeneo\PimEnterprise\ApiClient\AkeneoPimEnterpriseClientInterface;
use Symfony\Component\Filesystem\Filesystem;

/**
* @author Samir Boulil <[email protected]>
* @copyright 2019 Akeneo SAS (http://www.akeneo.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
class MediaAttributeConverter implements DataConverterInterface
{
private const IMAGE_ATTRIBUTE_TYPE = 'image';

/** @var AkeneoPimEnterpriseClientInterface */
private $pimClient;

/** @var Filesystem */
private $filesystem;

public function __construct(AkeneoPimEnterpriseClientInterface $pimClient, Filesystem $filesystem)
{
$this->filesystem = $filesystem;
$this->pimClient = $pimClient;
}

public function support(array $attribute): bool
{
return self::IMAGE_ATTRIBUTE_TYPE === $attribute['type'];
}

public function convert(array $attribute, string $data, array $context)
{
$mediaFilePath = $this->mediaFilePath($data, $context);
$this->checkMediaExists($mediaFilePath);
$mediaIdentifier = $this->uploadMediaToPIM($mediaFilePath);

return $mediaIdentifier;
}

private function mediaFilePath(string $relativeMediaPath, array $context): string
{
$fileToImportPath = $context['filePath'];

return sprintf('%s%s%s', dirname($fileToImportPath), DIRECTORY_SEPARATOR, $relativeMediaPath);
}

private function checkMediaExists(string $mediaFilePath): void
{
if (!$this->filesystem->exists($mediaFilePath)) {
throw new \RuntimeException(sprintf('media file at path "%s" was not found.', $mediaFilePath));
}
}

private function uploadMediaToPIM(string $mediaFilePath): string
{
try {
$mediaIdentifier = $this->pimClient->getReferenceEntityMediaFileApi()->create($mediaFilePath);

return $mediaIdentifier;
} catch (\Exception $exception) {
$message = sprintf(
'An error occured while uploading the media at path "%s" to the PIM: %s',
$mediaFilePath,
$exception->getMessage()
);

throw new \RuntimeException($message);
}
}
}
2 changes: 1 addition & 1 deletion src/Processor/Converter/MultipleOptionsConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function support(array $attribute): bool
/**
* {@inheritdoc}
*/
public function convert(array $attribute, string $data)
public function convert(array $attribute, string $data, array $context)
{
if (empty($data)) {
return [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function support(array $attribute): bool
/**
* {@inheritdoc}
*/
public function convert(array $attribute, string $data)
public function convert(array $attribute, string $data, array $context)
{
if (empty($data)) {
return [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function support(array $attribute): bool
/**
* {@inheritdoc}
*/
public function convert(array $attribute, string $data)
public function convert(array $attribute, string $data, array $context)
{
return $data;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Processor/Converter/SingleOptionConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function support(array $attribute): bool
/**
* {@inheritdoc}
*/
public function convert(array $attribute, string $data)
public function convert(array $attribute, string $data, array $context)
{
return $data;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Processor/Converter/TextAttributeConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function support(array $attribute): bool
/**
* {@inheritdoc}
*/
public function convert(array $attribute, string $data)
public function convert(array $attribute, string $data, array $context)
{
return $data;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Processor/RecordProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function __construct(ValueKeyGenerator $valueKeyGenerator, DataConverter
$this->dataConverter = $dataConverter;
}

public function process(array $line, array $validStructure): array
public function process(array $line, array $validStructure, string $filePath): array
{
$values = [];
foreach ($validStructure as $valueKey => $attribute) {
Expand All @@ -29,7 +29,7 @@ public function process(array $line, array $validStructure): array
$values[$attribute['code']][] = [
'channel' => $context['channel'],
'locale' => $context['locale'],
'data' => $this->dataConverter->convert($attribute, $line[$valueKey]),
'data' => $this->dataConverter->convert($attribute, $line[$valueKey], ['filePath' => $filePath]),
];
}

Expand Down
22 changes: 0 additions & 22 deletions src/YamlReader.php

This file was deleted.

Loading

0 comments on commit eb012ef

Please sign in to comment.