Skip to content

Commit

Permalink
Use PropertyAccessor in Generic ResourceProcessor. Defined ImporterRe…
Browse files Browse the repository at this point in the history
…sult as Service and injected it into the importes for better testing. Updated Readme
  • Loading branch information
Matthias Alt committed Nov 18, 2017
1 parent 82ad8b5 commit 85cd304
Show file tree
Hide file tree
Showing 16 changed files with 333 additions and 83 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ sylius.importer.foo.bar:
- "@sylius.factory.bar_reader"
- "@sylius.manager.foo"
- "@sylius.processor.foo"
- "@sylius.importer.result"
tags:
- { name: sylius.importer, type: country, format: csv }
```
Expand All @@ -102,6 +103,7 @@ sylius.importer.foo.bar:
- "@sylius.factory.bar_reader"
- "@sylius.manager.foo"
- "@sylius.processor.foo"
- "@sylius.importer.result"
tags:
- { name: sylius.importer, type: country, format: bar }
```
Expand All @@ -116,10 +118,11 @@ sylius.processor.foo:
arguments:
- "@sylius.factory.foo"
- "@sylius.repository.foo"
- "@property_accessor"
- ["HeaderKey0", "HeaderKey1", "HeaderKey2"]
```
The third parameter represents the Headers of the data to import. For csv-files this would be the headers defined in
The fourth parameter represents the Headers of the data to import. For csv-files this would be the headers defined in
its first line. These HeaderKeys have to be equal to the fields in the resource to import if the generic
ResourceProcessor is used, since the Keys are used for building dynamic Methodnames
Expand All @@ -139,6 +142,7 @@ class FooProcessor implements ResourceProcessorInterface
- "@sylius.repository.foo"
- ["HeaderKey0", "HeaderKey1", "HeaderKey2"]
```
### Running plugin tests
- Test application install
Expand Down
2 changes: 1 addition & 1 deletion phpspec.yml.dist
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
suites:
main:
namespace: FriendsOfSylius\SyliusImportExportPlugin
psr4_prefix: FriendsOfSylius\SyliusImportExportPlugin
psr4_prefix: FriendsOfSylius\SyliusImportExportPlugin
84 changes: 84 additions & 0 deletions spec/Importer/ImporterResultSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

declare(strict_types=1);

namespace spec\FriendsOfSylius\SyliusImportExportPlugin\Importer;

use FriendsOfSylius\SyliusImportExportPlugin\Importer\ImporterResult;
use PhpSpec\ObjectBehavior;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\Stopwatch\StopwatchEvent;

class ImporterResultSpec extends ObjectBehavior
{
function let(Stopwatch $stopwatch)
{
$this->beConstructedWith($stopwatch);
}

function it_is_initializable()
{
$this->shouldHaveType(ImporterResult::class);
}

function it_can_start_the_stopwatch(Stopwatch $stopwatch)
{
$stopwatch->start('import')->shouldBeCalled();
$this->start();
}

function it_can_stop_the_stopwatch(Stopwatch $stopwatch)
{
$stopwatch->stop('import')->shouldBeCalled();
$this->stop();
}

function it_can_gather_successfull_line_numbers()
{
$this->success(1);
$this->success(2);
$this->failed(3);
$this->success(4);
$this->skipped(5);

$this->getSuccessRows()->shouldReturn(
[1, 2, 4]
);
}

function it_can_gather_failed_line_numbers()
{
$this->success(1);
$this->success(2);
$this->failed(3);
$this->success(4);
$this->skipped(5);

$this->getFailedRows()->shouldReturn(
[3]
);
}

function it_can_gather_skipped_rows()
{
$this->success(1);
$this->success(2);
$this->failed(3);
$this->success(4);
$this->skipped(5);

$this->getSkippedRows()->shouldReturn(
[5]
);
}

function it_can_return_the_duration_of_the_import(Stopwatch $stopwatch, StopwatchEvent $stopwatchEvent)
{
$stopwatch->stop('import')
->willReturn($stopwatchEvent);
$stopwatchEvent->getDuration()->willReturn(1000);

$this->stop();
$this->getDuration()->shouldReturn(1000);
}
}
28 changes: 10 additions & 18 deletions spec/Importer/ResourceImporterSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
namespace spec\FriendsOfSylius\SyliusImportExportPlugin\Importer;

use Doctrine\Common\Persistence\ObjectManager;
use FriendsOfSylius\SyliusImportExportPlugin\Exception\ImporterException;
use FriendsOfSylius\SyliusImportExportPlugin\Importer\ImporterInterface;
use FriendsOfSylius\SyliusImportExportPlugin\Importer\ImporterResultInterface;
use FriendsOfSylius\SyliusImportExportPlugin\Importer\ResourceImporter;
use FriendsOfSylius\SyliusImportExportPlugin\Processor\ResourceProcessorInterface;
use PhpSpec\ObjectBehavior;
use Port\Csv\CsvReader;
use Port\Excel\ExcelReader;
use Port\Reader;
use Port\Reader\ReaderFactory;
use Prophecy\Argument;

