From dc23d4722798f787c66f28c041658b290ec8ff5e Mon Sep 17 00:00:00 2001 From: Thijs De Paepe Date: Mon, 2 Dec 2024 14:28:43 +0100 Subject: [PATCH] fix: storeAction: false action --- src/Component/Action/StoreAction.php | 7 +- .../Akeneo/Client/HttpReaderFactory.php | 12 ++- .../Common/Utils/ContextFormatter.php | 83 +++++++++++++++---- .../Configurator/ConfigurationManager.php | 69 ++++++++------- .../Source/SourceCollectionFactory.php | 2 +- .../Common/Utils/ContextFormatterTest.php | 67 +++++++++++++++ 6 files changed, 191 insertions(+), 49 deletions(-) diff --git a/src/Component/Action/StoreAction.php b/src/Component/Action/StoreAction.php index 664beb0c..38a9a5a4 100644 --- a/src/Component/Action/StoreAction.php +++ b/src/Component/Action/StoreAction.php @@ -126,13 +126,16 @@ public function apply(array $item): array $item = $this->trueActionProcessor->process($item); } + $this->storeProduct($identifier); + + return $item; + } else { + // see GroupAction, get actionProcessor, process your action(s) if ([] !== $falseAction) { $item = $this->falseActionProcessor->process($item); } - $this->storeProduct($identifier); - return $item; } diff --git a/src/Component/Akeneo/Client/HttpReaderFactory.php b/src/Component/Akeneo/Client/HttpReaderFactory.php index 3a10c195..398eb2e4 100644 --- a/src/Component/Akeneo/Client/HttpReaderFactory.php +++ b/src/Component/Akeneo/Client/HttpReaderFactory.php @@ -9,6 +9,7 @@ use App\Component\Akeneo\Api\Resources\AkeneoProductsResource; use App\Component\Common\Cursor\MultiCursor; use App\Component\Common\Resource\EntityResourceInterface; +use App\Component\Common\Resource\SearchAbleEntityResourceInterface; use Assert\Assert; use Misery\Component\Common\Registry\RegisteredByNameInterface; use Misery\Component\Configurator\Configuration; @@ -90,9 +91,16 @@ public function createFromConfiguration(array $configuration, Configuration $con Assert::that($endpoint)->notNull('Unknown endpoint: ' . $endpoint); - /** @var EntityResourceInterface $resource */ + /** @var EntityResourceInterface|SearchAbleEntityResourceInterface $resource */ $resource = $resources->getResource($endpoint); + if (!empty($configuration['filter'])) { + $cursor = $resource->query($configuration['filter']); + + return new ApiReader($resource, $cursor); + } + dd($configuration); + $queryString = $context['limiters']['querystring'] ?? null; if (!empty($queryString)) { $cursor = $resource->querystring($queryString); @@ -100,7 +108,7 @@ public function createFromConfiguration(array $configuration, Configuration $con return new ApiReader($resource, $cursor); } - if (isset($configuration['identifier_filter_list'])) { + if (!empty($configuration['identifier_filter_list'])) { $cursor = new MultiCursor(); foreach ($configuration['identifier_filter_list'] as $identifier) { $cursor->addCursor($resource->getAllByAttributeCode($identifier)); diff --git a/src/Component/Common/Utils/ContextFormatter.php b/src/Component/Common/Utils/ContextFormatter.php index 363bb38e..0269345d 100644 --- a/src/Component/Common/Utils/ContextFormatter.php +++ b/src/Component/Common/Utils/ContextFormatter.php @@ -4,31 +4,84 @@ class ContextFormatter { - public static function format(array $context, array $data): array + /** + * Formats the data array by replacing placeholders with context values. + * + * @param array $context The context containing placeholder replacements. + * @param mixed $data The data to format (can be an array or scalar). + * @return mixed The formatted data. + */ + public static function format(array $context, $data): mixed { $replacements = []; foreach ($context as $key => $contextValue) { $replacements["%$key%"] = $contextValue; } - $newData = []; - foreach ($data as $key => $value) { - if (is_string($key)) { - $newKey = strtr($key, $replacements); - $newData[$newKey] = $value; - } else { - $newData[$key] = $value; + return self::recursiveFormat($data, $replacements); + } + + /** + * Recursively formats data by replacing placeholders in both keys and values. + * + * @param mixed $data The data to format. + * @param array $replacements The array of replacements. + * @return mixed The formatted data. + */ + private static function recursiveFormat($data, array $replacements): mixed + { + if (is_array($data)) { + $newData = []; + foreach ($data as $key => $value) { + // Replace placeholders in keys if the key is a string + $newKey = is_string($key) ? self::replacePlaceholdersInString($key, $replacements) : $key; + // Recursively format the value + $newValue = self::recursiveFormat($value, $replacements); + $newData[$newKey] = $newValue; } + return $newData; + } elseif (is_string($data)) { + // Replace the value if it's a string + return self::replacePlaceholdersInValue($data, $replacements); + } else { + // For other data types (int, float, bool, null), return as is + return $data; } + } - foreach ($newData as &$value) { - if (is_array($value)) { - $value = self::format($context, $value); - } elseif (is_string($value)) { - $value = strtr($value, $replacements); - } + /** + * Replaces placeholders in a string with corresponding values. + * + * @param string $string The string containing placeholders. + * @param array $replacements The array of replacements. + * @return mixed The replaced value. + */ + private static function replacePlaceholdersInString(string $string, array $replacements): mixed + { + if (array_key_exists($string, $replacements)) { + // If the entire string is a placeholder, return the replacement value directly + return $replacements[$string]; + } else { + // Otherwise, replace placeholders within the string + return strtr($string, array_filter($replacements, 'is_scalar')); } + } - return $newData; + /** + * Handles replacement when the value is a string. + * + * @param string $value The value containing placeholders. + * @param array $replacements The array of replacements. + * @return mixed The replaced value. + */ + private static function replacePlaceholdersInValue(string $value, array $replacements): mixed + { + if (array_key_exists($value, $replacements)) { + // Return the replacement directly, even if it's an array or object + return $replacements[$value]; + } else { + // Replace within the string if it's not an exact match + return strtr($value, array_filter($replacements, 'is_scalar')); + } } } diff --git a/src/Component/Configurator/ConfigurationManager.php b/src/Component/Configurator/ConfigurationManager.php index e609f976..443cc458 100644 --- a/src/Component/Configurator/ConfigurationManager.php +++ b/src/Component/Configurator/ConfigurationManager.php @@ -131,64 +131,58 @@ public function addContext(array $configuration): void * context grows, but needs to be reset to previous state per step */ public function addTransformationSteps(array $transformationSteps, array $masterConfiguration, array $context): void - { + {; /** @var ProjectDirectories $projectDirectories */ $projectDirectories = $this->factory->getFactory('project_directories'); $dirName = pathinfo($this->config->getContext('transformation_file'))['dirname'] ?? null; unset($masterConfiguration['transformation_steps']); - # list of transformations + // Iterate over the transformation steps foreach ($transformationSteps as $transformationFile) { - // this code detects the run | with option - // and creates virtual steps, so you don't need to repeat yourself + // Check if 'run' is specified if (isset($transformationFile['run'])) { $file = $transformationFile['run']; - $withArray = $transformationFile['with']; - - // Get the number of iterations needed - $iterationCount = count(current($withArray)); - // Iterate over each index - for ($i = 0; $i < $iterationCount; $i++) { - $context = []; + // Handle 'once_with' directive + if (isset($transformationFile['once_with'])) { + $this->addTransformationSteps([$file], $masterConfiguration, array_merge($context, $transformationFile['once_with'])); - // Build the context for the current index - foreach ($withArray as $key => $values) { - if (isset($values[$i])) { - $context[$key] = $values[$i]; - } + // Handle 'all_with' directive + } elseif (isset($transformationFile['all_with'])) { + // Iterate over each combination of parameters + foreach ($this->arrayCartesianItem($transformationFile['all_with']) as $comboContext) { + $this->addTransformationSteps([$file], $masterConfiguration, array_merge($context, $comboContext)); } - - $this->addTransformationSteps([$file], $masterConfiguration, $context); } continue; } - $file = $dirName . DIRECTORY_SEPARATOR . $transformationFile; - if (!is_file($file) && $projectDirectories->getTemplatePath()->isFile($transformationFile)) { - $file = $projectDirectories->getTemplatePath()->getAbsolutePath($transformationFile); + // Process the transformation file as before + $filePath = $dirName . DIRECTORY_SEPARATOR . $transformationFile; + if (!is_file($filePath) && $projectDirectories->getTemplatePath()->isFile($transformationFile)) { + $filePath = $projectDirectories->getTemplatePath()->getAbsolutePath($transformationFile); } else { - Assertion::file($file); + Assertion::file($filePath); } - // we need to start a new configuration manager. - $transformationFile = ArrayFunctions::array_filter_recursive(Yaml::parseFile($file), function ($value) { - return $value !== NULL; + // Read and parse the transformation file + $transformationContent = ArrayFunctions::array_filter_recursive(Yaml::parseFile($filePath), function ($value) { + return $value !== null; }); - $configuration = array_replace_recursive($transformationFile, $masterConfiguration); + $configuration = array_replace_recursive($transformationContent, $masterConfiguration); $configuration['context'] = array_merge($configuration['context'], $context); $configuration = $this->factory->parseDirectivesFromConfiguration($configuration); - // only start the process if our transformation file has a pipeline - if (!isset($transformationFile['pipeline']) && !isset($transformationFile['shell'])) { + // Start the process if the transformation file has a pipeline or shell + if (!isset($transformationContent['pipeline']) && !isset($transformationContent['shell'])) { continue; } (new ProcessManager($configuration))->startProcess(); - // TODO connect the outputs here + // Execute shell commands if any if ($shellCommands = $configuration->getShellCommands()) { $shellCommands->exec(); $configuration->clearShellCommands(); @@ -196,6 +190,23 @@ public function addTransformationSteps(array $transformationSteps, array $master } } + // Helper function to compute Cartesian product of arrays + private function arrayCartesianItem($arrays): array + { + $result = [[]]; + foreach ($arrays as $key => $values) { + $append = []; + foreach ($result as $resultData) { + foreach ((array) $values as $item) { + $resultData[$key] = $item; + $append[] = $resultData; + } + } + $result = $append; + } + return $result; + } + public function configureShellCommands(array $configuration): void { /** @var ShellCommandFactory $factory */ diff --git a/src/Component/Source/SourceCollectionFactory.php b/src/Component/Source/SourceCollectionFactory.php index 9fc5d8a4..f4bad15c 100644 --- a/src/Component/Source/SourceCollectionFactory.php +++ b/src/Component/Source/SourceCollectionFactory.php @@ -65,7 +65,7 @@ private function addSourceFileToCollection(SourceCollection $sourceCollection, s Assert::that($file)->file(); $path = pathinfo($file); - if (in_array(strtolower($path['extension']), ['json', 'buffer'])) { + if (in_array(strtolower($path['extension']), ['json', 'jsonl', 'buffer'])) { $sourceCollection->add( Source::createSimple(JsonFileParser::create($file), $alias ?? $path['basename']) ); diff --git a/tests/Component/Common/Utils/ContextFormatterTest.php b/tests/Component/Common/Utils/ContextFormatterTest.php index 01f2b33e..e85a0922 100644 --- a/tests/Component/Common/Utils/ContextFormatterTest.php +++ b/tests/Component/Common/Utils/ContextFormatterTest.php @@ -228,4 +228,71 @@ public function testContextFormatWithMultipleFoundValuesAndKeys() $this->assertEquals($expectedResult, $result); } + + public function testContextFormatWithMultipleMixedValuesAndKeys() + { + $context = [ + 'locale' => 'en_US', + 'code' => 'some_code', + 'idx', [], + 'filter' => null, + "id_list" => ['1', '2', '3'], + ]; + $data = [ + 'filter' => [ + [ + 'name' => 'my_filter', + 'source' => 'some_path.csv', + 'options' => [ + 'filter' => [ + 'code' => '%code%', + 'locale' => '%locale%', + ], + 'return_value' => '%code%', + ], + ] + ], + 'actions' => [ + 'first_action' => [ + 'context' => [ + '%code%' => 'code', + ] + ], + 'second_action' => [ + 'id_list' => '%id_list%', + 'filter' => '%filter%', + ], + ], + ]; + $expectedResult = [ + 'filter' => [ + [ + 'name' => 'my_filter', + 'source' => 'some_path.csv', + 'options' => [ + 'filter' => [ + 'code' => 'some_code', + 'locale' => 'en_US', + ], + 'return_value' => 'some_code', + ], + ] + ], + 'actions' => [ + 'first_action' => [ + 'context' => [ + 'some_code' => 'code', + ] + ], + 'second_action' => [ + 'filter' => null, + 'id_list' => ['1', '2', '3'], + ], + ] + ]; + + $result = ContextFormatter::format($context, $data); + + $this->assertEquals($expectedResult, $result); + } }