From dc1c45f1c1fedb57e73d5650854e61f22dd516a1 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Mon, 1 Feb 2021 14:14:05 +0100 Subject: [PATCH] Initial import mechanism Provide first basic import mechanism. It already allows to import entities into TYPO3 database. Three entities are supported. Entities are configured through import configuration. This can be created, viewed, and edited through backend module. Imports are tracked and accessible from backend module. Still this is basic. Importing lists of entities is not supported. Multiple languages is not supported, etc. Relates: #8214 --- .../Controller/Backend/AbstractController.php | 59 +++++ .../Controller/Backend/ImportController.php | 80 ++++++ .../Controller/Backend/OverviewController.php | 58 +++++ Classes/DependencyInjection/ConverterPass.php | 48 ++++ .../DependencyInjection/UrlProvidersPass.php | 48 ++++ Classes/Domain/Import/Converter/Converter.php | 37 +++ .../Domain/Import/Converter/Organisation.php | 47 ++++ Classes/Domain/Import/Converter/Registry.php | 48 ++++ .../Import/Converter/TouristInformation.php | 72 ++++++ Classes/Domain/Import/Converter/Town.php | 58 +++++ Classes/Domain/Import/Importer.php | 93 +++++++ Classes/Domain/Import/Importer/FetchData.php | 51 ++++ Classes/Domain/Import/Importer/SaveData.php | 102 ++++++++ Classes/Domain/Import/Model/Entity.php | 65 +++++ Classes/Domain/Import/Model/GenericEntity.php | 88 +++++++ Classes/Domain/Import/RequestFactory.php | 41 +++ .../Domain/Import/UrlProvider/Registry.php | 53 ++++ .../Import/UrlProvider/StaticUrlProvider.php | 57 ++++ .../Domain/Import/UrlProvider/UrlProvider.php | 38 +++ .../Domain/Model/Backend/AbstractEntity.php | 54 ++++ .../Model/Backend/ImportConfiguration.php | 77 ++++++ Classes/Domain/Model/Backend/ImportLog.php | 114 ++++++++ .../Domain/Model/Backend/ImportLogEntry.php | 97 +++++++ Classes/Domain/Model/Backend/Organisation.php | 54 ++++ .../Model/Backend/TouristInformation.php | 28 ++ Classes/Domain/Model/Backend/Town.php | 39 +++ .../Backend/ImportConfigurationRepository.php | 42 +++ .../Backend/ImportLogRepository.php | 90 +++++++ .../Backend/OrganisationRepository.php | 44 ++++ .../Repository/Backend/TownRepository.php | 56 ++++ Classes/Extension.php | 59 +++++ Classes/Typo3Wrapper/TranslationService.php | 37 +++ Classes/View/Backend/Menu.php | 65 +++++ Configuration/Extbase/Persistence/Classes.php | 22 ++ .../FlexForm/ImportConfiguration/Static.xml | 39 +++ Configuration/Services.php | 18 ++ Configuration/Services.yaml | 12 + .../SiteConfiguration/Overrides/sites.php | 4 +- .../TCA/tx_thuecat_import_configuration.php | 71 +++++ Configuration/TCA/tx_thuecat_import_log.php | 57 ++++ .../TCA/tx_thuecat_import_log_entry.php | 88 +++++++ Configuration/TCA/tx_thuecat_organisation.php | 81 ++++++ .../TCA/tx_thuecat_tourist_information.php | 82 ++++++ Configuration/TCA/tx_thuecat_town.php | 76 ++++++ Resources/Private/Language/locallang.xlf | 102 ++++++++ Resources/Private/Language/locallang_be.xlf | 2 +- .../Private/Language/locallang_flexform.xlf | 17 ++ Resources/Private/Language/locallang_mod.xlf | 17 ++ Resources/Private/Language/locallang_tca.xlf | 133 ++++++++++ .../Templates/Backend/Import/Index.html | 54 ++++ .../Templates/Backend/Overview/Index.html | 138 ++++++++++ .../Functional/Domain/Import/ImporterTest.php | 48 ++++ .../Import/Converter/OrganisationTest.php | 91 +++++++ .../Domain/Import/Converter/RegistryTest.php | 108 ++++++++ .../Converter/TouristInformationTest.php | 199 ++++++++++++++ .../Unit/Domain/Import/Converter/TownTest.php | 141 ++++++++++ .../Domain/Import/Importer/FetchDataTest.php | 118 +++++++++ Tests/Unit/Domain/Import/ImporterTest.php | 243 ++++++++++++++++++ .../Domain/Import/Model/GenericEntityTest.php | 183 +++++++++++++ .../Unit/Domain/Import/RequestFactoryTest.php | 54 ++++ .../Import/UrlProvider/RegistryTest.php | 92 +++++++ .../UrlProvider/StaticUrlProviderTest.php | 95 +++++++ Tests/Unit/ExtensionTest.php | 42 +++ composer.json | 24 +- ecs.php | 15 +- ext_tables.php | 5 + ext_tables.sql | 42 +++ ext_typoscript_setup.typoscript | 9 + phpunit.xml.dist | 36 +++ 69 files changed, 4546 insertions(+), 11 deletions(-) create mode 100644 Classes/Controller/Backend/AbstractController.php create mode 100644 Classes/Controller/Backend/ImportController.php create mode 100644 Classes/Controller/Backend/OverviewController.php create mode 100644 Classes/DependencyInjection/ConverterPass.php create mode 100644 Classes/DependencyInjection/UrlProvidersPass.php create mode 100644 Classes/Domain/Import/Converter/Converter.php create mode 100644 Classes/Domain/Import/Converter/Organisation.php create mode 100644 Classes/Domain/Import/Converter/Registry.php create mode 100644 Classes/Domain/Import/Converter/TouristInformation.php create mode 100644 Classes/Domain/Import/Converter/Town.php create mode 100644 Classes/Domain/Import/Importer.php create mode 100644 Classes/Domain/Import/Importer/FetchData.php create mode 100644 Classes/Domain/Import/Importer/SaveData.php create mode 100644 Classes/Domain/Import/Model/Entity.php create mode 100644 Classes/Domain/Import/Model/GenericEntity.php create mode 100644 Classes/Domain/Import/RequestFactory.php create mode 100644 Classes/Domain/Import/UrlProvider/Registry.php create mode 100644 Classes/Domain/Import/UrlProvider/StaticUrlProvider.php create mode 100644 Classes/Domain/Import/UrlProvider/UrlProvider.php create mode 100644 Classes/Domain/Model/Backend/AbstractEntity.php create mode 100644 Classes/Domain/Model/Backend/ImportConfiguration.php create mode 100644 Classes/Domain/Model/Backend/ImportLog.php create mode 100644 Classes/Domain/Model/Backend/ImportLogEntry.php create mode 100644 Classes/Domain/Model/Backend/Organisation.php create mode 100644 Classes/Domain/Model/Backend/TouristInformation.php create mode 100644 Classes/Domain/Model/Backend/Town.php create mode 100644 Classes/Domain/Repository/Backend/ImportConfigurationRepository.php create mode 100644 Classes/Domain/Repository/Backend/ImportLogRepository.php create mode 100644 Classes/Domain/Repository/Backend/OrganisationRepository.php create mode 100644 Classes/Domain/Repository/Backend/TownRepository.php create mode 100644 Classes/Extension.php create mode 100644 Classes/Typo3Wrapper/TranslationService.php create mode 100644 Classes/View/Backend/Menu.php create mode 100644 Configuration/Extbase/Persistence/Classes.php create mode 100644 Configuration/FlexForm/ImportConfiguration/Static.xml create mode 100644 Configuration/Services.php create mode 100644 Configuration/Services.yaml create mode 100644 Configuration/TCA/tx_thuecat_import_configuration.php create mode 100644 Configuration/TCA/tx_thuecat_import_log.php create mode 100644 Configuration/TCA/tx_thuecat_import_log_entry.php create mode 100644 Configuration/TCA/tx_thuecat_organisation.php create mode 100644 Configuration/TCA/tx_thuecat_tourist_information.php create mode 100644 Configuration/TCA/tx_thuecat_town.php create mode 100644 Resources/Private/Language/locallang.xlf create mode 100644 Resources/Private/Language/locallang_flexform.xlf create mode 100644 Resources/Private/Language/locallang_mod.xlf create mode 100644 Resources/Private/Language/locallang_tca.xlf create mode 100644 Resources/Private/Templates/Backend/Import/Index.html create mode 100644 Resources/Private/Templates/Backend/Overview/Index.html create mode 100644 Tests/Functional/Domain/Import/ImporterTest.php create mode 100644 Tests/Unit/Domain/Import/Converter/OrganisationTest.php create mode 100644 Tests/Unit/Domain/Import/Converter/RegistryTest.php create mode 100644 Tests/Unit/Domain/Import/Converter/TouristInformationTest.php create mode 100644 Tests/Unit/Domain/Import/Converter/TownTest.php create mode 100644 Tests/Unit/Domain/Import/Importer/FetchDataTest.php create mode 100644 Tests/Unit/Domain/Import/ImporterTest.php create mode 100644 Tests/Unit/Domain/Import/Model/GenericEntityTest.php create mode 100644 Tests/Unit/Domain/Import/RequestFactoryTest.php create mode 100644 Tests/Unit/Domain/Import/UrlProvider/RegistryTest.php create mode 100644 Tests/Unit/Domain/Import/UrlProvider/StaticUrlProviderTest.php create mode 100644 Tests/Unit/ExtensionTest.php create mode 100644 ext_tables.php create mode 100644 ext_tables.sql create mode 100644 ext_typoscript_setup.typoscript create mode 100644 phpunit.xml.dist diff --git a/Classes/Controller/Backend/AbstractController.php b/Classes/Controller/Backend/AbstractController.php new file mode 100644 index 0000000..2d6da48 --- /dev/null +++ b/Classes/Controller/Backend/AbstractController.php @@ -0,0 +1,59 @@ + + * + * 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. + */ + +use TYPO3\CMS\Backend\View\BackendTemplateView; +use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; +use TYPO3\CMS\Extbase\Mvc\View\ViewInterface; +use WerkraumMedia\ThueCat\View\Backend\Menu; + +abstract class AbstractController extends ActionController +{ + /** + * BackendTemplateContainer + * + * @var BackendTemplateView + */ + protected $view; + + /** + * Backend Template Container + * + * @var string + */ + protected $defaultViewObjectName = BackendTemplateView::class; + + protected function initializeView(ViewInterface $view) + { + if ($view instanceof BackendTemplateView) { + $this->getMenu()->addMenu( + $view->getModuleTemplate()->getDocHeaderComponent()->getMenuRegistry(), + $this->uriBuilder, + get_class($this) + ); + } + } + + abstract protected function getMenu(): Menu; +} diff --git a/Classes/Controller/Backend/ImportController.php b/Classes/Controller/Backend/ImportController.php new file mode 100644 index 0000000..7a849b3 --- /dev/null +++ b/Classes/Controller/Backend/ImportController.php @@ -0,0 +1,80 @@ + + * + * 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. + */ + +use WerkraumMedia\ThueCat\Domain\Import\Importer; +use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportConfiguration; +use WerkraumMedia\ThueCat\Domain\Repository\Backend\ImportLogRepository; +use WerkraumMedia\ThueCat\Extension; +use WerkraumMedia\ThueCat\Typo3Wrapper\TranslationService; +use WerkraumMedia\ThueCat\View\Backend\Menu; + +class ImportController extends AbstractController +{ + private Importer $importer; + private ImportLogRepository $repository; + private TranslationService $translation; + private Menu $menu; + + public function __construct( + Importer $importer, + ImportLogRepository $repository, + TranslationService $translation, + Menu $menu + ) { + $this->importer = $importer; + $this->repository = $repository; + $this->translation = $translation; + $this->menu = $menu; + } + + public function indexAction(): void + { + $this->view->assignMultiple([ + 'imports' => $this->repository->findAll(), + ]); + } + + public function importAction(ImportConfiguration $importConfiguration): void + { + $this->importer->importConfiguration($importConfiguration); + $this->addFlashMessage( + $this->translation->translate( + 'controller.backend.import.import.success.text', + Extension::EXTENSION_NAME, + [$importConfiguration->getTitle()] + ), + $this->translation->translate( + 'controller.backend.import.import.success.title', + Extension::EXTENSION_NAME + ) + ); + $this->redirect('index', 'Backend\Overview'); + } + + protected function getMenu(): Menu + { + return $this->menu; + } +} diff --git a/Classes/Controller/Backend/OverviewController.php b/Classes/Controller/Backend/OverviewController.php new file mode 100644 index 0000000..39d68c8 --- /dev/null +++ b/Classes/Controller/Backend/OverviewController.php @@ -0,0 +1,58 @@ + + * + * 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. + */ + +use WerkraumMedia\ThueCat\Domain\Repository\Backend\ImportConfigurationRepository; +use WerkraumMedia\ThueCat\Domain\Repository\Backend\OrganisationRepository; +use WerkraumMedia\ThueCat\View\Backend\Menu; + +class OverviewController extends AbstractController +{ + private OrganisationRepository $organisationRepository; + private ImportConfigurationRepository $importConfigurationRepository; + private Menu $menu; + + public function __construct( + OrganisationRepository $organisationRepository, + ImportConfigurationRepository $importConfigurationRepository, + Menu $menu + ) { + $this->organisationRepository = $organisationRepository; + $this->importConfigurationRepository = $importConfigurationRepository; + $this->menu = $menu; + } + + public function indexAction(): void + { + $this->view->assignMultiple([ + 'importConfigurations' => $this->importConfigurationRepository->findAll(), + 'organisations' => $this->organisationRepository->findAll(), + ]); + } + + protected function getMenu(): Menu + { + return $this->menu; + } +} diff --git a/Classes/DependencyInjection/ConverterPass.php b/Classes/DependencyInjection/ConverterPass.php new file mode 100644 index 0000000..966329d --- /dev/null +++ b/Classes/DependencyInjection/ConverterPass.php @@ -0,0 +1,48 @@ + + * + * 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. + */ + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use WerkraumMedia\ThueCat\Domain\Import\Converter\Registry; + +class ConverterPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $registry = $container->findDefinition(Registry::class); + + foreach ($container->findTaggedServiceIds('thuecat.converter') as $id => $tags) { + $definition = $container->findDefinition($id); + if (!$definition->isAutoconfigured() || $definition->isAbstract()) { + continue; + } + + // Services that implement MyCustomInterface need to be public, + // to be lazy loadable by the registry via $container->get() + // $container->findDefinition($id)->setPublic(true); + $registry->addMethodCall('registerConverter', [$definition]); + } + } +} diff --git a/Classes/DependencyInjection/UrlProvidersPass.php b/Classes/DependencyInjection/UrlProvidersPass.php new file mode 100644 index 0000000..7e62d49 --- /dev/null +++ b/Classes/DependencyInjection/UrlProvidersPass.php @@ -0,0 +1,48 @@ + + * + * 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. + */ + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use WerkraumMedia\ThueCat\Domain\Import\UrlProvider\Registry; + +class UrlProvidersPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $registry = $container->findDefinition(Registry::class); + + foreach ($container->findTaggedServiceIds('thuecat.urlprovider') as $id => $tags) { + $definition = $container->findDefinition($id); + if (!$definition->isAutoconfigured() || $definition->isAbstract()) { + continue; + } + + // Services that implement MyCustomInterface need to be public, + // to be lazy loadable by the registry via $container->get() + // $container->findDefinition($id)->setPublic(true); + $registry->addMethodCall('registerProvider', [$definition]); + } + } +} diff --git a/Classes/Domain/Import/Converter/Converter.php b/Classes/Domain/Import/Converter/Converter.php new file mode 100644 index 0000000..01ce484 --- /dev/null +++ b/Classes/Domain/Import/Converter/Converter.php @@ -0,0 +1,37 @@ + + * + * 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. + */ + +use WerkraumMedia\ThueCat\Domain\Import\Model\Entity; + +interface Converter +{ + /** + * A single type is an array of different types. + * All types together identify a specific entity and possible converter. + */ + public function canConvert(array $type): bool; + + public function convert(array $jsonIdOfEntity): Entity; +} diff --git a/Classes/Domain/Import/Converter/Organisation.php b/Classes/Domain/Import/Converter/Organisation.php new file mode 100644 index 0000000..c04769c --- /dev/null +++ b/Classes/Domain/Import/Converter/Organisation.php @@ -0,0 +1,47 @@ + + * + * 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. + */ + +use WerkraumMedia\ThueCat\Domain\Import\Model\GenericEntity; + +class Organisation implements Converter +{ + public function convert(array $jsonIdOfEntity): GenericEntity + { + return new GenericEntity( + 95, + 'tx_thuecat_organisation', + $jsonIdOfEntity['@id'], + [ + 'title' => $jsonIdOfEntity['schema:name']['@value'], + 'description' => $jsonIdOfEntity['schema:description']['@value'], + ] + ); + } + + public function canConvert(array $type): bool + { + return array_search('thuecat:TouristMarketingCompany', $type) !== false; + } +} diff --git a/Classes/Domain/Import/Converter/Registry.php b/Classes/Domain/Import/Converter/Registry.php new file mode 100644 index 0000000..a9421dd --- /dev/null +++ b/Classes/Domain/Import/Converter/Registry.php @@ -0,0 +1,48 @@ + + * + * 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. + */ + +/** + * Central registry of all available converters. + */ +class Registry +{ + private array $converters = []; + + public function registerConverter(Converter $converter): void + { + $this->converters[] = $converter; + } + + public function getConverterBasedOnType(array $type): ?Converter + { + foreach ($this->converters as $converter) { + if ($converter->canConvert($type)) { + return $converter; + } + } + + return null; + } +} diff --git a/Classes/Domain/Import/Converter/TouristInformation.php b/Classes/Domain/Import/Converter/TouristInformation.php new file mode 100644 index 0000000..18c0cd4 --- /dev/null +++ b/Classes/Domain/Import/Converter/TouristInformation.php @@ -0,0 +1,72 @@ + + * + * 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. + */ + +use WerkraumMedia\ThueCat\Domain\Import\Model\GenericEntity; +use WerkraumMedia\ThueCat\Domain\Repository\Backend\OrganisationRepository; +use WerkraumMedia\ThueCat\Domain\Repository\Backend\TownRepository; + +class TouristInformation implements Converter +{ + private OrganisationRepository $organisationRepository; + private TownRepository $townRepository; + + public function __construct( + OrganisationRepository $organisationRepository, + TownRepository $townRepository + ) { + $this->organisationRepository = $organisationRepository; + $this->townRepository = $townRepository; + } + + public function convert(array $jsonIdOfEntity): GenericEntity + { + $manager = $this->organisationRepository->findOneByRemoteId($jsonIdOfEntity['thuecat:managedBy']['@id']); + $town = $this->townRepository->findOneByRemoteIds($this->getContainedInPlaceIds($jsonIdOfEntity)); + + return new GenericEntity( + 95, + 'tx_thuecat_tourist_information', + $jsonIdOfEntity['@id'], + [ + 'title' => $jsonIdOfEntity['schema:name']['@value'], + 'description' => $jsonIdOfEntity['schema:description'][0]['@value'], + 'managed_by' => $manager ? $manager->getUid() : 0, + 'town' => $town ? $town->getUid() : 0, + ] + ); + } + + public function canConvert(array $type): bool + { + return array_search('thuecat:TouristInformation', $type) !== false; + } + + private function getContainedInPlaceIds(array $jsonIdOfEntity): array + { + return array_map(function (array $place) { + return $place['@id']; + }, $jsonIdOfEntity['schema:containedInPlace']); + } +} diff --git a/Classes/Domain/Import/Converter/Town.php b/Classes/Domain/Import/Converter/Town.php new file mode 100644 index 0000000..cf0d922 --- /dev/null +++ b/Classes/Domain/Import/Converter/Town.php @@ -0,0 +1,58 @@ + + * + * 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. + */ + +use WerkraumMedia\ThueCat\Domain\Import\Model\GenericEntity; +use WerkraumMedia\ThueCat\Domain\Repository\Backend\OrganisationRepository; + +class Town implements Converter +{ + private OrganisationRepository $organisationRepository; + + public function __construct( + OrganisationRepository $organisationRepository + ) { + $this->organisationRepository = $organisationRepository; + } + + public function convert(array $jsonIdOfEntity): GenericEntity + { + $manager = $this->organisationRepository->findOneByRemoteId($jsonIdOfEntity['thuecat:managedBy']['@id']); + return new GenericEntity( + 95, + 'tx_thuecat_town', + $jsonIdOfEntity['@id'], + [ + 'title' => $jsonIdOfEntity['schema:name']['@value'], + 'description' => $jsonIdOfEntity['schema:description']['@value'] ?? '', + 'managed_by' => $manager ? $manager->getUid() : 0, + ] + ); + } + + public function canConvert(array $type): bool + { + return array_search('thuecat:Town', $type) !== false; + } +} diff --git a/Classes/Domain/Import/Importer.php b/Classes/Domain/Import/Importer.php new file mode 100644 index 0000000..2c9f20a --- /dev/null +++ b/Classes/Domain/Import/Importer.php @@ -0,0 +1,93 @@ + + * + * 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. + */ + +use TYPO3\CMS\Core\Utility\GeneralUtility; +use WerkraumMedia\ThueCat\Domain\Import\Converter\Converter; +use WerkraumMedia\ThueCat\Domain\Import\Converter\Registry as ConverterRegistry; +use WerkraumMedia\ThueCat\Domain\Import\Importer\FetchData; +use WerkraumMedia\ThueCat\Domain\Import\Importer\SaveData; +use WerkraumMedia\ThueCat\Domain\Import\UrlProvider\Registry as UrlProviderRegistry; +use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportConfiguration; +use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLog; +use WerkraumMedia\ThueCat\Domain\Repository\Backend\ImportLogRepository; + +class Importer +{ + private UrlProviderRegistry $urls; + private ConverterRegistry $converter; + private FetchData $fetchData; + private SaveData $saveData; + private ImportLog $importLog; + private ImportLogRepository $importLogRepository; + + public function __construct( + UrlProviderRegistry $urls, + ConverterRegistry $converter, + ImportLogRepository $importLogRepository, + FetchData $fetchData, + SaveData $saveData + ) { + $this->urls = $urls; + $this->converter = $converter; + $this->importLogRepository = $importLogRepository; + $this->fetchData = $fetchData; + $this->saveData = $saveData; + } + + public function importConfiguration(ImportConfiguration $configuration): ImportLog + { + $this->importLog = GeneralUtility::makeInstance(ImportLog::class, $configuration); + + $urlProvider = $this->urls->getProviderForConfiguration($configuration); + foreach ($urlProvider->getUrls() as $url) { + $this->importResourceByUrl($url); + } + + $this->importLogRepository->addLog($this->importLog); + return clone $this->importLog; + } + + private function importResourceByUrl(string $url): void + { + $content = $this->fetchData->jsonLDFromUrl($url); + + if ($content === []) { + return; + } + + foreach ($content['@graph'] as $jsonEntity) { + $this->importJsonEntity($jsonEntity); + } + } + + private function importJsonEntity(array $jsonEntity): void + { + $converter = $this->converter->getConverterBasedOnType($jsonEntity['@type']); + if ($converter instanceof Converter) { + $this->saveData->import($converter->convert($jsonEntity), $this->importLog); + return; + } + } +} diff --git a/Classes/Domain/Import/Importer/FetchData.php b/Classes/Domain/Import/Importer/FetchData.php new file mode 100644 index 0000000..241c2cc --- /dev/null +++ b/Classes/Domain/Import/Importer/FetchData.php @@ -0,0 +1,51 @@ + + * + * 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. + */ + +use Psr\Http\Client\ClientInterface; +use Psr\Http\Message\RequestFactoryInterface; + +class FetchData +{ + public function __construct( + RequestFactoryInterface $requestFactory, + ClientInterface $httpClient + ) { + $this->requestFactory = $requestFactory; + $this->httpClient = $httpClient; + } + + public function jsonLDFromUrl(string $url): array + { + $request = $this->requestFactory->createRequest('GET', $url); + $response = $this->httpClient->sendRequest($request); + + $json = json_decode((string) $response->getBody(), true); + if (is_array($json)) { + return $json; + } + + return []; + } +} diff --git a/Classes/Domain/Import/Importer/SaveData.php b/Classes/Domain/Import/Importer/SaveData.php new file mode 100644 index 0000000..e9d415a --- /dev/null +++ b/Classes/Domain/Import/Importer/SaveData.php @@ -0,0 +1,102 @@ + + * + * 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. + */ + +use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\DataHandling\DataHandler; +use WerkraumMedia\ThueCat\Domain\Import\Model\Entity; +use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLog; +use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry; + +class SaveData +{ + private DataHandler $dataHandler; + private ConnectionPool $connectionPool; + + public function __construct( + DataHandler $dataHandler, + ConnectionPool $connectionPool + ) { + $this->dataHandler = $dataHandler; + $this->dataHandler->stripslashes_values = 0; + $this->connectionPool = $connectionPool; + } + + public function import(Entity $entity, ImportLog $log): void + { + $dataHandler = clone $this->dataHandler; + + $identifier = $this->getIdentifier($entity); + $dataHandler->start([ + $entity->getTypo3DatabaseTableName() => [ + $identifier => array_merge($entity->getData(), [ + 'pid' => $entity->getTypo3StoragePid(), + 'remote_id' => $entity->getRemoteId(), + ]), + ], + ], []); + $dataHandler->process_datamap(); + + if (isset($dataHandler->substNEWwithIDs[$identifier])) { + $entity->setImportedTypo3Uid($dataHandler->substNEWwithIDs[$identifier]); + } elseif (is_numeric($identifier)) { + $entity->setExistingTypo3Uid((int) $identifier); + } + + $log->addEntry(new ImportLogEntry( + $entity, + $dataHandler->errorLog + )); + } + + private function getIdentifier(Entity $entity): string + { + $existingUid = $this->getExistingUid($entity); + + if ($existingUid > 0) { + return (string) $existingUid; + } + + return 'NEW_1'; + } + + private function getExistingUid(Entity $entity): int + { + $queryBuilder = $this->connectionPool->getQueryBuilderForTable($entity->getTypo3DatabaseTableName()); + $queryBuilder->getRestrictions()->removeAll(); + $queryBuilder->select('uid'); + $queryBuilder->from($entity->getTypo3DatabaseTableName()); + $queryBuilder->where($queryBuilder->expr()->eq( + 'remote_id', + $queryBuilder->createNamedParameter($entity->getRemoteId()) + )); + + $result = $queryBuilder->execute()->fetchColumn(); + if (is_numeric($result)) { + return (int) $result; + } + + return 0; + } +} diff --git a/Classes/Domain/Import/Model/Entity.php b/Classes/Domain/Import/Model/Entity.php new file mode 100644 index 0000000..86a7b70 --- /dev/null +++ b/Classes/Domain/Import/Model/Entity.php @@ -0,0 +1,65 @@ + + * + * 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. + */ + +interface Entity +{ + public function getTypo3StoragePid(): int; + + public function getTypo3DatabaseTableName(): string; + + /** + * Return full remote id as delivered by remote API. + */ + public function getRemoteId(): string; + + /** + * Return db_column_name => db_value. + */ + public function getData(): array; + + /** + * Might be called during import. + * Only in case entitiy already existed. + * Is then called with existing UID from system. + */ + public function setExistingTypo3Uid(int $uid): void; + + /** + * Might be called during import. + * Only in case entitiy didn't exist within system. + * Is then called with new UID from system. + */ + public function setImportedTypo3Uid(int $uid): void; + + /** + * Should be 0 if no uid is known. + */ + public function getTypo3Uid(): int; + + /** + * Must return true in case the entitiy did not exist. + */ + public function wasCreated(): bool; +} diff --git a/Classes/Domain/Import/Model/GenericEntity.php b/Classes/Domain/Import/Model/GenericEntity.php new file mode 100644 index 0000000..2383bdf --- /dev/null +++ b/Classes/Domain/Import/Model/GenericEntity.php @@ -0,0 +1,88 @@ + + * + * 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. + */ + +class GenericEntity implements Entity +{ + private int $typo3StoragePid; + private string $typo3DatabaseTableName; + private bool $created = false; + private int $typo3Uid = 0; + private string $remoteId; + private array $data; + + public function __construct( + int $typo3StoragePid, + string $typo3DatabaseTableName, + string $remoteId, + array $data + ) { + $this->typo3StoragePid = $typo3StoragePid; + $this->typo3DatabaseTableName = $typo3DatabaseTableName; + $this->remoteId = $remoteId; + $this->data = $data; + } + + public function getTypo3StoragePid(): int + { + return $this->typo3StoragePid; + } + + public function getTypo3DatabaseTableName(): string + { + return $this->typo3DatabaseTableName; + } + + public function getRemoteId(): string + { + return $this->remoteId; + } + + public function getData(): array + { + return $this->data; + } + + public function setImportedTypo3Uid(int $uid): void + { + $this->typo3Uid = $uid; + $this->created = true; + } + + public function setExistingTypo3Uid(int $uid): void + { + $this->typo3Uid = $uid; + $this->created = false; + } + + public function getTypo3Uid(): int + { + return $this->typo3Uid; + } + + public function wasCreated(): bool + { + return $this->created; + } +} diff --git a/Classes/Domain/Import/RequestFactory.php b/Classes/Domain/Import/RequestFactory.php new file mode 100644 index 0000000..e6e80e4 --- /dev/null +++ b/Classes/Domain/Import/RequestFactory.php @@ -0,0 +1,41 @@ + + * + * 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. + */ + +use Psr\Http\Message\RequestInterface; +use TYPO3\CMS\Core\Http\RequestFactory as Typo3RequestFactory; +use TYPO3\CMS\Core\Http\Uri; + +class RequestFactory extends Typo3RequestFactory +{ + public function createRequest(string $method, $uri): RequestInterface + { + $uri = new Uri($uri); + $uri = $uri->withQuery('?format=jsonId'); + + // TODO: Add api key from site + + return parent::createRequest($method, $uri); + } +} diff --git a/Classes/Domain/Import/UrlProvider/Registry.php b/Classes/Domain/Import/UrlProvider/Registry.php new file mode 100644 index 0000000..87859db --- /dev/null +++ b/Classes/Domain/Import/UrlProvider/Registry.php @@ -0,0 +1,53 @@ + + * + * 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. + */ + +use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportConfiguration; + +/** + * Central registry of all available url provider. + */ +class Registry +{ + private array $provider = []; + + public function registerProvider(UrlProvider $provider): void + { + $this->provider[] = $provider; + } + + /** + * @return UrlProvider[] + */ + public function getProviderForConfiguration(ImportConfiguration $configuration): ?UrlProvider + { + foreach ($this->provider as $provider) { + if ($provider->canProvideForConfiguration($configuration)) { + return $provider->createWithConfiguration($configuration); + } + } + + return null; + } +} diff --git a/Classes/Domain/Import/UrlProvider/StaticUrlProvider.php b/Classes/Domain/Import/UrlProvider/StaticUrlProvider.php new file mode 100644 index 0000000..77228af --- /dev/null +++ b/Classes/Domain/Import/UrlProvider/StaticUrlProvider.php @@ -0,0 +1,57 @@ + + * + * 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. + */ + +use TYPO3\CMS\Core\Utility\GeneralUtility; +use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportConfiguration; + +class StaticUrlProvider implements UrlProvider +{ + private array $urls = []; + + public function __construct( + ImportConfiguration $configuration + ) { + if ($configuration instanceof ImportConfiguration) { + $this->urls = $configuration->getUrls(); + } + } + + public function canProvideForConfiguration( + ImportConfiguration $configuration + ): bool { + return $configuration->getType() === 'static'; + } + + public function createWithConfiguration( + ImportConfiguration $configuration + ): UrlProvider { + return GeneralUtility::makeInstance(self::class, $configuration); + } + + public function getUrls(): array + { + return $this->urls; + } +} diff --git a/Classes/Domain/Import/UrlProvider/UrlProvider.php b/Classes/Domain/Import/UrlProvider/UrlProvider.php new file mode 100644 index 0000000..215a48d --- /dev/null +++ b/Classes/Domain/Import/UrlProvider/UrlProvider.php @@ -0,0 +1,38 @@ + + * + * 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. + */ + +use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportConfiguration; + +interface UrlProvider +{ + /** + * @var string[] + */ + public function getUrls(): array; + + public function canProvideForConfiguration(ImportConfiguration $configuration): bool; + + public function createWithConfiguration(ImportConfiguration $configuration): UrlProvider; +} diff --git a/Classes/Domain/Model/Backend/AbstractEntity.php b/Classes/Domain/Model/Backend/AbstractEntity.php new file mode 100644 index 0000000..9741550 --- /dev/null +++ b/Classes/Domain/Model/Backend/AbstractEntity.php @@ -0,0 +1,54 @@ + + * + * 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. + */ + +use TYPO3\CMS\Extbase\DomainObject\AbstractEntity as Typo3AbstractEntity; + +class AbstractEntity extends Typo3AbstractEntity +{ + protected string $remoteId = ''; + protected string $title = ''; + protected string $description = ''; + protected ?\DateTimeImmutable $tstamp = null; + + public function getRemoteId(): string + { + return $this->remoteId; + } + + public function getTitle(): string + { + return $this->title; + } + + public function getDescription(): string + { + return $this->description; + } + + public function getLastImported(): ?\DateTimeImmutable + { + return $this->tstamp; + } +} diff --git a/Classes/Domain/Model/Backend/ImportConfiguration.php b/Classes/Domain/Model/Backend/ImportConfiguration.php new file mode 100644 index 0000000..3674ed4 --- /dev/null +++ b/Classes/Domain/Model/Backend/ImportConfiguration.php @@ -0,0 +1,77 @@ + + * + * 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. + */ + +use TYPO3\CMS\Core\Utility\ArrayUtility; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\DomainObject\AbstractEntity; + +class ImportConfiguration extends AbstractEntity +{ + protected string $title = ''; + protected string $type = ''; + protected string $configuration = ''; + protected ?\DateTimeImmutable $tstamp = null; + + public function getTitle(): string + { + return $this->title; + } + + public function getType(): string + { + return $this->type; + } + + public function getTableName(): string + { + return 'tx_thuecat_import_configuration'; + } + + public function getLastChanged(): ?\DateTimeImmutable + { + return $this->tstamp; + } + + public function getUrls(): array + { + if ($this->configuration === '') { + return []; + } + + $entries = array_map(function (array $urlEntry) { + return ArrayUtility::getValueByPath($urlEntry, 'url/el/url/vDEF'); + }, $this->getEntries()); + + return array_values($entries); + } + + private function getEntries(): array + { + return ArrayUtility::getValueByPath( + GeneralUtility::xml2array($this->configuration), + 'data/sDEF/lDEF/urls/el' + ); + } +} diff --git a/Classes/Domain/Model/Backend/ImportLog.php b/Classes/Domain/Model/Backend/ImportLog.php new file mode 100644 index 0000000..1a86914 --- /dev/null +++ b/Classes/Domain/Model/Backend/ImportLog.php @@ -0,0 +1,114 @@ + + * + * 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. + */ + +use TYPO3\CMS\Extbase\DomainObject\AbstractEntity as Typo3AbstractEntity; +use TYPO3\CMS\Extbase\Persistence\ObjectStorage; + +class ImportLog extends Typo3AbstractEntity +{ + /** + * @var ObjectStorage + */ + protected $logEntries = []; + + /** + * @var ImportConfiguration + */ + protected $configuration; + + /** + * @var \DateTimeImmutable|null + */ + protected $crdate; + + public function __construct( + ImportConfiguration $configuration + ) { + $this->logEntries = new ObjectStorage(); + $this->configuration = $configuration; + } + + public function addEntry(ImportLogEntry $entry): void + { + $this->logEntries->attach($entry); + } + + public function getConfiguration(): ImportConfiguration + { + return $this->configuration; + } + + /** + * @return ObjectStorage + */ + public function getEntries(): ObjectStorage + { + return $this->logEntries; + } + + public function getCreated(): ?\DateTimeImmutable + { + return $this->crdate; + } + + public function getListOfErrors(): array + { + $errors = []; + + foreach ($this->getEntries() as $entry) { + if ($entry->hasErrors()) { + $errors = array_merge($errors, $entry->getErrors()); + } + } + + return $errors; + } + + public function hasErrors(): bool + { + foreach ($this->getEntries() as $entry) { + if ($entry->hasErrors()) { + return true; + } + } + + return false; + } + + public function getSummaryOfEntries(): array + { + $summary = []; + + foreach ($this->getEntries() as $entry) { + if (isset($summary[$entry->getRecordDatabaseTableName()])) { + ++$summary[$entry->getRecordDatabaseTableName()]; + continue; + } + $summary[$entry->getRecordDatabaseTableName()] = 1; + } + + return $summary; + } +} diff --git a/Classes/Domain/Model/Backend/ImportLogEntry.php b/Classes/Domain/Model/Backend/ImportLogEntry.php new file mode 100644 index 0000000..afdda84 --- /dev/null +++ b/Classes/Domain/Model/Backend/ImportLogEntry.php @@ -0,0 +1,97 @@ + + * + * 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. + */ + +use TYPO3\CMS\Extbase\DomainObject\AbstractEntity as Typo3AbstractEntity; +use WerkraumMedia\ThueCat\Domain\Import\Model\Entity; + +class ImportLogEntry extends Typo3AbstractEntity +{ + /** + * @var bool + */ + protected $insertion = false; + + /** + * @var int + */ + protected $recordUid = 0; + + /** + * @var int + */ + protected $recordPid = 0; + + /** + * @var string + */ + protected $tableName = ''; + + /** + * @var string + */ + protected $errors = ''; + + protected array $errorsAsArray = []; + + public function __construct( + Entity $entity, + array $dataHandlerErrorLog + ) { + $this->insertion = $entity->wasCreated(); + $this->recordUid = $entity->getTypo3Uid(); + $this->recordPid = $entity->getTypo3StoragePid(); + $this->tableName = $entity->getTypo3DatabaseTableName(); + $this->errorsAsArray = $dataHandlerErrorLog; + } + + public function wasInsertion(): bool + { + return $this->insertion; + } + + public function getRecordUid(): int + { + return $this->recordUid; + } + + public function getRecordDatabaseTableName(): string + { + return $this->tableName; + } + + public function getErrors(): array + { + if ($this->errorsAsArray === [] && $this->errors !== '') { + $this->errorsAsArray = json_decode($this->errors, true); + } + + return $this->errorsAsArray; + } + + public function hasErrors(): bool + { + return $this->getErrors() !== []; + } +} diff --git a/Classes/Domain/Model/Backend/Organisation.php b/Classes/Domain/Model/Backend/Organisation.php new file mode 100644 index 0000000..0ff984d --- /dev/null +++ b/Classes/Domain/Model/Backend/Organisation.php @@ -0,0 +1,54 @@ + + * + * 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. + */ + +use TYPO3\CMS\Extbase\Persistence\ObjectStorage; + +class Organisation extends AbstractEntity +{ + /** + * @var ObjectStorage + */ + protected $managesTowns; + + /** + * @var ObjectStorage + */ + protected $managesTouristInformation; + + public function getManagesTowns(): ObjectStorage + { + return $this->managesTowns; + } + + public function getManagesTouristInformation(): ObjectStorage + { + return $this->managesTouristInformation; + } + + public function getTableName(): string + { + return 'tx_thuecat_organisation'; + } +} diff --git a/Classes/Domain/Model/Backend/TouristInformation.php b/Classes/Domain/Model/Backend/TouristInformation.php new file mode 100644 index 0000000..ba7bb67 --- /dev/null +++ b/Classes/Domain/Model/Backend/TouristInformation.php @@ -0,0 +1,28 @@ + + * + * 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. + */ + +class TouristInformation extends AbstractEntity +{ +} diff --git a/Classes/Domain/Model/Backend/Town.php b/Classes/Domain/Model/Backend/Town.php new file mode 100644 index 0000000..6f40178 --- /dev/null +++ b/Classes/Domain/Model/Backend/Town.php @@ -0,0 +1,39 @@ + + * + * 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. + */ + +use TYPO3\CMS\Extbase\Persistence\ObjectStorage; + +class Town extends AbstractEntity +{ + /** + * @var ObjectStorage + */ + protected $touristInformation; + + public function getTouristInformation(): ObjectStorage + { + return $this->touristInformation; + } +} diff --git a/Classes/Domain/Repository/Backend/ImportConfigurationRepository.php b/Classes/Domain/Repository/Backend/ImportConfigurationRepository.php new file mode 100644 index 0000000..8eaec0a --- /dev/null +++ b/Classes/Domain/Repository/Backend/ImportConfigurationRepository.php @@ -0,0 +1,42 @@ + + * + * 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. + */ + +use TYPO3\CMS\Extbase\Object\ObjectManagerInterface; +use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings; +use TYPO3\CMS\Extbase\Persistence\Repository; + +class ImportConfigurationRepository extends Repository +{ + public function __construct( + ObjectManagerInterface $objectManager, + Typo3QuerySettings $querySettings + ) { + parent::__construct($objectManager); + + $querySettings->setRespectStoragePage(false); + + $this->setDefaultQuerySettings($querySettings); + } +} diff --git a/Classes/Domain/Repository/Backend/ImportLogRepository.php b/Classes/Domain/Repository/Backend/ImportLogRepository.php new file mode 100644 index 0000000..f1f54a3 --- /dev/null +++ b/Classes/Domain/Repository/Backend/ImportLogRepository.php @@ -0,0 +1,90 @@ + + * + * 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. + */ + +use TYPO3\CMS\Core\DataHandling\DataHandler; +use TYPO3\CMS\Extbase\Object\ObjectManagerInterface; +use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings; +use TYPO3\CMS\Extbase\Persistence\QueryInterface; +use TYPO3\CMS\Extbase\Persistence\Repository; +use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLog; + +class ImportLogRepository extends Repository +{ + private DataHandler $dataHandler; + + public function __construct( + ObjectManagerInterface $objectManager, + DataHandler $dataHandler, + Typo3QuerySettings $querySettings + ) { + parent::__construct($objectManager); + + $this->dataHandler = $dataHandler; + $this->dataHandler->stripslashes_values = 0; + + $querySettings->setRespectStoragePage(false); + $this->setDefaultQuerySettings($querySettings); + + $this->setDefaultOrderings([ + 'crdate' => QueryInterface::ORDER_DESCENDING, + ]); + } + + public function addLog(ImportLog $log): void + { + $dataHandler = clone $this->dataHandler; + $dataHandler->start([ + 'tx_thuecat_import_log' => [ + 'NEW0' => [ + 'pid' => 0, + 'configuration' => $log->getConfiguration()->getUid(), + ], + ], + 'tx_thuecat_import_log_entry' => $this->getLogEntries($log), + ], []); + $dataHandler->process_datamap(); + } + + private function getLogEntries(ImportLog $log): array + { + $number = 1; + $entries = []; + + foreach ($log->getEntries() as $entry) { + $number++; + + $entries['NEW' . $number] = [ + 'pid' => 0, + 'import_log' => 'NEW0', + 'insertion' => $entry->wasInsertion(), + 'record_uid' => $entry->getRecordUid(), + 'table_name' => $entry->getRecordDatabaseTableName(), + 'errors' => json_encode($entry->getErrors()), + ]; + } + + return $entries; + } +} diff --git a/Classes/Domain/Repository/Backend/OrganisationRepository.php b/Classes/Domain/Repository/Backend/OrganisationRepository.php new file mode 100644 index 0000000..741fe0b --- /dev/null +++ b/Classes/Domain/Repository/Backend/OrganisationRepository.php @@ -0,0 +1,44 @@ + + * + * 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. + */ + +use TYPO3\CMS\Extbase\Object\ObjectManagerInterface; +use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings; +use TYPO3\CMS\Extbase\Persistence\Repository; + +/** + * @method Organisation|null findOneByRemoteId(string $remoteId) + */ +class OrganisationRepository extends Repository +{ + public function __construct( + ObjectManagerInterface $objectManager, + Typo3QuerySettings $querySettings + ) { + parent::__construct($objectManager); + + $querySettings->setRespectStoragePage(false); + $this->setDefaultQuerySettings($querySettings); + } +} diff --git a/Classes/Domain/Repository/Backend/TownRepository.php b/Classes/Domain/Repository/Backend/TownRepository.php new file mode 100644 index 0000000..1ea6c1d --- /dev/null +++ b/Classes/Domain/Repository/Backend/TownRepository.php @@ -0,0 +1,56 @@ + + * + * 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. + */ + +use TYPO3\CMS\Extbase\Object\ObjectManagerInterface; +use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings; +use TYPO3\CMS\Extbase\Persistence\Repository; +use WerkraumMedia\ThueCat\Domain\Model\Backend\Town; + +/** + * @method Town|null findOneByRemoteIds(string[] $remoteIds) + */ +class TownRepository extends Repository +{ + public function __construct( + ObjectManagerInterface $objectManager, + Typo3QuerySettings $querySettings + ) { + parent::__construct($objectManager); + + $querySettings->setRespectStoragePage(false); + + $this->setDefaultQuerySettings($querySettings); + } + + public function findOneByRemoteIds(array $remoteIds): ?Town + { + $query = $this->createQuery(); + + $query->in('remoteId', $remoteIds); + $query->setLimit(1); + + return $query->execute()->getFirst(); + } +} diff --git a/Classes/Extension.php b/Classes/Extension.php new file mode 100644 index 0000000..c717285 --- /dev/null +++ b/Classes/Extension.php @@ -0,0 +1,59 @@ + + * + * 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. + */ + +use TYPO3\CMS\Extbase\Utility\ExtensionUtility; +use WerkraumMedia\ThueCat\Controller\Backend\ImportController; +use WerkraumMedia\ThueCat\Controller\Backend\OverviewController; + +class Extension +{ + public const EXTENSION_KEY = 'thuecat'; + + public const EXTENSION_NAME = 'Thuecat'; + + public static function getLanguagePath(): string + { + return 'LLL:EXT:' . self::EXTENSION_KEY . '/Resources/Private/Language/'; + } + + public static function registerBackendModules(): void + { + ExtensionUtility::registerModule( + self::EXTENSION_NAME, + 'site', + 'thuecat', + '', + [ + OverviewController::class => 'index', + ImportController::class => 'import, index', + ], + [ + 'access' => 'user,group', + 'icon' => 'EXT:' . self::EXTENSION_KEY . '/Resources/Public/Icons/module.svg', + 'labels' => self::getLanguagePath() . 'locallang_mod.xlf', + ] + ); + } +} diff --git a/Classes/Typo3Wrapper/TranslationService.php b/Classes/Typo3Wrapper/TranslationService.php new file mode 100644 index 0000000..2b2a338 --- /dev/null +++ b/Classes/Typo3Wrapper/TranslationService.php @@ -0,0 +1,37 @@ + + * + * 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. + */ + +use TYPO3\CMS\Extbase\Utility\LocalizationUtility; + +class TranslationService +{ + public function translate( + string $id, + string $extensionName, + array $arguments = null + ): string { + return LocalizationUtility::translate($id, $extensionName, $arguments) ?? ''; + } +} diff --git a/Classes/View/Backend/Menu.php b/Classes/View/Backend/Menu.php new file mode 100644 index 0000000..6478446 --- /dev/null +++ b/Classes/View/Backend/Menu.php @@ -0,0 +1,65 @@ + + * + * 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. + */ + +use TYPO3\CMS\Backend\Template\Components\MenuRegistry; +use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder; +use WerkraumMedia\ThueCat\Controller\Backend\ImportController; +use WerkraumMedia\ThueCat\Controller\Backend\OverviewController; +use WerkraumMedia\ThueCat\Extension; +use WerkraumMedia\ThueCat\Typo3Wrapper\TranslationService; + +class Menu +{ + private TranslationService $translation; + + public function __construct( + TranslationService $translation + ) { + $this->translation = $translation; + } + + public function addMenu( + MenuRegistry $registry, + UriBuilder $uriBuilder, + string $controllerClassName + ): void { + $menu = $registry->makeMenu(); + $menu->setIdentifier('action'); + + $menuItem = $menu->makeMenuItem(); + $menuItem->setTitle($this->translation->translate('module.overview.headline', Extension::EXTENSION_NAME)); + $menuItem->setHref($uriBuilder->reset()->uriFor('index', [], 'Backend\Overview')); + $menuItem->setActive($controllerClassName === OverviewController::class); + $menu->addMenuItem($menuItem); + + $menuItem = $menu->makeMenuItem(); + $menuItem->setTitle($this->translation->translate('module.imports.headline', Extension::EXTENSION_NAME)); + $menuItem->setHref($uriBuilder->reset()->uriFor('index', [], 'Backend\Import')); + $menuItem->setActive($controllerClassName === ImportController::class); + $menu->addMenuItem($menuItem); + + $registry->addMenu($menu); + } +} diff --git a/Configuration/Extbase/Persistence/Classes.php b/Configuration/Extbase/Persistence/Classes.php new file mode 100644 index 0000000..280fbe1 --- /dev/null +++ b/Configuration/Extbase/Persistence/Classes.php @@ -0,0 +1,22 @@ + [ + 'tableName' => 'tx_thuecat_organisation', + ], + \WerkraumMedia\ThueCat\Domain\Model\Backend\Town::class => [ + 'tableName' => 'tx_thuecat_town', + ], + \WerkraumMedia\ThueCat\Domain\Model\Backend\TouristInformation::class => [ + 'tableName' => 'tx_thuecat_tourist_information', + ], + \WerkraumMedia\ThueCat\Domain\Model\Backend\ImportConfiguration::class => [ + 'tableName' => 'tx_thuecat_import_configuration', + ], + \WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLog::class => [ + 'tableName' => 'tx_thuecat_import_log', + ], + \WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLogEntry::class => [ + 'tableName' => 'tx_thuecat_import_log_entry', + ], +]; diff --git a/Configuration/FlexForm/ImportConfiguration/Static.xml b/Configuration/FlexForm/ImportConfiguration/Static.xml new file mode 100644 index 0000000..33a7d47 --- /dev/null +++ b/Configuration/FlexForm/ImportConfiguration/Static.xml @@ -0,0 +1,39 @@ + + + 1 + + + + + + LLL:EXT:thuecat/Resources/Private/Language/locallang_flexform.xlf:importConfiguration.static.sheetTitle + + array + + + LLL:EXT:thuecat/Resources/Private/Language/locallang_flexform.xlf:importConfiguration.static.urls +
1
+ array + + + LLL:EXT:thuecat/Resources/Private/Language/locallang_flexform.xlf:importConfiguration.static.url + array + + + + + + input + required,trim + + + + + + +
+
+
+
+
+
diff --git a/Configuration/Services.php b/Configuration/Services.php new file mode 100644 index 0000000..8400cbc --- /dev/null +++ b/Configuration/Services.php @@ -0,0 +1,18 @@ +registerForAutoconfiguration(UrlProvider::class)->addTag('thuecat.urlprovider'); + $containerBuilder->addCompilerPass(new DependencyInjection\UrlProvidersPass('thuecat.urlprovider')); + + $containerBuilder->registerForAutoconfiguration(Converter::class)->addTag('thuecat.converter'); + $containerBuilder->addCompilerPass(new DependencyInjection\ConverterPass('thuecat.converter')); +}; diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml new file mode 100644 index 0000000..7820594 --- /dev/null +++ b/Configuration/Services.yaml @@ -0,0 +1,12 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + WerkraumMedia\ThueCat\: + resource: '../Classes/*' + + WerkraumMedia\ThueCat\Domain\Import\Importer\FetchData: + arguments: + $requestFactory: '@WerkraumMedia\ThueCat\Domain\Import\RequestFactory' diff --git a/Configuration/SiteConfiguration/Overrides/sites.php b/Configuration/SiteConfiguration/Overrides/sites.php index 6dcc6ed..7b392ff 100644 --- a/Configuration/SiteConfiguration/Overrides/sites.php +++ b/Configuration/SiteConfiguration/Overrides/sites.php @@ -1,5 +1,7 @@ [ + 'label' => 'title', + 'default_sortby' => 'title', + 'tstamp' => 'tstamp', + 'crdate' => 'crdate', + 'cruser_id' => 'cruser_id', + 'title' => $languagePath, + 'enablecolumns' => [ + 'disabled' => 'disable', + ], + 'searchFields' => 'title', + 'rootLevel' => 1, + ], + 'columns' => [ + 'title' => [ + 'label' => $languagePath . '.title', + 'config' => [ + 'type' => 'input', + 'max' => 255, + 'eval' => 'required,trim,unique', + ], + ], + 'type' => [ + 'label' => $languagePath . '.type', + 'config' => [ + 'type' => 'select', + 'renderType' => 'selectSingle', + 'items' => [ + [ + $languagePath . '.type.static', + 'static', + ], + ], + ], + ], + 'configuration' => [ + 'label' => $languagePath . '.configuration', + 'config' => [ + 'type' => 'flex', + 'ds_pointerField' => 'type', + 'ds' => [ + 'default' => $flexFormConfigurationPath . 'ImportConfiguration/Static.xml', + 'static' => $flexFormConfigurationPath . 'ImportConfiguration/Static.xml', + ], + ], + ], + 'tstamp' => [ + 'config' => [ + 'type' => 'input', + 'renderType' => 'inputDateTime', + 'eval' => 'datetime', + 'readOnly' => true, + ], + ], + ], + 'types' => [ + '0' => [ + 'showitem' => 'title, type, configuration', + ], + ], + ]; +})(\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY, 'tx_thuecat_import_configuration'); diff --git a/Configuration/TCA/tx_thuecat_import_log.php b/Configuration/TCA/tx_thuecat_import_log.php new file mode 100644 index 0000000..0126abc --- /dev/null +++ b/Configuration/TCA/tx_thuecat_import_log.php @@ -0,0 +1,57 @@ + [ + 'label' => 'crdate', + 'default_sortby' => 'crdate', + 'tstamp' => 'tstamp', + 'crdate' => 'crdate', + 'cruser_id' => 'cruser_id', + 'title' => $languagePath, + 'enablecolumns' => [ + 'disabled' => 'disable', + ], + 'rootLevel' => 1, + ], + 'columns' => [ + 'configuration' => [ + 'label' => $languagePath . '.configuration', + 'config' => [ + 'type' => 'select', + 'renderType' => 'selectSingle', + 'foreign_table' => 'tx_thuecat_import_configuration', + 'readOnly' => true, + ], + ], + 'log_entries' => [ + 'label' => $languagePath . '.log_entries', + 'config' => [ + 'type' => 'inline', + 'foreign_table' => 'tx_thuecat_import_log_entry', + 'foreign_field' => 'import_log', + 'readOnly' => true, + ], + ], + 'crdate' => [ + 'label' => $languagePath . '.crdate', + 'config' => [ + 'type' => 'input', + 'renderType' => 'inputDateTime', + 'eval' => 'datetime', + 'readOnly' => true, + ], + ], + ], + 'types' => [ + '0' => [ + 'showitem' => 'crdate, log_entries, configuration', + ], + ], + ]; +})(\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY, 'tx_thuecat_import_log'); diff --git a/Configuration/TCA/tx_thuecat_import_log_entry.php b/Configuration/TCA/tx_thuecat_import_log_entry.php new file mode 100644 index 0000000..533dd63 --- /dev/null +++ b/Configuration/TCA/tx_thuecat_import_log_entry.php @@ -0,0 +1,88 @@ + [ + 'label' => 'table_name', + 'label_alt' => 'record_uid', + 'label_alt_force' => true, + 'default_sortby' => 'title', + 'tstamp' => 'tstamp', + 'crdate' => 'crdate', + 'cruser_id' => 'cruser_id', + 'title' => $languagePath, + 'enablecolumns' => [ + 'disabled' => 'disable', + ], + 'rootLevel' => 1, + 'hideTable' => true, + ], + 'columns' => [ + 'insertion' => [ + 'label' => $languagePath . '.insertion', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxLabeledToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'labelChecked' => $languagePath . '.insertion.yes', + 'labelUnchecked' => $languagePath . '.insertion.no', + ], + ], + 'readOnly' => true, + ], + ], + 'table_name' => [ + 'label' => $languagePath . '.table_name', + 'config' => [ + 'type' => 'input', + 'readOnly' => true, + ], + ], + 'record_uid' => [ + 'label' => $languagePath . '.record_uid', + 'config' => [ + 'type' => 'input', + 'readOnly' => true, + ], + ], + 'errors' => [ + 'label' => $languagePath . '.errors', + 'config' => [ + 'type' => 'text', + 'readOnly' => true, + ], + ], + 'import_log' => [ + 'label' => $languagePath . '.import_log', + 'config' => [ + 'type' => 'select', + 'renderType' => 'selectSingle', + 'foreign_table' => 'tx_thuecat_import_log', + 'readOnly' => true, + ], + ], + 'crdate' => [ + 'label' => $languagePath . '.crdate', + 'config' => [ + 'type' => 'input', + 'renderType' => 'inputDateTime', + 'eval' => 'datetime', + 'readOnly' => true, + ], + ], + ], + 'types' => [ + '0' => [ + 'showitem' => 'table_name, record_uid, insertion, errors, import_log, crdate', + ], + ], + ]; +})(\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY, 'tx_thuecat_import_log_entry'); diff --git a/Configuration/TCA/tx_thuecat_organisation.php b/Configuration/TCA/tx_thuecat_organisation.php new file mode 100644 index 0000000..e10be7e --- /dev/null +++ b/Configuration/TCA/tx_thuecat_organisation.php @@ -0,0 +1,81 @@ + [ + 'label' => 'title', + 'default_sortby' => 'title', + 'tstamp' => 'tstamp', + 'crdate' => 'crdate', + 'cruser_id' => 'cruser_id', + 'title' => $languagePath, + 'enablecolumns' => [ + 'disabled' => 'disable', + ], + 'searchFields' => 'title', + ], + 'columns' => [ + 'title' => [ + 'label' => $languagePath . '.title', + 'config' => [ + 'type' => 'input', + 'size' => 20, + 'max' => 255, + 'readOnly' => true, + ], + ], + 'description' => [ + 'label' => $languagePath . '.description', + 'config' => [ + 'type' => 'text', + 'readOnly' => true, + ], + ], + 'remote_id' => [ + 'label' => $languagePath . '.remote_id', + 'config' => [ + 'type' => 'input', + 'readOnly' => true, + ], + ], + 'manages_towns' => [ + 'label' => $languagePath . '.manages_towns', + 'config' => [ + 'type' => 'inline', + 'foreign_table' => 'tx_thuecat_town', + 'foreign_field' => 'managed_by', + 'readOnly' => true, + ], + ], + 'manages_tourist_information' => [ + 'label' => $languagePath . '.manages_tourist_information', + 'config' => [ + 'type' => 'inline', + 'foreign_table' => 'tx_thuecat_tourist_information', + 'foreign_field' => 'managed_by', + 'readOnly' => true, + ], + ], + 'tstamp' => [ + 'label' => $languagePath . '.tstamp', + 'config' => [ + 'type' => 'input', + 'renderType' => 'inputDateTime', + 'eval' => 'datetime', + 'readOnly' => true, + ], + ], + ], + 'types' => [ + '0' => [ + 'showitem' => 'title, description, remote_id, tstamp' + . ',--div--;' . $languagePath . '.div.manages' + . ',manages_towns, manages_tourist_information', + ], + ], + ]; +})(\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY, 'tx_thuecat_organisation'); diff --git a/Configuration/TCA/tx_thuecat_tourist_information.php b/Configuration/TCA/tx_thuecat_tourist_information.php new file mode 100644 index 0000000..dfa24ce --- /dev/null +++ b/Configuration/TCA/tx_thuecat_tourist_information.php @@ -0,0 +1,82 @@ + [ + 'label' => 'title', + 'default_sortby' => 'title', + 'tstamp' => 'tstamp', + 'crdate' => 'crdate', + 'cruser_id' => 'cruser_id', + 'title' => $languagePath, + 'enablecolumns' => [ + 'disabled' => 'disable', + ], + 'searchFields' => 'title', + ], + 'columns' => [ + 'title' => [ + 'label' => $languagePath . '.title', + 'config' => [ + 'type' => 'input', + 'size' => 20, + 'max' => 255, + 'readOnly' => true, + ], + ], + 'description' => [ + 'label' => $languagePath . '.description', + 'config' => [ + 'type' => 'text', + 'readOnly' => true, + ], + ], + 'remote_id' => [ + 'label' => $languagePath . '.remote_id', + 'config' => [ + 'type' => 'input', + 'readOnly' => true, + ], + ], + 'town' => [ + 'label' => $languagePath . '.town', + 'config' => [ + 'type' => 'select', + 'renderType' => 'selectSingle', + 'foreign_table' => 'tx_thuecat_town', + 'items' => [ + [ + $languagePath . '.town.unkown', + 0, + ], + ], + 'readOnly' => true, + ], + ], + 'managed_by' => [ + 'label' => $languagePath . '.managed_by', + 'config' => [ + 'type' => 'select', + 'renderType' => 'selectSingle', + 'foreign_table' => 'tx_thuecat_organisation', + 'items' => [ + [ + $languagePath . '.managed_by.unkown', + 0, + ], + ], + 'readOnly' => true, + ], + ], + ], + 'types' => [ + '0' => [ + 'showitem' => 'title, description, remote_id, town, managed_by', + ], + ], + ]; +})(\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY, 'tx_thuecat_tourist_information'); diff --git a/Configuration/TCA/tx_thuecat_town.php b/Configuration/TCA/tx_thuecat_town.php new file mode 100644 index 0000000..f39e3d1 --- /dev/null +++ b/Configuration/TCA/tx_thuecat_town.php @@ -0,0 +1,76 @@ + [ + 'label' => 'title', + 'default_sortby' => 'title', + 'tstamp' => 'tstamp', + 'crdate' => 'crdate', + 'cruser_id' => 'cruser_id', + 'title' => $languagePath, + 'enablecolumns' => [ + 'disabled' => 'disable', + ], + 'searchFields' => 'title', + ], + 'columns' => [ + 'title' => [ + 'label' => $languagePath . '.title', + 'config' => [ + 'type' => 'input', + 'size' => 20, + 'max' => 255, + 'readOnly' => true, + ], + ], + 'description' => [ + 'label' => $languagePath . '.description', + 'config' => [ + 'type' => 'text', + 'readOnly' => true, + ], + ], + 'remote_id' => [ + 'label' => $languagePath . '.remote_id', + 'config' => [ + 'type' => 'input', + 'readOnly' => true, + ], + ], + 'managed_by' => [ + 'label' => $languagePath . '.managed_by', + 'config' => [ + 'type' => 'select', + 'renderType' => 'selectSingle', + 'foreign_table' => 'tx_thuecat_organisation', + 'items' => [ + [ + $languagePath . '.managed_by.unkown', + 0, + ], + ], + 'readOnly' => true, + ], + ], + 'tourist_information' => [ + 'label' => $languagePath . '.tourist_information', + 'config' => [ + 'type' => 'inline', + 'foreign_table' => 'tx_thuecat_tourist_information', + 'foreign_field' => 'town', + 'readOnly' => true, + ], + ], + ], + 'types' => [ + '0' => [ + 'showitem' => 'title, description, remote_id, tourist_information, managed_by', + ], + ], + ]; +})(\WerkraumMedia\ThueCat\Extension::EXTENSION_KEY, 'tx_thuecat_town'); diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf new file mode 100644 index 0000000..ad9e211 --- /dev/null +++ b/Resources/Private/Language/locallang.xlf @@ -0,0 +1,102 @@ + + + +
+ + + Actions + + + ThüCAT - Overview + + + + Import Configurations + + + None configured + + + Title + + + Last changed + + + create the first to get started.]]> + + + Edit import configuration + + + Create new import configuration + + + Import based on import configuration + + + + Organisations + + + None imported + + + Please provide an import configuration and trigger import to create an organisation. + + + Organisation + + + Towns + + + - + + + Last imported + + + + ThüCAT - Imports + + + No imports + + + No import was executed yet. + + + Created + + + Number of records + + + Configuration + + + Errors + + + Summary + + + Organisation + + + Town + + + Tourist Information + + + + Import finished + + + Imported configuration "%1$s". + + + + diff --git a/Resources/Private/Language/locallang_be.xlf b/Resources/Private/Language/locallang_be.xlf index 9a52e1e..b632daf 100644 --- a/Resources/Private/Language/locallang_be.xlf +++ b/Resources/Private/Language/locallang_be.xlf @@ -1,6 +1,6 @@ - +
diff --git a/Resources/Private/Language/locallang_flexform.xlf b/Resources/Private/Language/locallang_flexform.xlf new file mode 100644 index 0000000..2264517 --- /dev/null +++ b/Resources/Private/Language/locallang_flexform.xlf @@ -0,0 +1,17 @@ + + + +
+ + + Static import configuration + + + URLs + + + URL + + + + diff --git a/Resources/Private/Language/locallang_mod.xlf b/Resources/Private/Language/locallang_mod.xlf new file mode 100644 index 0000000..54e9d2d --- /dev/null +++ b/Resources/Private/Language/locallang_mod.xlf @@ -0,0 +1,17 @@ + + + +
+ + + ThüCat + + + Provides access to current connection, imported data and configuration. + + + ThüCat + + + + diff --git a/Resources/Private/Language/locallang_tca.xlf b/Resources/Private/Language/locallang_tca.xlf new file mode 100644 index 0000000..4445cf7 --- /dev/null +++ b/Resources/Private/Language/locallang_tca.xlf @@ -0,0 +1,133 @@ + + + +
+ + + Organisation + + + Title + + + Description + + + Remote ID + + + Last imported + + + Manages + + + Towns + + + Tourist Information + + + + Tourist Information + + + Title + + + Description + + + Remote ID + + + Town + + + Managed by + + + Unkown + + + + Town + + + Title + + + Description + + + Remote ID + + + Tourist Information + + + Managed by + + + Unkown + + + + Import Configuration + + + Title + + + Type + + + Static list of URLs + + + Configuration + + + + Import Log + + + Used configuration + + + Imported entries + + + Created + + + + Import Log Entry + + + Was inserted or updated + + + Inserted + + + Updated + + + Table name (record type) + + + Record UID + + + Errors (JSON Format) + + + Part of this import log + + + Created + + + + diff --git a/Resources/Private/Templates/Backend/Import/Index.html b/Resources/Private/Templates/Backend/Import/Index.html new file mode 100644 index 0000000..31f6617 --- /dev/null +++ b/Resources/Private/Templates/Backend/Import/Index.html @@ -0,0 +1,54 @@ + + +

