From 773098623eb0b835e66f66bf7046addfa5fb6eb7 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Mon, 28 Aug 2023 15:29:08 +0200 Subject: [PATCH] Add command to allow import of a single configuration. The command is also available as scheduler task. This finally allows to regularly execute imports. This also allows to import from CLI context with differently configured timeouts. --- .../Command/ImportConfigurationCommand.php | 92 +++++++++++++++ .../Backend/ImportConfigurationRepository.php | 13 +++ Configuration/Services.yaml | 5 + Documentation/Changelog/2.1.0.rst | 31 ++++++ Tests/Functional/AbstractImportTest.php | 92 +++++++++++++++ .../ImportConfigurationCommandTest.php | 105 ++++++++++++++++++ Tests/Functional/ImportTest.php | 56 +--------- 7 files changed, 339 insertions(+), 55 deletions(-) create mode 100644 Classes/Command/ImportConfigurationCommand.php create mode 100644 Documentation/Changelog/2.1.0.rst create mode 100644 Tests/Functional/AbstractImportTest.php create mode 100644 Tests/Functional/ImportConfigurationCommandTest.php diff --git a/Classes/Command/ImportConfigurationCommand.php b/Classes/Command/ImportConfigurationCommand.php new file mode 100644 index 0000000..95e4dad --- /dev/null +++ b/Classes/Command/ImportConfigurationCommand.php @@ -0,0 +1,92 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +namespace WerkraumMedia\ThueCat\Command; + +use Exception; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use TYPO3\CMS\Core\Core\Bootstrap; +use WerkraumMedia\ThueCat\Domain\Import\Importer; +use WerkraumMedia\ThueCat\Domain\Repository\Backend\ImportConfigurationRepository; + +class ImportConfigurationCommand extends Command +{ + /** + * @var ImportConfigurationRepository + */ + private $importConfigurationRepository; + + /** + * @var Importer + */ + private $importer; + + public function __construct( + ImportConfigurationRepository $importConfigurationRepository, + Importer $importer + ) { + parent::__construct(); + + $this->importConfigurationRepository = $importConfigurationRepository; + $this->importer = $importer; + } + + protected function configure(): void + { + $this->setDescription('Executes a single import based on the given configuration.'); + + $this->addArgument( + 'configuration', + InputArgument::REQUIRED, + 'The UID of the import configuration to use' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + Bootstrap::initializeBackendAuthentication(); + + $configurationUid = $input->getArgument('configuration'); + if (is_numeric($configurationUid)) { + $configurationUid = (int)$configurationUid; + } else { + throw new Exception('No numeric uid for configuration provided.', 1643267138); + } + + $configuration = $this->importConfigurationRepository->findOneByUid($configurationUid); + if ($configuration === null) { + throw new Exception('No configuration found for uid: ' . $configurationUid, 1693228522); + } + + $importLog = $this->importer->importConfiguration($configuration); + + if ($importLog->hasErrors()) { + return Command::FAILURE; + } + + return Command::SUCCESS; + } +} diff --git a/Classes/Domain/Repository/Backend/ImportConfigurationRepository.php b/Classes/Domain/Repository/Backend/ImportConfigurationRepository.php index 6192df3..f19354b 100644 --- a/Classes/Domain/Repository/Backend/ImportConfigurationRepository.php +++ b/Classes/Domain/Repository/Backend/ImportConfigurationRepository.php @@ -26,6 +26,7 @@ namespace WerkraumMedia\ThueCat\Domain\Repository\Backend; use TYPO3\CMS\Extbase\Object\ObjectManagerInterface; use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings; use TYPO3\CMS\Extbase\Persistence\Repository; +use WerkraumMedia\ThueCat\Domain\Import\ImportConfiguration; class ImportConfigurationRepository extends Repository { @@ -39,4 +40,16 @@ class ImportConfigurationRepository extends Repository $this->setDefaultQuerySettings($querySettings); } + + /** + * @return ImportConfiguration|null + */ + public function findOneByUid(int $uid) + { + $query = $this->createQuery(); + + $query->matching($query->equals('uid', $uid)); + + return $query->execute()->getFirst(); + } } diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index f6935f7..c7357f3 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -7,6 +7,11 @@ services: WerkraumMedia\ThueCat\: resource: '../Classes/*' + WerkraumMedia\ThueCat\Command\ImportConfigurationCommand: + tags: + - name: 'console.command' + command: 'thuecat:importviaconfiguration' + WerkraumMedia\ThueCat\Domain\Import\Importer\FetchData: arguments: $requestFactory: '@WerkraumMedia\ThueCat\Domain\Import\RequestFactory' diff --git a/Documentation/Changelog/2.1.0.rst b/Documentation/Changelog/2.1.0.rst new file mode 100644 index 0000000..09919e5 --- /dev/null +++ b/Documentation/Changelog/2.1.0.rst @@ -0,0 +1,31 @@ +2.1.0 +===== + +Breaking +-------- + +Nothing + +Features +-------- + +* Add command to allow import of a single configuration. + The command is also available as scheduler task. + This finally allows to regularly execute imports. + This also allows to import from CLI context with differently configured timeouts. + +Fixes +----- + +Nothing + +Tasks +----- + +Nothing + +Deprecation +----------- + +Nothing + diff --git a/Tests/Functional/AbstractImportTest.php b/Tests/Functional/AbstractImportTest.php new file mode 100644 index 0000000..5584542 --- /dev/null +++ b/Tests/Functional/AbstractImportTest.php @@ -0,0 +1,92 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +namespace WerkraumMedia\ThueCat\Tests\Functional; + +use TYPO3\CMS\Core\Localization\LanguageService; +use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; + +abstract class AbstractImportTest extends FunctionalTestCase +{ + protected function setUp(): void + { + $this->coreExtensionsToLoad = array_merge($this->coreExtensionsToLoad, [ + 'core', + 'backend', + 'extbase', + 'frontend', + ]); + + $this->testExtensionsToLoad = array_merge($this->testExtensionsToLoad, [ + 'typo3conf/ext/thuecat/', + ]); + + $this->pathsToLinkInTestInstance = array_merge($this->pathsToLinkInTestInstance, [ + 'typo3conf/ext/thuecat/Tests/Functional/Fixtures/Import/Sites/' => 'typo3conf/sites', + ]); + + $this->configurationToUseInTestInstance = array_merge($this->configurationToUseInTestInstance, [ + 'LOG' => [ + 'WerkraumMedia' => [ + 'writerConfiguration' => [ + \TYPO3\CMS\Core\Log\LogLevel::DEBUG => [ + \TYPO3\CMS\Core\Log\Writer\FileWriter::class => [ + 'logFileInfix' => 'debug', + ], + ], + ], + ], + ], + 'EXTENSIONS' => [ + 'thuecat' => [ + 'apiKey' => null, + ], + ], + ]); + + parent::setUp(); + GuzzleClientFaker::registerClient(); + + $this->setUpBackendUserFromFixture(1); + + $GLOBALS['LANG'] = $this->getContainer()->get(LanguageService::class); + } + + protected function assertPostConditions(): void + { + $path = self::getInstancePath() . '/typo3temp/var/log/typo3_0493d91d8e.log'; + $this->assertSame( + '', + file_get_contents($path), + 'The TYPO3 log file contained content while expecting to be empty.' + ); + } + + protected function tearDown(): void + { + unset($GLOBALS['LANG']); + GuzzleClientFaker::tearDown(); + + parent::tearDown(); + } +} diff --git a/Tests/Functional/ImportConfigurationCommandTest.php b/Tests/Functional/ImportConfigurationCommandTest.php new file mode 100644 index 0000000..eb5e1e5 --- /dev/null +++ b/Tests/Functional/ImportConfigurationCommandTest.php @@ -0,0 +1,105 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +namespace WerkraumMedia\ThueCat\Tests\Functional; + +use Exception; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Tester\CommandTester; +use WerkraumMedia\ThueCat\Command\ImportConfigurationCommand; + +/** + * @covers \WerkraumMedia\ThueCat\Command\ImportConfigurationCommand + * @testdox The 'thuecat:importviaconfiguration' command + */ +final class ImportConfigurationCommandTest extends AbstractImportTest +{ + /** + * @test + */ + public function canImport(): void + { + $subject = $this->getContainer()->get(ImportConfigurationCommand::class); + self::assertInstanceOf(Command::class, $subject); + + $this->importDataSet(__DIR__ . '/Fixtures/Import/ImportsFreshOrganization.xml'); + GuzzleClientFaker::appendResponseFromFile(__DIR__ . '/Fixtures/Import/Guzzle/thuecat.org/resources/018132452787-ngbe.json'); + + $tester = new CommandTester($subject); + $tester->execute(['configuration' => 1], ['capture_stderr_separately' => true]); + + $this->assertCSVDataSet('EXT:thuecat/Tests/Functional/Fixtures/Import/ImportsFreshOrganization.csv'); + } + + /** + * @test + */ + public function throwsExceptionOnNoneExistingConfiguration(): void + { + $subject = $this->getContainer()->get(ImportConfigurationCommand::class); + self::assertInstanceOf(Command::class, $subject); + + $tester = new CommandTester($subject); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('No configuration found for uid: 1'); + $this->expectExceptionCode(1693228522); + + $tester->execute(['configuration' => 1], ['capture_stderr_separately' => true]); + } + + /** + * @test + */ + public function throwsExceptionOnMissingArgument(): void + { + $subject = $this->getContainer()->get(ImportConfigurationCommand::class); + self::assertInstanceOf(Command::class, $subject); + + $tester = new CommandTester($subject); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Not enough arguments (missing: "configuration")'); + $this->expectExceptionCode(0); + + $tester->execute([], ['capture_stderr_separately' => true]); + } + + /** + * @test + */ + public function throwsExceptionOnNoneNumericConfigurationArgument(): void + { + $subject = $this->getContainer()->get(ImportConfigurationCommand::class); + self::assertInstanceOf(Command::class, $subject); + + $tester = new CommandTester($subject); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('No numeric uid for configuration provided.'); + $this->expectExceptionCode(1643267138); + + $tester->execute(['configuration' => 'a'], ['capture_stderr_separately' => true]); + } +} diff --git a/Tests/Functional/ImportTest.php b/Tests/Functional/ImportTest.php index cab73eb..5405395 100644 --- a/Tests/Functional/ImportTest.php +++ b/Tests/Functional/ImportTest.php @@ -23,8 +23,6 @@ namespace WerkraumMedia\ThueCat\Tests\Functional; * 02110-1301, USA. */ -use TYPO3\CMS\Core\Localization\LanguageService; -use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase as TestCase; use WerkraumMedia\ThueCat\Domain\Import\Importer; use WerkraumMedia\ThueCat\Domain\Repository\Backend\ImportConfigurationRepository; @@ -59,60 +57,8 @@ use WerkraumMedia\ThueCat\Domain\Repository\Backend\ImportConfigurationRepositor * * @testdox The import */ -class ImportTest extends TestCase +class ImportTest extends AbstractImportTest { - protected $coreExtensionsToLoad = [ - 'core', - 'backend', - 'extbase', - 'frontend', - ]; - - protected $testExtensionsToLoad = [ - 'typo3conf/ext/thuecat/', - ]; - - protected $pathsToLinkInTestInstance = [ - 'typo3conf/ext/thuecat/Tests/Functional/Fixtures/Import/Sites/' => 'typo3conf/sites', - ]; - - protected $configurationToUseInTestInstance = [ - 'LOG' => [ - 'WerkraumMedia' => [ - 'writerConfiguration' => [ - \TYPO3\CMS\Core\Log\LogLevel::DEBUG => [ - \TYPO3\CMS\Core\Log\Writer\FileWriter::class => [ - 'logFileInfix' => 'debug', - ], - ], - ], - ], - ], - 'EXTENSIONS' => [ - 'thuecat' => [ - 'apiKey' => null, - ], - ], - ]; - - protected function setUp(): void - { - parent::setUp(); - GuzzleClientFaker::registerClient(); - - $this->setUpBackendUserFromFixture(1); - - $GLOBALS['LANG'] = $this->getContainer()->get(LanguageService::class); - } - - protected function tearDown(): void - { - unset($GLOBALS['LANG']); - GuzzleClientFaker::tearDown(); - - parent::tearDown(); - } - /** * @test */