diff --git a/src/Configurator/AddLinesConfigurator.php b/src/Configurator/AddLinesConfigurator.php index 99443575..686abfec 100644 --- a/src/Configurator/AddLinesConfigurator.php +++ b/src/Configurator/AddLinesConfigurator.php @@ -129,8 +129,9 @@ public function executeConfigure(Recipe $recipe, $config): void continue; } $target = isset($patch['target']) ? $patch['target'] : null; + $createIfMissing = isset($patch['create_if_missing']) ? $patch['create_if_missing'] : null; - $newContents = $this->getPatchedContents($file, $content, $position, $target, $warnIfMissing); + $newContents = $this->getPatchedContents($file, $content, $position, $target, $warnIfMissing, $createIfMissing); $this->fileContents[$file] = $newContents; } } @@ -164,7 +165,7 @@ public function executeUnconfigure(Recipe $recipe, $config): void } } - private function getPatchedContents(string $file, string $value, string $position, ?string $target, bool $warnIfMissing): string + private function getPatchedContents(string $file, string $value, string $position, ?string $target, bool $warnIfMissing, ?string $createIfMissing = null): string { $fileContents = $this->readFile($file); @@ -192,6 +193,15 @@ private function getPatchedContents(string $file, string $value, string $positio break; } } + + if (!$targetFound && null !== $createIfMissing) { + // Insert the create_if_missing content at the end of the file, then add the value after the target + $createIfMissingLines = explode("\n", $createIfMissing); + $lines = array_merge($lines, $createIfMissingLines); + $lines[] = $value; + $targetFound = true; + } + $fileContents = implode("\n", $lines); if (!$targetFound) { diff --git a/tests/Configurator/AddLinesConfiguratorTest.php b/tests/Configurator/AddLinesConfiguratorTest.php index 03602b16..918e97a3 100644 --- a/tests/Configurator/AddLinesConfiguratorTest.php +++ b/tests/Configurator/AddLinesConfiguratorTest.php @@ -198,6 +198,66 @@ public function testSkippedIfTargetCannotBeFound() $this->assertSame($originalContent, $this->readFile('webpack.config.js')); } + public function testCreateIfMissingCreatesTargetAndAddsContent() + { + $this->copyFixture('ai_without_store.yaml', 'config/packages/ai.yaml'); + + $this->runConfigure([ + [ + 'file' => 'config/packages/ai.yaml', + 'position' => 'after_target', + 'target' => ' store:', + 'create_if_missing' => ' store:', + 'content' => " azuresearch:\n default:\n endpoint: '%env(AZURE_SEARCH_ENDPOINT)%'", + ], + ]); + + $this->assertSame( + $this->loadFixture('ai_without_store_expected.yaml'), + $this->readFile('config/packages/ai.yaml') + ); + } + + public function testCreateIfMissingWithExistingTarget() + { + $this->copyFixture('ai_with_store.yaml', 'config/packages/ai.yaml'); + + $this->runConfigure([ + [ + 'file' => 'config/packages/ai.yaml', + 'position' => 'after_target', + 'target' => ' store:', + 'create_if_missing' => ' store:', + 'content' => " azuresearch:\n default:\n endpoint: '%env(AZURE_SEARCH_ENDPOINT)%'", + ], + ]); + + $this->assertSame( + $this->loadFixture('ai_with_store_expected.yaml'), + $this->readFile('config/packages/ai.yaml') + ); + } + + public function testCreateIfMissingNotUsedWhenOptionNotSet() + { + $this->copyFixture('ai_without_store.yaml', 'config/packages/ai.yaml'); + + $this->runConfigure([ + [ + 'file' => 'config/packages/ai.yaml', + 'position' => 'after_target', + 'target' => ' store:', + 'content' => " azuresearch:\n default:\n endpoint: '%env(AZURE_SEARCH_ENDPOINT)%'", + ], + ]); + + // Content should remain unchanged since target was not found and no create_if_missing was provided + $this->assertSame( + $this->loadFixture('ai_without_store.yaml'), + $this->readFile('config/packages/ai.yaml') + ); + } + public function testPatchIgnoredIfValueAlreadyExists() { $originalContents = <<saveFile($targetPath, $this->loadFixture($fixtureName)); + } + private function createComposerMockWithPackagesInstalled(array $packages) { $packages = array_map(fn ($package) => explode(':', $package), $packages); diff --git a/tests/Configurator/Fixtures/AddLines/ai_with_store.yaml b/tests/Configurator/Fixtures/AddLines/ai_with_store.yaml new file mode 100644 index 00000000..8d23b232 --- /dev/null +++ b/tests/Configurator/Fixtures/AddLines/ai_with_store.yaml @@ -0,0 +1,5 @@ +ai: + store: + pinecone: + default: + api_key: 'xxx' diff --git a/tests/Configurator/Fixtures/AddLines/ai_with_store_expected.yaml b/tests/Configurator/Fixtures/AddLines/ai_with_store_expected.yaml new file mode 100644 index 00000000..ad9f1263 --- /dev/null +++ b/tests/Configurator/Fixtures/AddLines/ai_with_store_expected.yaml @@ -0,0 +1,8 @@ +ai: + store: + azuresearch: + default: + endpoint: '%env(AZURE_SEARCH_ENDPOINT)%' + pinecone: + default: + api_key: 'xxx' diff --git a/tests/Configurator/Fixtures/AddLines/ai_without_store.yaml b/tests/Configurator/Fixtures/AddLines/ai_without_store.yaml new file mode 100644 index 00000000..49312f76 --- /dev/null +++ b/tests/Configurator/Fixtures/AddLines/ai_without_store.yaml @@ -0,0 +1,4 @@ +ai: + platform: + default: + api_key: '%env(OPENAI_API_KEY)%' diff --git a/tests/Configurator/Fixtures/AddLines/ai_without_store_expected.yaml b/tests/Configurator/Fixtures/AddLines/ai_without_store_expected.yaml new file mode 100644 index 00000000..f953375a --- /dev/null +++ b/tests/Configurator/Fixtures/AddLines/ai_without_store_expected.yaml @@ -0,0 +1,8 @@ +ai: + platform: + default: + api_key: '%env(OPENAI_API_KEY)%' + store: + azuresearch: + default: + endpoint: '%env(AZURE_SEARCH_ENDPOINT)%'