Expand All @@ -21,9 +20,10 @@ class ResourceImporterSpec extends ObjectBehavior
function let(
ReaderFactory $readerFactory,
ObjectManager $objectManager,
ResourceProcessorInterface $resourceProcessor
ResourceProcessorInterface $resourceProcessor,
ImporterResultInterface $importerResult
) {
$this->beConstructedWith($readerFactory, $objectManager, $resourceProcessor);
$this->beConstructedWith($readerFactory, $objectManager, $resourceProcessor, $importerResult);
}

function it_is_initializable()
Expand All @@ -36,21 +36,12 @@ function it_implements_the_importer_interface()
$this->shouldImplement(ImporterInterface::class);
}

function it_throws_exception_for_reader_without_required_method(
ReaderFactory $readerFactory,
Reader $someReader,
ObjectManager $objectManager,
ResourceProcessorInterface $resourceProcessor
) {
$readerFactory->getReader(Argument::type(\SplFileObject::class))->willReturn($someReader);
$this->shouldThrow(ImporterException::class)->during('import', [__DIR__ . '/tax_categories.csv']);
}

function it_imports_countries_from_csv_file(
ReaderFactory $readerFactory,
CsvReader $csvReader,
ObjectManager $objectManager,
ResourceProcessorInterface $resourceProcessor
ResourceProcessorInterface $resourceProcessor,
ImporterResultInterface $importerResult
) {
$csvReader->getColumnHeaders()->willReturn(['Code']);
$csvReader->rewind()->willReturn();
Expand All @@ -67,6 +58,10 @@ function it_imports_countries_from_csv_file(
$resourceProcessor->process(Argument::type('array'))->shouldBeCalledTimes(2);
$objectManager->flush()->shouldBeCalledTimes(1);

$importerResult->start()->shouldBeCalledTimes(1);
$importerResult->success(Argument::type('int'))->shouldBeCalledTimes(2);
$importerResult->stop()->shouldBeCalledTimes(1);

$this->import(__DIR__ . '/countries.csv');
}

Expand All @@ -76,7 +71,6 @@ function it_imports_countries_from_excel_file(
ObjectManager $objectManager,
ResourceProcessorInterface $resourceProcessor
) {
$excelReader->getColumnHeaders()->willReturn(['Code']);
$excelReader->rewind()->willReturn();
$excelReader->key()->willReturn(0, 1);
$excelReader->count()->willReturn(2);
Expand All @@ -99,7 +93,6 @@ function it_imports_tax_categories_from_csv_file(
ObjectManager $objectManager,
ResourceProcessorInterface $resourceProcessor
) {
$csvReader->getColumnHeaders()->willReturn(['Code', 'Name', 'Description']);
$csvReader->rewind()->willReturn();
$csvReader->key()->willReturn(0, 1);
$csvReader->count()->willReturn(2);
Expand All @@ -123,7 +116,6 @@ function it_imports_tax_categories_from_excel_file(
ObjectManager $objectManager,
ResourceProcessorInterface $resourceProcessor
) {
$excelReader->getColumnHeaders()->willReturn(['Code', 'Name', 'Description']);
$excelReader->rewind()->willReturn();
$excelReader->key()->willReturn(0, 1);
$excelReader->count()->willReturn(2);
Expand Down
2 changes: 2 additions & 0 deletions spec/Processor/PaymentMethodProcessorSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Sylius\Component\Core\Factory\PaymentMethodFactoryInterface;
use Sylius\Component\Core\Model\PaymentMethodInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;

class PaymentMethodProcessorSpec extends ObjectBehavior
{
Expand All @@ -35,6 +36,7 @@ function it_can_process_an_array_of_payment_method_data(
PaymentMethodFactoryInterface $factory,
PaymentMethodInterface $paymentMethod,
GatewayConfigInterface $gatewayConfig,
PropertyAccessorInterface $propertyAccessor,
RepositoryInterface $repository
) {
$this->beConstructedWith($factory, $repository, ['Code', 'Name', 'Instructions', 'Gateway']);
Expand Down
71 changes: 62 additions & 9 deletions spec/Processor/ResourceProcessorSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,23 @@

namespace spec\FriendsOfSylius\SyliusImportExportPlugin\Processor;

use FriendsOfSylius\SyliusImportExportPlugin\Exception\AccessorNotFoundException;
use FriendsOfSylius\SyliusImportExportPlugin\Processor\ResourceProcessor;
use FriendsOfSylius\SyliusImportExportPlugin\Processor\ResourceProcessorInterface;
use PhpSpec\ObjectBehavior;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
use Sylius\Component\Taxation\Model\TaxCategoryInterface;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;

class ResourceProcessorSpec extends ObjectBehavior
{
public function let(
FactoryInterface $factory,
RepositoryInterface $repository
RepositoryInterface $repository,
PropertyAccessorInterface $propertyAccessor
) {
$this->beConstructedWith($factory, $repository);
$this->beConstructedWith($factory, $repository, $propertyAccessor, []);
}

function it_is_initializable()
Expand All @@ -33,30 +36,80 @@ function it_implements_the_resource_processor_interface()
function it_can_process_an_array_of_tax_category_data(
FactoryInterface $factory,
TaxCategoryInterface $taxCategory,
PropertyAccessorInterface $propertyAccessor,
RepositoryInterface $repository
) {
$this->beConstructedWith($factory, $repository, ['Code', 'Name', 'Description']);
$this->beConstructedWith($factory, $repository, $propertyAccessor, ['Code', 'Name', 'Description']);
$repository->findOneBy(['code' => 'BOOKS'])->willReturn(null);
$factory->createNew()->willReturn($taxCategory);
$repository->add($taxCategory)->shouldBeCalledTimes(1);
$taxCategory->setCode('BOOKS')->shouldBeCalled();
$taxCategory->setName('books')->shouldBeCalled();
$taxCategory->setDescription('tax category for books')->shouldBeCalled();

$propertyAccessor->isReadable($taxCategory, 'Code')
->willReturn(true)
->shouldBeCalled();
$propertyAccessor->isReadable($taxCategory, 'Name')
->willReturn(true)
->shouldBeCalled();
$propertyAccessor->isReadable($taxCategory, 'Description')
->willReturn(true)
->shouldBeCalled();

$propertyAccessor->setValue($taxCategory, 'Code', 'BOOKS')->shouldBeCalled();
$propertyAccessor->setValue($taxCategory, 'Name', 'books')->shouldBeCalled();
$propertyAccessor->setValue($taxCategory, 'Description', 'tax category for books')->shouldBeCalled();

$this->process(['Code' => 'BOOKS', 'Name' => 'books', 'Description' => 'tax category for books']);
}

function it_can_process_an_array_of_country_data(
FactoryInterface $factory,
TaxCategoryInterface $country,
RepositoryInterface $repository
RepositoryInterface $repository,
PropertyAccessorInterface $propertyAccessor
) {
$this->beConstructedWith($factory, $repository, ['Code']);
$this->beConstructedWith($factory, $repository, $propertyAccessor, ['Code']);
$repository->findOneBy(['code' => 'DE'])->willReturn(null);
$factory->createNew()->willReturn($country);
$repository->add($country)->shouldBeCalledTimes(1);
$country->setCode('DE')->shouldBeCalled();

$propertyAccessor->isReadable($country, 'Code')
->willReturn(true)
->shouldBeCalled();
$propertyAccessor->setValue($country, 'Code', 'DE')
->shouldBeCalled();

$this->process(['Code' => 'DE']);
}

function it_throws_accessor_not_found_exception_for_non_existing_header_keys(
FactoryInterface $factory,
TaxCategoryInterface $country,
RepositoryInterface $repository,
PropertyAccessorInterface $propertyAccessor
) {
$this->beConstructedWith($factory, $repository, $propertyAccessor, ['Code', 'not_existing_header_key']);
$repository->findOneBy(['code' => 'DE'])->willReturn(null);
$factory->createNew()->willReturn($country);
$repository->add($country)->shouldNotBeCalled();

$propertyAccessor->isReadable($country, 'Code')
->willReturn(true)
->shouldBeCalled();
$propertyAccessor->isReadable($country, 'not_existing_header_key')
->willReturn(false)
->shouldBeCalled();

$propertyAccessor->setValue($country, 'Code', 'DE')
->shouldBeCalled();

$this->shouldThrow(
new AccessorNotFoundException(
sprintf(
'No Accessor found for %s in Resource %s, please implement one or change the Header-Key to an existing field',
'not_existing_header_key',
get_class($country->getWrappedObject())
)
)
)->during('process', [['Code' => 'DE', 'not_existing_header_key' => 'some value']]);
}
}
9 changes: 9 additions & 0 deletions src/Exception/AccessorNotFoundException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace FriendsOfSylius\SyliusImportExportPlugin\Exception;

class AccessorNotFoundException extends ImporterException
{
}
4 changes: 2 additions & 2 deletions src/Importer/ImporterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface ImporterInterface
/**
* @param string $fileName
*
* @return ImporterResult
* @return ImporterResultInterface
*/
public function import(string $fileName): ImporterResult;
public function import(string $fileName): ImporterResultInterface;
}
Loading

0 comments on commit 85cd304

Please sign in to comment.