diff --git a/Classes/Command/ImportCommandController.php b/Classes/Command/ImportCommandController.php index 32fda3f..504ee66 100644 --- a/Classes/Command/ImportCommandController.php +++ b/Classes/Command/ImportCommandController.php @@ -37,15 +37,21 @@ public function importCommand(string $preset, ?bool $quiet = null, ?bool $forceU * @param bool|null $quiet If set, no output, apart from errors, will be displayed * @param bool|null $forceUpdates If set, all local records will be updated regardless of their version/timestamp. This is useful for node type changes that require new data to be fetched * @param bool|null $fromFixture If set, the data will be loaded from a local fixture file instead of the configured data source + * @param string|null $overrideSourceOptions Allows to override default options for the data source via JSON, e.g. '{"endpoint":"https://some-custom.tld/endpoint"}' + * @param string|null $overrideTargetOptions Allows to override default options for the data target via JSON, e.g. '{"endpoint":"https://some-custom.tld/endpoint"}' */ - public function runCommand(string $preset, ?bool $quiet = null, ?bool $forceUpdates = null, ?bool $fromFixture = null): void + public function runCommand(string $preset, ?bool $quiet = null, ?bool $forceUpdates = null, ?bool $fromFixture = null, ?string $overrideSourceOptions = null, ?string $overrideTargetOptions = null): void { - if ($fromFixture === true) { - $importService = $this->importServiceFactory->createWithFixture($preset); + $customSourceOptions = self::parseJsonOption($overrideSourceOptions); + $customTargetOptions = self::parseJsonOption($overrideTargetOptions); + if ($fromFixture) { + if ($customSourceOptions !== null) { + throw new \InvalidArgumentException('The options from-fixture and override-source-options must not be combined!', 1716824420); + } + $importService = $this->importServiceFactory->createWithFixture($preset, $customTargetOptions); } else { - $importService = $this->importServiceFactory->create($preset); + $importService = $this->importServiceFactory->create($preset, $customSourceOptions, $customTargetOptions); } - $this->registerEventHandlers($importService, $quiet ?? false); try { $importService->importData($forceUpdates ?? false); @@ -110,6 +116,8 @@ public function presetsCommand(): void /** * Displays configuration for a given preset + * + * @param string $preset The preset to show configuration for (see Wwwision.Import.presets setting) */ public function presetCommand(string $preset): void { @@ -164,6 +172,18 @@ private function renderResult(Result $result): void array_map(fn (Message $message) => $this->outputLine('%s', [$message->render()]), $result->getNotices()); } + private static function parseJsonOption(string|null $option): ?array + { + if ($option === null) { + return null; + } + try { + return json_decode($option, true, 512, JSON_THROW_ON_ERROR); + } catch (\JsonException $e) { + throw new \InvalidArgumentException(sprintf('Failed to JSON-decode option "%s": %s', $option, $e->getMessage()), 1716823861); + } + } + /** * @param ImportService $importService * @param bool $quiet diff --git a/Classes/Factory/ImportServiceFactory.php b/Classes/Factory/ImportServiceFactory.php index a14c143..7033457 100644 --- a/Classes/Factory/ImportServiceFactory.php +++ b/Classes/Factory/ImportServiceFactory.php @@ -23,9 +23,9 @@ public function __construct( ) { } - public function create(string $presetName): ImportService + public function create(string $presetName, array|null $customSourceOptions, array|null $customTargetOptions): ImportService { - return new ImportService($this->presetFactory->create($this->getPresetConfiguration($presetName))); + return new ImportService($this->presetFactory->create($this->getPresetConfiguration($presetName), $customSourceOptions, $customTargetOptions)); } public function createFromPreset(Preset $preset): ImportService @@ -33,9 +33,9 @@ public function createFromPreset(Preset $preset): ImportService return new ImportService($preset); } - public function createWithFixture(string $presetName): ImportService + public function createWithFixture(string $presetName, array|null $customTargetOptions): ImportService { - $preset = $this->presetFactory->create($this->getPresetConfiguration($presetName)); + $preset = $this->presetFactory->create($this->getPresetConfiguration($presetName), null, $customTargetOptions); if (!isset($this->presets[$presetName]['source']['fixture']['file'])) { throw new \RuntimeException(sprintf('Missing "source.fixture.file" configuration for preset "%s"', $presetName), 1558433554); } @@ -49,7 +49,7 @@ public function createWithFixture(string $presetName): ImportService public function createWithDataSource(string $presetName, DataSourceInterface $dataSource): ImportService { - return new ImportService($this->presetFactory->create($this->getPresetConfiguration($presetName))->withDataSource($dataSource)); + return new ImportService($this->presetFactory->create($this->getPresetConfiguration($presetName), null, null)->withDataSource($dataSource)); } public function getPresetConfiguration(string $presetName): array diff --git a/Classes/Factory/PresetFactory.php b/Classes/Factory/PresetFactory.php index cd2f1c2..fba37c9 100644 --- a/Classes/Factory/PresetFactory.php +++ b/Classes/Factory/PresetFactory.php @@ -24,7 +24,7 @@ protected function __construct( { } - public function create(array $configuration): Preset + public function create(array $configuration, array|null $customSourceOptions, array|null $customTargetOptions): Preset { if (!isset($configuration['source']['factory'])) { throw new \RuntimeException('Missing "source.factory" configuration', 1557238721); @@ -39,15 +39,18 @@ public function create(array $configuration): Preset throw new \RuntimeException(sprintf('The configured "source.factory" %s is not an instance of %s', $factoryClassName, DataSourceFactoryInterface::class), 1557238800); } $options = $configuration['source']['options'] ?? []; + if ($customSourceOptions !== null) { + $options = [...$options, ...$customSourceOptions]; + } try { $dataSourceFactory->optionsSchema()->validate($options); } catch (\InvalidArgumentException $e) { throw new \RuntimeException(sprintf('Failed to create data source for factory of type %s: %s', $factoryClassName, $e->getMessage()), 1716822862, $e); } - return $this->createWithDataSource($configuration, $dataSourceFactory->create($options)); + return $this->createWithDataSource($configuration, $dataSourceFactory->create($options), $customTargetOptions); } - public function createWithDataSource(array $configuration, DataSourceInterface $dataSource): Preset + public function createWithDataSource(array $configuration, DataSourceInterface $dataSource, array|null $customTargetOptions): Preset { if (!isset($configuration['mapping'])) { throw new \RuntimeException('Missing "mapping" configuration', 1558080904); @@ -71,6 +74,9 @@ public function createWithDataSource(array $configuration, DataSourceInterface $ throw new \RuntimeException(sprintf('The configured "target.factory" %s is not an instance of %s', $factoryClassName, DataTargetFactoryInterface::class), 1557238877); } $options = $configuration['target']['options'] ?? []; + if ($customTargetOptions !== null) { + $options = [...$options, ...$customTargetOptions]; + } try { $dataTargetFactory->optionsSchema()->validate($options); } catch (\InvalidArgumentException $e) { diff --git a/README.md b/README.md index df00eff..2b91a1b 100644 --- a/README.md +++ b/README.md @@ -126,9 +126,11 @@ Synchronizes data (add, update,delete) for the given preset. #### Options ``` - --quiet If set, no output, apart from errors, will be displayed - --force-updates If set, all local records will be updated regardless of their version/timestamp. This is useful for node type changes that require new data to be fetched - --from-fixture If set, the data will be loaded from a local fixture file instead of the configured data source + --quiet If set, no output, apart from errors, will be displayed + --force-updates If set, all local records will be updated regardless of their version/timestamp. This is useful for node type changes that require new data to be fetched + --from-fixture If set, the data will be loaded from a local fixture file instead of the configured data source + --override-source-options Allows to override default options for the data source via JSON, e.g. '{"endpoint":"https://some-custom.tld/endpoint"}' + --override-target-options Allows to override default options for the data target via JSON, e.g. '{"endpoint":"https://some-custom.tld/endpoint"}' ``` ### `import:prune`