fix: #3586729 Add existence checks to SetupVdbServer and SetupVdbIndex config actions

Description

I have added existence checks to SetupVdbServer::apply() and SetupVdbIndex::apply(). Recipes using these actions, like ai_recipe_content_search_vector, can now be re-applied without failing.

SetupVdbIndex::apply() loads the target index first. If it exists, the action is a no-op instead of calling create()->save() unconditionally.

SetupVdbServer::apply() loads the target server first. If it exists, I compare the recipe's remote-relevant backend_config keys (database, database_settings, embeddings_engine, embeddings_engine_configuration, embedding_strategy, embedding_strategy_configuration) against the stored server. A match is a no-op. A conflict throws a ConfigActionException naming the dotted paths that differ, instead of the old generic "Could not save the configuration."

Closes #3586729

Testing instructions

Automated

Run:

ddev exec bash -c "SIMPLETEST_BASE_URL=https://drupal.ddev.site SIMPLETEST_DB=mysql://db:db@db/db phpunit --bootstrap /var/www/html/web/core/tests/bootstrap.php web/modules/contrib/ai/tests/src/Kernel/Plugin/ConfigAction/SetupVdbIndexTest.php web/modules/contrib/ai/tests/src/Kernel/Plugin/ConfigAction/SetupVdbServerIdempotencyTest.php web/modules/contrib/ai/tests/src/Kernel/Plugin/ConfigAction/SetupVdbServerTest.php"

10 tests should pass, including the new no-op-on-match and conflict-throws-on-mismatch assertions.

Manual — reproduce the bug, then confirm the fix, with a real recipe

Needs a working AI provider set as the default embeddings model (ddev launch $(ddev drush uli /admin/config/ai), add an OpenAI or Anthropic key, set it as default under Embeddings).

  1. On main, apply the recipe once. It should succeed:
    ddev drush recipe:apply web/modules/contrib/ai_recipe_content_search_vector
  2. Still on main, apply it again. This reproduces the bug:
    ddev drush recipe:apply web/modules/contrib/ai_recipe_content_search_vector
    You should see In SetupVdbServer.php line 116: Could not save the configuration. The recipe fails outright, matching the issue report.
  3. Delete the config from step 1 (ddev drush config:delete search_api.server.content_vector, ddev drush config:delete search_api.index.content_vector), then switch to this branch.
  4. Apply the recipe once. Same as before, it succeeds.
  5. Apply it again. With this fix, it now completes without error instead of failing.
  6. To check the conflict path: edit the recipe's embedding_strategy_configuration.chunk_size value, then re-apply. You should get a ConfigActionException naming the conflicting path (backend_config.embedding_strategy_configuration.chunk_size), not the old generic error.

Checklist

  • I have linked the related issue in the MR title or description
  • I have performed a self-review of my own code
  • I have added or updated tests, or explained in the description why this change is not covered by tests
  • I have updated documentation for any new or changed functionality
  • I have written testing instructions and verified them locally
  • I have noted any required post-merge steps (config imports, cache rebuilds, manual changes)
  • This MR contains no breaking API or hook changes, or they are explicitly documented in the description

AI Compliance

Note

Check the one that best describes your usage, or leave all unchecked if AI was not significantly used.

  • AI Assisted Code
    Mainly written by a human; AI used for autocomplete or partial generation under full human supervision.

  • AI Generated Code
    Mainly generated by AI, reviewed and approved by a human before this MR was created.

  • Vibe Coded
    Generated by AI and only functionally reviewed before this MR was created.

Merge request reports

Loading