{f:translate(id: 'module.imports.headline')}

+ + + + + + {f:render(section: 'Imports', arguments: {imports: imports})} + + + + {f:translate(id: 'module.imports.missing.text')} + + + + + + + + + + + + + + + + + + {f:render(section: 'Import', arguments: {import: import})} + + +
{f:translate(id: 'module.imports.th.created')}{f:translate(id: 'module.imports.th.configuration')}{f:translate(id: 'module.imports.th.amountOfRecords')}{f:translate(id: 'module.imports.th.summary')}{f:translate(id: 'module.imports.th.errors')}
+
+ + + + {import.created -> f:format.date(format: 'd.m.Y H:i:s')} + {import.configuration.title} + {import.entries -> f:count()} + + {f:translate(id: 'module.imports.summary.tableName.{tableName}')} {amount}
+
+ + {error}
+
+ +
+ diff --git a/Resources/Private/Templates/Backend/Overview/Index.html b/Resources/Private/Templates/Backend/Overview/Index.html new file mode 100644 index 0000000..c8ed6b4 --- /dev/null +++ b/Resources/Private/Templates/Backend/Overview/Index.html @@ -0,0 +1,138 @@ + + +

{f:translate(id: 'module.overview.headline')}

+ + + +

+ {f:translate(id: 'module.importConfigurations.headline')} + + {f:icon(identifier: 'actions-document-add')} + +

+ + + {f:render(section: 'ImportConfigurations', arguments: {importConfigurations: importConfigurations})} + + + + {f:translate( + id: 'module.importConfigurations.missing.text', + arguments: { + 0: "{f:uri.newRecord(table: 'tx_thuecat_import_configuration')}" + } + ) -> f:format.raw()} + + + + +

{f:translate(id: 'module.organisations.headline')}

+ + + {f:render(section: 'Organisations', arguments: {organisations: organisations})} + + + {f:translate(id: 'module.organisations.missing.text')} + + + + + + + + + + + + + + + + + + + + + +
{f:translate(id: 'module.importConfigurations.title')}{f:translate(id: 'module.importConfigurations.lastChanged')}{f:translate(id: 'module.actions')}
{importConfiguration.title}{importConfiguration.lastChanged -> f:format.date(format: 'd.m.Y H:i')} + + {f:icon(identifier: 'actions-document-edit')} + + + {f:icon(identifier: 'actions-download')} + +
+
+ + + + + + + + + + + + + + + + + + + + + +
{f:translate(id: 'module.organisation.title')}{f:translate(id: 'module.organisation.towns')}{f:translate(id: 'module.organisation.lastImported')}{f:translate(id: 'module.actions')}
{organisation.title} + {f:render(section: 'Towns', arguments: {towns: organisation.managesTowns})} + {organisation.lastImported -> f:format.date(format: 'd.m.Y H:i')} + + {f:icon(identifier: 'actions-document-edit')} + +
+
+ + + + + {f:translate(id: 'module.organisation.towns.none')} + + + + {town.title} {f:render(section: 'TouristInformation', arguments: {touristInformation: town.touristInformation})} + + + + + ( + + {info.title} + + ) + + diff --git a/Tests/Functional/Domain/Import/ImporterTest.php b/Tests/Functional/Domain/Import/ImporterTest.php new file mode 100644 index 0000000..9140d1f --- /dev/null +++ b/Tests/Functional/Domain/Import/ImporterTest.php @@ -0,0 +1,48 @@ + + * + * 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. + */ + +use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase as TestCase; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\Importer + */ +class ImporterTest extends TestCase +{ + /** + * @test + */ + public function importsNewEntity(): void + { + $command = new CommandTester(); + $command->execute(); + } + + /** + * @test + */ + public function updatesExistingEntity(): void + { + } +} diff --git a/Tests/Unit/Domain/Import/Converter/OrganisationTest.php b/Tests/Unit/Domain/Import/Converter/OrganisationTest.php new file mode 100644 index 0000000..d28d857 --- /dev/null +++ b/Tests/Unit/Domain/Import/Converter/OrganisationTest.php @@ -0,0 +1,91 @@ + + * + * 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. + */ + +use PHPUnit\Framework\TestCase; +use WerkraumMedia\ThueCat\Domain\Import\Converter\Converter; +use WerkraumMedia\ThueCat\Domain\Import\Converter\Organisation; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\Converter\Organisation + * @uses WerkraumMedia\ThueCat\Domain\Import\Model\GenericEntity + */ +class OrganisationTest extends TestCase +{ + /** + * @test + */ + public function instanceCanBeCreated(): void + { + $subject = new Organisation(); + self::assertInstanceOf(Organisation::class, $subject); + } + + /** + * @test + */ + public function isInstanceOfConverter(): void + { + $subject = new Organisation(); + self::assertInstanceOf(Converter::class, $subject); + } + + /** + * @test + */ + public function canConvertTouristMarketingCompany(): void + { + $subject = new Organisation(); + self::assertTrue($subject->canConvert([ + 'thuecat:TouristMarketingCompany', + 'schema:Thing', + 'ttgds:Organization', + ])); + } + + /** + * @test + */ + public function convertsJsonIdToGenericEntity(): void + { + $subject = new Organisation(); + $entity = $subject->convert([ + '@id' => 'https://example.com/resources/018132452787-ngbe', + 'schema:name' => [ + '@value' => 'Title', + ], + 'schema:description' => [ + '@value' => 'Description', + ], + ]); + + self::assertSame(95, $entity->getTypo3StoragePid()); + self::assertSame('tx_thuecat_organisation', $entity->getTypo3DatabaseTableName()); + self::assertSame('https://example.com/resources/018132452787-ngbe', $entity->getRemoteId()); + self::assertSame([ + 'title' => 'Title', + 'description' => 'Description', + ], $entity->getData()); + } +} diff --git a/Tests/Unit/Domain/Import/Converter/RegistryTest.php b/Tests/Unit/Domain/Import/Converter/RegistryTest.php new file mode 100644 index 0000000..3389a39 --- /dev/null +++ b/Tests/Unit/Domain/Import/Converter/RegistryTest.php @@ -0,0 +1,108 @@ + + * + * 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. + */ + +use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; +use WerkraumMedia\ThueCat\Domain\Import\Converter\Converter; +use WerkraumMedia\ThueCat\Domain\Import\Converter\Registry; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\Converter\Registry + */ +class RegistryTest extends TestCase +{ + use ProphecyTrait; + + /** + * @test + */ + public function canBeCreated(): void + { + $subject = new Registry(); + + self::assertInstanceOf(Registry::class, $subject); + } + + /** + * @test + */ + public function allowsRegistrationOfConverter(): void + { + $subject = new Registry(); + $converter = $this->prophesize(Converter::class); + + $subject->registerConverter($converter->reveal()); + self::assertTrue(true); + } + + /** + * @test + */ + public function returnsConverterForMatchingType(): void + { + $subject = new Registry(); + $converter = $this->prophesize(Converter::class); + $converter->canConvert(['thuecat:Entity'])->willReturn(true); + $subject->registerConverter($converter->reveal()); + + $result = $subject->getConverterBasedOnType(['thuecat:Entity']); + self::assertSame($converter->reveal(), $result); + } + + /** + * @test + */ + public function returnsFirstMatchingConverterForMatchingType(): void + { + $subject = new Registry(); + + $converter1 = $this->prophesize(Converter::class); + $converter1->canConvert(['thuecat:Entity'])->willReturn(true); + $converter2 = $this->prophesize(Converter::class); + $converter2->canConvert(['thuecat:Entity'])->willReturn(true); + + $subject->registerConverter($converter1->reveal()); + $subject->registerConverter($converter2->reveal()); + $result = $subject->getConverterBasedOnType(['thuecat:Entity']); + self::assertSame($converter1->reveal(), $result); + } + + /** + * @test + */ + public function returnsNullForNoMatchingConverter(): void + { + $subject = new Registry(); + + $converter1 = $this->prophesize(Converter::class); + $converter1->canConvert(['thuecat:Entity'])->willReturn(false); + + $subject->registerConverter($converter1->reveal()); + + $result = $subject->getConverterBasedOnType(['thuecat:Entity']); + + self::assertSame(null, $result); + } +} diff --git a/Tests/Unit/Domain/Import/Converter/TouristInformationTest.php b/Tests/Unit/Domain/Import/Converter/TouristInformationTest.php new file mode 100644 index 0000000..496f1e7 --- /dev/null +++ b/Tests/Unit/Domain/Import/Converter/TouristInformationTest.php @@ -0,0 +1,199 @@ + + * + * 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. + */ + +use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; +use WerkraumMedia\ThueCat\Domain\Import\Converter\Converter; +use WerkraumMedia\ThueCat\Domain\Import\Converter\TouristInformation; +use WerkraumMedia\ThueCat\Domain\Model\Backend\Organisation; +use WerkraumMedia\ThueCat\Domain\Model\Backend\Town; +use WerkraumMedia\ThueCat\Domain\Repository\Backend\OrganisationRepository; +use WerkraumMedia\ThueCat\Domain\Repository\Backend\TownRepository; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\Converter\TouristInformation + * @uses WerkraumMedia\ThueCat\Domain\Import\Model\GenericEntity + */ +class TouristInformationTest extends TestCase +{ + use ProphecyTrait; + + /** + * @test + */ + public function instanceCanBeCreated(): void + { + $organisationRepository = $this->prophesize(OrganisationRepository::class); + $townRepository = $this->prophesize(TownRepository::class); + + $subject = new TouristInformation( + $organisationRepository->reveal(), + $townRepository->reveal() + ); + self::assertInstanceOf(TouristInformation::class, $subject); + } + + /** + * @test + */ + public function isInstanceOfConverter(): void + { + $organisationRepository = $this->prophesize(OrganisationRepository::class); + $townRepository = $this->prophesize(TownRepository::class); + + $subject = new TouristInformation( + $organisationRepository->reveal(), + $townRepository->reveal() + ); + self::assertInstanceOf(Converter::class, $subject); + } + + /** + * @test + */ + public function canConvertTouristMarketingCompany(): void + { + $organisationRepository = $this->prophesize(OrganisationRepository::class); + $townRepository = $this->prophesize(TownRepository::class); + + $subject = new TouristInformation( + $organisationRepository->reveal(), + $townRepository->reveal() + ); + self::assertTrue($subject->canConvert([ + 'thuecat:TouristInformation', + ])); + } + + /** + * @test + */ + public function convertsJsonIdToGenericEntityWithoutRelations(): void + { + $organisationRepository = $this->prophesize(OrganisationRepository::class); + $organisationRepository->findOneByRemoteId('https://example.com/resources/018132452787-xxxx') + ->willReturn(null); + + $townRepository = $this->prophesize(TownRepository::class); + $townRepository->findOneByRemoteIds([ + 'https://example.com/resources/043064193523-jcyt', + 'https://example.com/resources/573211638937-gmqb', + ])->willReturn(null); + + $subject = new TouristInformation( + $organisationRepository->reveal(), + $townRepository->reveal() + ); + $entity = $subject->convert([ + '@id' => 'https://example.com/resources/018132452787-ngbe', + 'thuecat:managedBy' => [ + '@id' => 'https://example.com/resources/018132452787-xxxx', + ], + 'schema:containedInPlace' => [ + [ + '@id' => 'https://example.com/resources/043064193523-jcyt', + ], + [ + '@id' => 'https://example.com/resources/573211638937-gmqb', + ], + ], + 'schema:name' => [ + '@value' => 'Title', + ], + 'schema:description' => [ + [ + '@value' => 'Description', + ], + ], + ]); + + self::assertSame(95, $entity->getTypo3StoragePid()); + self::assertSame('tx_thuecat_tourist_information', $entity->getTypo3DatabaseTableName()); + self::assertSame('https://example.com/resources/018132452787-ngbe', $entity->getRemoteId()); + self::assertSame([ + 'title' => 'Title', + 'description' => 'Description', + 'managed_by' => 0, + 'town' => 0, + ], $entity->getData()); + } + + /** + * @test + */ + public function convertsJsonIdToGenericEntityWithRelations(): void + { + $organisation = $this->prophesize(Organisation::class); + $organisation->getUid()->willReturn(10); + $organisationRepository = $this->prophesize(OrganisationRepository::class); + $organisationRepository->findOneByRemoteId('https://example.com/resources/018132452787-xxxx') + ->willReturn($organisation->reveal()); + + $town = $this->prophesize(Town::class); + $town->getUid()->willReturn(20); + $townRepository = $this->prophesize(TownRepository::class); + $townRepository->findOneByRemoteIds([ + 'https://example.com/resources/043064193523-jcyt', + 'https://example.com/resources/573211638937-gmqb', + ])->willReturn($town->reveal()); + + $subject = new TouristInformation( + $organisationRepository->reveal(), + $townRepository->reveal() + ); + $entity = $subject->convert([ + '@id' => 'https://example.com/resources/018132452787-ngbe', + 'thuecat:managedBy' => [ + '@id' => 'https://example.com/resources/018132452787-xxxx', + ], + 'schema:containedInPlace' => [ + [ + '@id' => 'https://example.com/resources/043064193523-jcyt', + ], + [ + '@id' => 'https://example.com/resources/573211638937-gmqb', + ], + ], + 'schema:name' => [ + '@value' => 'Title', + ], + 'schema:description' => [ + [ + '@value' => 'Description', + ], + ], + ]); + + self::assertSame(95, $entity->getTypo3StoragePid()); + self::assertSame('tx_thuecat_tourist_information', $entity->getTypo3DatabaseTableName()); + self::assertSame('https://example.com/resources/018132452787-ngbe', $entity->getRemoteId()); + self::assertSame([ + 'title' => 'Title', + 'description' => 'Description', + 'managed_by' => 10, + 'town' => 20, + ], $entity->getData()); + } +} diff --git a/Tests/Unit/Domain/Import/Converter/TownTest.php b/Tests/Unit/Domain/Import/Converter/TownTest.php new file mode 100644 index 0000000..b0d4be3 --- /dev/null +++ b/Tests/Unit/Domain/Import/Converter/TownTest.php @@ -0,0 +1,141 @@ + + * + * 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. + */ + +use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; +use WerkraumMedia\ThueCat\Domain\Import\Converter\Converter; +use WerkraumMedia\ThueCat\Domain\Import\Converter\Town; +use WerkraumMedia\ThueCat\Domain\Model\Backend\Organisation; +use WerkraumMedia\ThueCat\Domain\Repository\Backend\OrganisationRepository; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\Converter\Town + * @uses WerkraumMedia\ThueCat\Domain\Import\Model\GenericEntity + */ +class TownTest extends TestCase +{ + use ProphecyTrait; + + /** + * @test + */ + public function instanceCanBeCreated(): void + { + $organisationRepository = $this->prophesize(OrganisationRepository::class); + + $subject = new Town($organisationRepository->reveal()); + self::assertInstanceOf(Town::class, $subject); + } + + /** + * @test + */ + public function isInstanceOfConverter(): void + { + $organisationRepository = $this->prophesize(OrganisationRepository::class); + + $subject = new Town($organisationRepository->reveal()); + self::assertInstanceOf(Converter::class, $subject); + } + + /** + * @test + */ + public function canConvertTouristMarketingCompany(): void + { + $organisationRepository = $this->prophesize(OrganisationRepository::class); + + $subject = new Town($organisationRepository->reveal()); + self::assertTrue($subject->canConvert([ + 'thuecat:Town', + ])); + } + + /** + * @test + */ + public function convertsJsonIdToGenericEntityWithoutOrganisation(): void + { + $organisationRepository = $this->prophesize(OrganisationRepository::class); + $organisationRepository->findOneByRemoteId('https://example.com/resources/018132452787-xxxx')->willReturn(null); + + $subject = new Town($organisationRepository->reveal()); + $entity = $subject->convert([ + '@id' => 'https://example.com/resources/018132452787-ngbe', + 'thuecat:managedBy' => [ + '@id' => 'https://example.com/resources/018132452787-xxxx', + ], + 'schema:name' => [ + '@value' => 'Title', + ], + 'schema:description' => [ + '@value' => 'Description', + ], + ]); + + self::assertSame(95, $entity->getTypo3StoragePid()); + self::assertSame('tx_thuecat_town', $entity->getTypo3DatabaseTableName()); + self::assertSame('https://example.com/resources/018132452787-ngbe', $entity->getRemoteId()); + self::assertSame([ + 'title' => 'Title', + 'description' => 'Description', + 'managed_by' => 0, + ], $entity->getData()); + } + + /** + * @test + */ + public function convertsJsonIdToGenericEntityWithOrganisation(): void + { + $organisation = $this->prophesize(Organisation::class); + $organisation->getUid()->willReturn(10); + $organisationRepository = $this->prophesize(OrganisationRepository::class); + $organisationRepository->findOneByRemoteId('https://example.com/resources/018132452787-xxxx')->willReturn($organisation->reveal()); + + $subject = new Town($organisationRepository->reveal()); + $entity = $subject->convert([ + '@id' => 'https://example.com/resources/018132452787-ngbe', + 'thuecat:managedBy' => [ + '@id' => 'https://example.com/resources/018132452787-xxxx', + ], + 'schema:name' => [ + '@value' => 'Title', + ], + 'schema:description' => [ + '@value' => 'Description', + ], + ]); + + self::assertSame(95, $entity->getTypo3StoragePid()); + self::assertSame('tx_thuecat_town', $entity->getTypo3DatabaseTableName()); + self::assertSame('https://example.com/resources/018132452787-ngbe', $entity->getRemoteId()); + self::assertSame([ + 'title' => 'Title', + 'description' => 'Description', + 'managed_by' => 10, + ], $entity->getData()); + } +} diff --git a/Tests/Unit/Domain/Import/Importer/FetchDataTest.php b/Tests/Unit/Domain/Import/Importer/FetchDataTest.php new file mode 100644 index 0000000..a6698bd --- /dev/null +++ b/Tests/Unit/Domain/Import/Importer/FetchDataTest.php @@ -0,0 +1,118 @@ + + * + * 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. + */ + +use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; +use Psr\Http\Client\ClientInterface; +use Psr\Http\Message\RequestFactoryInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use WerkraumMedia\ThueCat\Domain\Import\Importer\FetchData; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\Importer\FetchData + */ +class FetchDataTest extends TestCase +{ + use ProphecyTrait; + + /** + * @test + */ + public function canBeCreated(): void + { + $requestFactory = $this->prophesize(RequestFactoryInterface::class); + $httpClient = $this->prophesize(ClientInterface::class); + + $subject = new FetchData( + $requestFactory->reveal(), + $httpClient->reveal() + ); + + self::assertInstanceOf(FetchData::class, $subject); + } + + /** + * @test + */ + public function returnsParsedJsonLdBasedOnUrl(): void + { + $requestFactory = $this->prophesize(RequestFactoryInterface::class); + $httpClient = $this->prophesize(ClientInterface::class); + + $request = $this->prophesize(RequestInterface::class); + $response = $this->prophesize(ResponseInterface::class); + + $requestFactory->createRequest('GET', 'https://example.com/resources/018132452787-ngbe') + ->willReturn($request->reveal()); + + $httpClient->sendRequest($request->reveal()) + ->willReturn($response->reveal()); + + $response->getBody()->willReturn('{"@graph":[{"@id":"https://example.com/resources/018132452787-ngbe"}]}'); + + $subject = new FetchData( + $requestFactory->reveal(), + $httpClient->reveal() + ); + + $result = $subject->jsonLDFromUrl('https://example.com/resources/018132452787-ngbe'); + self::assertSame([ + '@graph' => [ + [ + '@id' => 'https://example.com/resources/018132452787-ngbe', + ], + ], + ], $result); + } + + /** + * @test + */ + public function returnsEmptyArrayInCaseOfError(): void + { + $requestFactory = $this->prophesize(RequestFactoryInterface::class); + $httpClient = $this->prophesize(ClientInterface::class); + + $request = $this->prophesize(RequestInterface::class); + $response = $this->prophesize(ResponseInterface::class); + + $requestFactory->createRequest('GET', 'https://example.com/resources/018132452787-ngbe') + ->willReturn($request->reveal()); + + $httpClient->sendRequest($request->reveal()) + ->willReturn($response->reveal()); + + $response->getBody()->willReturn(''); + + $subject = new FetchData( + $requestFactory->reveal(), + $httpClient->reveal() + ); + + $result = $subject->jsonLDFromUrl('https://example.com/resources/018132452787-ngbe'); + self::assertSame([], $result); + } +} diff --git a/Tests/Unit/Domain/Import/ImporterTest.php b/Tests/Unit/Domain/Import/ImporterTest.php new file mode 100644 index 0000000..31a0fca --- /dev/null +++ b/Tests/Unit/Domain/Import/ImporterTest.php @@ -0,0 +1,243 @@ + + * + * 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. + */ + +use PHPUnit\Framework\TestCase; +use Prophecy\Argument; +use Prophecy\PhpUnit\ProphecyTrait; +use WerkraumMedia\ThueCat\Domain\Import\Converter\Converter; +use WerkraumMedia\ThueCat\Domain\Import\Converter\Registry as ConverterRegistry; +use WerkraumMedia\ThueCat\Domain\Import\Importer; +use WerkraumMedia\ThueCat\Domain\Import\Importer\FetchData; +use WerkraumMedia\ThueCat\Domain\Import\Importer\SaveData; +use WerkraumMedia\ThueCat\Domain\Import\Model\Entity; +use WerkraumMedia\ThueCat\Domain\Import\UrlProvider\Registry as UrlProviderRegistry; +use WerkraumMedia\ThueCat\Domain\Import\UrlProvider\UrlProvider; +use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportConfiguration; +use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLog; +use WerkraumMedia\ThueCat\Domain\Repository\Backend\ImportLogRepository; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\Importer + * @uses WerkraumMedia\ThueCat\Domain\Model\Backend\ImportLog + */ +class ImporterTest extends TestCase +{ + use ProphecyTrait; + + /** + * @test + */ + public function canBeCreated(): void + { + $urls = $this->prophesize(UrlProviderRegistry::class); + $converter = $this->prophesize(ConverterRegistry::class); + $importLogRepository = $this->prophesize(ImportLogRepository::class); + $fetchData = $this->prophesize(FetchData::class); + $saveData = $this->prophesize(SaveData::class); + + $subject = new Importer( + $urls->reveal(), + $converter->reveal(), + $importLogRepository->reveal(), + $fetchData->reveal(), + $saveData->reveal() + ); + self::assertInstanceOf(Importer::class, $subject); + } + + /** + * @test + */ + public function importsNothingIfNoUrlProviderIsGiven(): void + { + $urls = $this->prophesize(UrlProviderRegistry::class); + $urlProvider = $this->prophesize(UrlProvider::class); + $converter = $this->prophesize(ConverterRegistry::class); + $importLogRepository = $this->prophesize(ImportLogRepository::class); + $fetchData = $this->prophesize(FetchData::class); + $saveData = $this->prophesize(SaveData::class); + $configuration = $this->prophesize(ImportConfiguration::class); + + $urls->getProviderForConfiguration($configuration->reveal())->willReturn($urlProvider->reveal()); + $urlProvider->getUrls()->willReturn([]); + $fetchData->jsonLDFromUrl()->shouldNotBeCalled(); + $saveData->import()->shouldNotBeCalled(); + + $subject = new Importer( + $urls->reveal(), + $converter->reveal(), + $importLogRepository->reveal(), + $fetchData->reveal(), + $saveData->reveal() + ); + $subject->importConfiguration($configuration->reveal()); + } + + /** + * @test + */ + public function importsAllUrlsFromAllUrlProvider(): void + { + $urls = $this->prophesize(UrlProviderRegistry::class); + $urlProvider = $this->prophesize(UrlProvider::class); + $converter = $this->prophesize(ConverterRegistry::class); + $concreteConverter = $this->prophesize(Converter::class); + $importLogRepository = $this->prophesize(ImportLogRepository::class); + $fetchData = $this->prophesize(FetchData::class); + $saveData = $this->prophesize(SaveData::class); + $configuration = $this->prophesize(ImportConfiguration::class); + + $entity1 = $this->prophesize(Entity::class); + $entity2 = $this->prophesize(Entity::class); + + $urls->getProviderForConfiguration($configuration->reveal())->willReturn($urlProvider->reveal()); + $urlProvider->getUrls()->willReturn([ + 'https://example.com/resources/34343-ex', + 'https://example.com/resources/34344-es', + ]); + + $fetchData->jsonLDFromUrl('https://example.com/resources/34343-ex')->willReturn(['@graph' => [ + [ + '@id' => 'https://example.com/resources/34343-ex', + '@type' => [ + 'schema:Organization', + 'thuecat:TouristMarketingCompany', + ], + ], + ]]); + $fetchData->jsonLDFromUrl('https://example.com/resources/34344-es')->willReturn(['@graph' => [ + [ + '@id' => 'https://example.com/resources/34344-es', + '@type' => [ + 'schema:Organization', + 'thuecat:TouristMarketingCompany', + ], + ], + ]]); + + $converter->getConverterBasedOnType([ + 'schema:Organization', + 'thuecat:TouristMarketingCompany', + ])->willReturn($concreteConverter->reveal()); + + $concreteConverter->convert(Argument::that(function (array $jsonEntity) { + return $jsonEntity['@id'] === 'https://example.com/resources/34343-ex'; + }))->willReturn($entity1->reveal()); + $concreteConverter->convert(Argument::that(function (array $jsonEntity) { + return $jsonEntity['@id'] === 'https://example.com/resources/34344-es'; + }))->willReturn($entity2->reveal()); + + $saveData->import($entity1->reveal(), Argument::type(ImportLog::class))->shouldBeCalled(); + $saveData->import($entity2->reveal(), Argument::type(ImportLog::class))->shouldBeCalled(); + + $subject = new Importer( + $urls->reveal(), + $converter->reveal(), + $importLogRepository->reveal(), + $fetchData->reveal(), + $saveData->reveal() + ); + $subject->importConfiguration($configuration->reveal()); + } + + /** + * @test + */ + public function handlesMissingConverter(): void + { + $urls = $this->prophesize(UrlProviderRegistry::class); + $urlProvider = $this->prophesize(UrlProvider::class); + $converter = $this->prophesize(ConverterRegistry::class); + $concreteConverter = $this->prophesize(Converter::class); + $importLogRepository = $this->prophesize(ImportLogRepository::class); + $fetchData = $this->prophesize(FetchData::class); + $saveData = $this->prophesize(SaveData::class); + $configuration = $this->prophesize(ImportConfiguration::class); + + $urls->getProviderForConfiguration($configuration->reveal())->willReturn($urlProvider->reveal()); + $urlProvider->getUrls()->willReturn([ + 'https://example.com/resources/34343-ex', + ]); + + $fetchData->jsonLDFromUrl('https://example.com/resources/34343-ex')->willReturn(['@graph' => [ + [ + '@id' => 'https://example.com/resources/34343-ex', + '@type' => [ + 'schema:Organization', + 'thuecat:TouristMarketingCompany', + ], + ], + ]]); + + $converter->getConverterBasedOnType([ + 'schema:Organization', + 'thuecat:TouristMarketingCompany', + ])->willReturn(null); + + $saveData->import()->shouldNotBeCalled(); + + $subject = new Importer( + $urls->reveal(), + $converter->reveal(), + $importLogRepository->reveal(), + $fetchData->reveal(), + $saveData->reveal() + ); + $subject->importConfiguration($configuration->reveal()); + } + + /** + * @test + */ + public function handlesEmptyResponse(): void + { + $urls = $this->prophesize(UrlProviderRegistry::class); + $urlProvider = $this->prophesize(UrlProvider::class); + $converter = $this->prophesize(ConverterRegistry::class); + $concreteConverter = $this->prophesize(Converter::class); + $importLogRepository = $this->prophesize(ImportLogRepository::class); + $fetchData = $this->prophesize(FetchData::class); + $saveData = $this->prophesize(SaveData::class); + $configuration = $this->prophesize(ImportConfiguration::class); + + $urls->getProviderForConfiguration($configuration->reveal())->willReturn($urlProvider->reveal()); + $urlProvider->getUrls()->willReturn([ + 'https://example.com/resources/34343-ex', + ]); + + $fetchData->jsonLDFromUrl('https://example.com/resources/34343-ex')->willReturn([]); + + $converter->getConverterBasedOnType()->shouldNotBeCalled(); + + $subject = new Importer( + $urls->reveal(), + $converter->reveal(), + $importLogRepository->reveal(), + $fetchData->reveal(), + $saveData->reveal() + ); + $subject->importConfiguration($configuration->reveal()); + } +} diff --git a/Tests/Unit/Domain/Import/Model/GenericEntityTest.php b/Tests/Unit/Domain/Import/Model/GenericEntityTest.php new file mode 100644 index 0000000..863285a --- /dev/null +++ b/Tests/Unit/Domain/Import/Model/GenericEntityTest.php @@ -0,0 +1,183 @@ + + * + * 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. + */ + +use PHPUnit\Framework\TestCase; +use WerkraumMedia\ThueCat\Domain\Import\Model\GenericEntity; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\Model\GenericEntity + */ +class GenericEntityTest extends TestCase +{ + /** + * @test + */ + public function canBeCreated(): void + { + $subject = new GenericEntity( + 0, + '', + '', + [] + ); + self::assertInstanceOf(GenericEntity::class, $subject); + } + + /** + * @test + */ + public function returnsTypo3StoragePid(): void + { + $subject = new GenericEntity( + 10, + '', + '', + [] + ); + self::assertSame(10, $subject->getTypo3StoragePid()); + } + + /** + * @test + */ + public function returnsTypo3DatabaseTableName(): void + { + $subject = new GenericEntity( + 0, + 'tx_thuecat_entity', + '', + [] + ); + self::assertSame('tx_thuecat_entity', $subject->getTypo3DatabaseTableName()); + } + + /** + * @test + */ + public function returnsRemoteId(): void + { + $subject = new GenericEntity( + 0, + '', + 'https://example.com/resources/333039283321-xxwg', + [] + ); + self::assertSame( + 'https://example.com/resources/333039283321-xxwg', + $subject->getRemoteId() + ); + } + + /** + * @test + */ + public function returnsData(): void + { + $subject = new GenericEntity( + 0, + '', + '', + [ + 'column_name_1' => 'value 1', + 'column_name_2' => 'value 2', + ] + ); + self::assertSame( + [ + 'column_name_1' => 'value 1', + 'column_name_2' => 'value 2', + ], + $subject->getData() + ); + } + + /** + * @test + */ + public function returnsNotCreatedByDefault(): void + { + $subject = new GenericEntity( + 0, + '', + '', + [] + ); + self::assertSame( + false, + $subject->wasCreated() + ); + } + + /** + * @test + */ + public function returnsZeroAsDefaultTypo3Uid(): void + { + $subject = new GenericEntity( + 0, + '', + '', + [] + ); + self::assertSame( + 0, + $subject->getTypo3Uid() + ); + } + + /** + * @test + */ + public function canBeMarkedAsImported(): void + { + $subject = new GenericEntity( + 0, + '', + '', + [] + ); + + $subject->setImportedTypo3Uid(10); + self::assertSame(true, $subject->wasCreated()); + self::assertSame(10, $subject->getTypo3Uid()); + } + + /** + * @test + */ + public function canBeMarkedAsExisting(): void + { + $subject = new GenericEntity( + 0, + '', + '', + [] + ); + + $subject->setExistingTypo3Uid(10); + self::assertSame(false, $subject->wasCreated()); + self::assertSame(10, $subject->getTypo3Uid()); + } +} diff --git a/Tests/Unit/Domain/Import/RequestFactoryTest.php b/Tests/Unit/Domain/Import/RequestFactoryTest.php new file mode 100644 index 0000000..69e5197 --- /dev/null +++ b/Tests/Unit/Domain/Import/RequestFactoryTest.php @@ -0,0 +1,54 @@ + + * + * 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. + */ + +use PHPUnit\Framework\TestCase; +use WerkraumMedia\ThueCat\Domain\Import\RequestFactory; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\RequestFactory + */ +class RequestFactoryTest extends TestCase +{ + /** + * @test + */ + public function canBeCreated(): void + { + $subject = new RequestFactory(); + + self::assertInstanceOf(RequestFactory::class, $subject); + } + + /** + * @test + */ + public function returnsRequestWithJsonIdFormat(): void + { + $subject = new RequestFactory(); + $request = $subject->createRequest('GET', 'https://example.com/resources/333039283321-xxwg'); + + self::assertSame('format=jsonId', $request->getUri()->getQuery()); + } +} diff --git a/Tests/Unit/Domain/Import/UrlProvider/RegistryTest.php b/Tests/Unit/Domain/Import/UrlProvider/RegistryTest.php new file mode 100644 index 0000000..b368137 --- /dev/null +++ b/Tests/Unit/Domain/Import/UrlProvider/RegistryTest.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. + */ + +use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; +use WerkraumMedia\ThueCat\Domain\Import\UrlProvider\Registry; +use WerkraumMedia\ThueCat\Domain\Import\UrlProvider\UrlProvider; +use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportConfiguration; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\UrlProvider\Registry + */ +class RegistryTest extends TestCase +{ + use ProphecyTrait; + + /** + * @test + */ + public function canBeCreated(): void + { + $subject = new Registry(); + + self::assertInstanceOf(Registry::class, $subject); + } + + /** + * @test + */ + public function allowsRegistrationOfUrlProvider(): void + { + $subject = new Registry(); + $provider = $this->prophesize(UrlProvider::class); + + $subject->registerProvider($provider->reveal()); + self::assertTrue(true); + } + + /** + * @test + */ + public function returnsNullIfNoProviderExistsForConfiguration(): void + { + $configuration = $this->prophesize(ImportConfiguration::class); + + $subject = new Registry(); + + $result = $subject->getProviderForConfiguration($configuration->reveal()); + self::assertSame(null, $result); + } + + /** + * @test + */ + public function returnsProviderForConfiguration(): void + { + $configuration = $this->prophesize(ImportConfiguration::class); + + $subject = new Registry(); + + $provider = $this->prophesize(UrlProvider::class); + $concreteProvider = $this->prophesize(UrlProvider::class); + $provider->canProvideForConfiguration($configuration->reveal())->willReturn(true); + $provider->createWithConfiguration($configuration->reveal())->willReturn($concreteProvider->reveal()); + $subject->registerProvider($provider->reveal()); + + $result = $subject->getProviderForConfiguration($configuration->reveal()); + self::assertSame($concreteProvider->reveal(), $result); + } +} diff --git a/Tests/Unit/Domain/Import/UrlProvider/StaticUrlProviderTest.php b/Tests/Unit/Domain/Import/UrlProvider/StaticUrlProviderTest.php new file mode 100644 index 0000000..a9a593b --- /dev/null +++ b/Tests/Unit/Domain/Import/UrlProvider/StaticUrlProviderTest.php @@ -0,0 +1,95 @@ + + * + * 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. + */ + +use PHPUnit\Framework\TestCase; +use Prophecy\PhpUnit\ProphecyTrait; +use WerkraumMedia\ThueCat\Domain\Import\UrlProvider\StaticUrlProvider; +use WerkraumMedia\ThueCat\Domain\Model\Backend\ImportConfiguration; + +/** + * @covers WerkraumMedia\ThueCat\Domain\Import\UrlProvider\StaticUrlProvider + */ +class StaticUrlProviderTest extends TestCase +{ + use ProphecyTrait; + + /** + * @test + */ + public function canBeCreated(): void + { + $configuration = $this->prophesize(ImportConfiguration::class); + $configuration->getUrls()->willReturn([]); + + $subject = new StaticUrlProvider($configuration->reveal()); + self::assertInstanceOf(StaticUrlProvider::class, $subject); + } + + /** + * @test + */ + public function canProvideForStaticConfiguration(): void + { + $configuration = $this->prophesize(ImportConfiguration::class); + $configuration->getUrls()->willReturn([]); + $configuration->getType()->willReturn('static'); + + $subject = new StaticUrlProvider($configuration->reveal()); + + $result = $subject->canProvideForConfiguration($configuration->reveal()); + self::assertTrue($result); + } + + /** + * @test + */ + public function returnsConcreteProviderForConfiguration(): void + { + $configuration = $this->prophesize(ImportConfiguration::class); + $configuration->getUrls()->willReturn(['https://example.com']); + + $subject = new StaticUrlProvider($configuration->reveal()); + + $result = $subject->createWithConfiguration($configuration->reveal()); + self::assertInstanceOf(StaticUrlProvider::class, $subject); + } + + /** + * @test + */ + public function concreteProviderReturnsUrls(): void + { + $configuration = $this->prophesize(ImportConfiguration::class); + $configuration->getUrls()->willReturn(['https://example.com']); + + $subject = new StaticUrlProvider($configuration->reveal()); + + $concreteProvider = $subject->createWithConfiguration($configuration->reveal()); + $result = $concreteProvider->getUrls(); + self::assertSame([ + 'https://example.com', + ], $result); + } +} diff --git a/Tests/Unit/ExtensionTest.php b/Tests/Unit/ExtensionTest.php new file mode 100644 index 0000000..fe8239d --- /dev/null +++ b/Tests/Unit/ExtensionTest.php @@ -0,0 +1,42 @@ + + * + * 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. + */ + +use PHPUnit\Framework\TestCase; +use WerkraumMedia\ThueCat\Extension; + +/** + * @covers WerkraumMedia\ThueCat\Extension + * @testdox The extension class + */ +class ExtensionTest extends TestCase +{ + /** + * @test + */ + public function returnsLanguagePath(): void + { + self::assertSame('LLL:EXT:thuecat/Resources/Private/Language/', Extension::getLanguagePath()); + } +} diff --git a/composer.json b/composer.json index fe010a5..9bd886a 100644 --- a/composer.json +++ b/composer.json @@ -15,15 +15,31 @@ "email": "coding@daniel-siepmann.de" } ], + "autoload": { + "psr-4": { + "WerkraumMedia\\ThueCat\\": "Classes/" + } + }, + "autoload-dev": { + "psr-4": { + "WerkraumMedia\\ThueCat\\Tests\\": "Tests/" + } + }, "require": { - "typo3/cms-core": "^10.4" + "php": "^7.4", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "symfony/console": "^5.2", + "typo3/cms-core": "^10.4", + "typo3/cms-extbase": "^10.4" }, "require-dev": { - "phpunit/phpunit": "^9.5", "maglnet/composer-require-checker": "^2.1", "phpspec/prophecy-phpunit": "^2.0", - "typo3/testing-framework": "^6.6", - "symplify/easy-coding-standard": "^9.0" + "phpunit/phpunit": "^9.5", + "symplify/easy-coding-standard": "^9.0", + "typo3/testing-framework": "^6.6" }, "scripts": { "post-autoload-dump": [ diff --git a/ecs.php b/ecs.php index 8c67f1f..9098ea2 100644 --- a/ecs.php +++ b/ecs.php @@ -5,6 +5,7 @@ declare(strict_types=1); use PhpCsFixer\Fixer\ArrayNotation\ArraySyntaxFixer; use PhpCsFixer\Fixer\ArrayNotation\TrailingCommaInMultilineArrayFixer; use PhpCsFixer\Fixer\Import\FullyQualifiedStrictTypesFixer; +use PhpCsFixer\Fixer\Import\NoUnusedImportsFixer; use PhpCsFixer\Fixer\PhpUnit\PhpUnitTestAnnotationFixer; use PhpCsFixer\Fixer\Strict\DeclareStrictTypesFixer; use PhpCsFixer\Fixer\StringNotation\SingleQuoteFixer; @@ -17,8 +18,9 @@ return static function (ContainerConfigurator $containerConfigurator): void { $services = $containerConfigurator->services(); $parameters->set(Option::PATHS, [ - __DIR__ . '/Classes', - __DIR__ . '/Tests', + __DIR__ . '/Classes/', + __DIR__ . '/Configuration/', + __DIR__ . '/Tests/', __DIR__ . '/ecs.php', __DIR__ . '/ext_emconf.php', ]); @@ -32,17 +34,20 @@ return static function (ContainerConfigurator $containerConfigurator): void { $parameters->set(Option::SKIP, [ DeclareStrictTypesFixer::class => [ + __DIR__ . '/Configuration/', __DIR__ . '/ext_emconf.php', ], ]); + $services->set(NoUnusedImportsFixer::class); $services->set(FullyQualifiedStrictTypesFixer::class); - $services->set(PhpUnitTestAnnotationFixer::class)->call('configure', [[ - 'style' => 'annotation', - ]]); $services->set(ArraySyntaxFixer::class)->call('configure', [[ 'syntax' => 'short', ]]); $services->set(SingleQuoteFixer::class); $services->set(TrailingCommaInMultilineArrayFixer::class); + + $services->set(PhpUnitTestAnnotationFixer::class)->call('configure', [[ + 'style' => 'annotation', + ]]); }; diff --git a/ext_tables.php b/ext_tables.php new file mode 100644 index 0000000..f338cb8 --- /dev/null +++ b/ext_tables.php @@ -0,0 +1,5 @@ + + + + + + Tests/Unit/ + + + + + + Classes + + + + + + +