Skip to content

feat(grails-data-neo4j): wire Neo4j adapter to GormRegistry's GormApiFactory mechanism (PR3)#15817

Open
borinquenkid wants to merge 1 commit into
feat/neo4j-settings-gradle-foldfrom
feat/neo4j-gorm-registry-wiring
Open

feat(grails-data-neo4j): wire Neo4j adapter to GormRegistry's GormApiFactory mechanism (PR3)#15817
borinquenkid wants to merge 1 commit into
feat/neo4j-settings-gradle-foldfrom
feat/neo4j-gorm-registry-wiring

Conversation

@borinquenkid

@borinquenkid borinquenkid commented Jul 3, 2026

Copy link
Copy Markdown
Member

📖 Neo4j GormRegistry migration — reading order: 3 of 5:

  1. Migrate grails-data-neo4j to Groovy 4/Jakarta/GORM 8 baseline #15816 — PR1: Groovy4/Jakarta/GORM8 baseline migration (no GormRegistry coupling)
  2. feat(grails-data-neo4j): fold standalone build into root settings.gradle (PR2) #15951 — PR2: fold standalone build into root settings.gradle
  3. feat(grails-data-neo4j): wire Neo4j adapter to GormRegistry's GormApiFactory mechanism (PR3) #15817 — PR3: wire Neo4jGormApiFactory into GormRegistry — this PR
  4. Re-add Neo4j example apps and docs into monorepo (PR4) #15832 — PR4: re-add example apps and docs into monorepo
  5. chore(grails-data-neo4j): clean up Checkstyle/CodeNarc violations (PR5) #15833 — PR5: Checkstyle/CodeNarc cleanup

You are reading 3 of 5.

This Neo4j chain branches off the core GormRegistry O(M+N) scaling stack's head (#15779#15780#15790) — it depends on that stack but is its own separate sequence, not additional steps within it.

Previously combined PR2 (wiring) and PR3 (settings.gradle fold) in this one PR; split per review feedback so the fold lands earlier (#15951, now PR2) and this PR is wiring-only.

Summary

Completes the Neo4j GormRegistry migration plan's PR3, on top of #15816 (PR1, baseline) and #15951 (PR2, settings.gradle fold): registers a Neo4jGormApiFactory so entities backed by Neo4jDatastore resolve a Neo4jGormStaticApi through GormRegistry instead of silently falling back to the generic GormStaticApi. Without this, static/cypher-string API calls (cypherStatic, findRelationship(s), findPath*, findShortestPath, find/findAll with a cypher string) threw ClassCastException or UnsupportedOperationException, since those methods only exist on Neo4jGormStaticApi.

This turned out to require far less than the "rewrite Neo4jEntity/Node/Relationship traits" originally scoped in the migration plan - a nice, concrete dividend of the GormRegistry refactor: adding a new datastore's wiring is now a small, mechanical exercise rather than an integration project. Neo4j's instance and validation APIs were already generic (GormInstanceApi/GormValidationApi, no Neo4j-specific subclass), matching DefaultGormApiFactory's base implementations already - only the static API needed a factory override, mirroring MongoGormApiFactory's exact shape (which only overrides createStaticApi() for the same reason). The whole change is a ~25-line factory class plus a single line registering it.

  • Neo4jGormApiFactory#createStaticApi() resolves the datastore via DatastoreResolver#resolve() rather than Neo4j's old bespoke getDatastoreForQualifier()/datastoresByConnectionSource logic - qualifier/multi-datasource routing is now handled generically by GormRegistry/GormApiResolver (the O(M+N) scaling work this plan depends on). Verified this doesn't regress Neo4j's own multi-tenancy/multi-datasource tests.
  • registerApiFactory() is called from Neo4jDatastore#initialize(), before constructing the GormEnhancer whose constructor eagerly registers this datastore's entities - registering after would leave those entities bound to the generic factory forever, since GormEnhancer only registers each entity once.
  • 17 of the 34 tests marked @PendingFeature in the baseline PR now pass and had their annotations removed (ApiExtensionsSpec, CypherQueryStringSpec, OneToManyUpdateSpec, MultiTenancySpec, PathSpec, RelationshipSpec, NativeIdentityGeneratorSpec's saveAll test - whose fix was already in place from the baseline PR but unreachable until this factory was registered).

Test plan

  • ./gradlew :grails-datastore-gorm-neo4j:test - BUILD SUCCESSFUL, 198/215 passing, 0 failures, 17 skipped (4 genuinely pending and unrelated to this change, plus pre-existing @Ignore'd tests)
  • Verified no regressions in previously-passing tests, including multi-tenancy/multi-datasource specs, after switching to the generic DatastoreResolver-based routing

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings July 3, 2026 03:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Wires the Neo4j GORM adapter into the core GormRegistry API-factory mechanism so Neo4j-backed entities resolve Neo4jGormStaticApi (instead of falling back to the generic GormStaticApi), restoring Neo4j-specific static/cypher APIs and allowing previously-pending Neo4j specs to execute normally.

Changes:

  • Add Neo4jGormApiFactory that overrides static API creation to return Neo4jGormStaticApi.
  • Register the Neo4j API factory during Neo4jDatastore initialization before entity enhancement occurs.
  • Remove @PendingFeature annotations from multiple Neo4j specs that now pass with the correct static API resolution.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jGormApiFactory.groovy Introduces a Neo4j-specific GormApiFactory that produces Neo4jGormStaticApi instances.
grails-data-neo4j/grails-datastore-gorm-neo4j/src/main/groovy/org/grails/datastore/gorm/neo4j/Neo4jDatastore.java Registers the Neo4j API factory early in datastore initialization to ensure entities bind to the Neo4j factory.
grails-data-neo4j/grails-datastore-gorm-neo4j/src/test/groovy/org/grails/datastore/gorm/neo4j/ApiExtensionsSpec.groovy Unblocks cypher query spec by removing @PendingFeature.
grails-data-neo4j/grails-datastore-gorm-neo4j/src/test/groovy/grails/gorm/tests/CypherQueryStringSpec.groovy Removes @PendingFeature from string/cypher static query tests now supported via Neo4jGormStaticApi.
grails-data-neo4j/grails-datastore-gorm-neo4j/src/test/groovy/grails/gorm/tests/OneToManyUpdateSpec.groovy Removes @PendingFeature now that cypherStatic resolves to the Neo4j static API.
grails-data-neo4j/grails-datastore-gorm-neo4j/src/test/groovy/grails/gorm/tests/NativeIdentityGeneratorSpec.groovy Removes @PendingFeature for saveAll now reachable via Neo4jGormStaticApi.
grails-data-neo4j/grails-datastore-gorm-neo4j/src/test/groovy/grails/gorm/tests/multitenancy/MultiTenancySpec.groovy Removes @PendingFeature now that cypher-string find resolves to Neo4j’s static API under multi-tenancy.
grails-data-neo4j/grails-datastore-gorm-neo4j/src/test/groovy/grails/gorm/tests/path/PathSpec.groovy Removes @PendingFeature for Neo4j path APIs now resolved from the correct static API.
grails-data-neo4j/grails-datastore-gorm-neo4j/src/test/groovy/grails/gorm/tests/path/RelationshipSpec.groovy Removes @PendingFeature for relationship finder APIs now resolved from the correct static API.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 49.6272%. Comparing base (e59c4bf) to head (087b7bc).

Additional details and impacted files

Impacted file tree graph

@@                            Coverage Diff                            @@
##             feat/neo4j-settings-gradle-fold     #15817        +/-   ##
=========================================================================
- Coverage                            49.6442%   49.6272%   -0.0171%     
+ Complexity                             17041      17033         -8     
=========================================================================
  Files                                   1961       1961                
  Lines                                  93741      93741                
  Branches                               16465      16465                
=========================================================================
- Hits                                   46537      46521        -16     
- Misses                                 39961      39978        +17     
+ Partials                                7243       7242         -1     

see 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Completes the Neo4j GormRegistry migration plan's PR2: registers a
Neo4jGormApiFactory so entities backed by Neo4jDatastore resolve a
Neo4jGormStaticApi through GormRegistry instead of silently falling back
to the generic GormStaticApi. Without this, static/cypher-string API calls
(cypherStatic, findRelationship(s), findPath*, findShortestPath, find/
findAll with a cypher string) threw ClassCastException or
UnsupportedOperationException, since those methods only exist on
Neo4jGormStaticApi.

Turned out to require far less than the "rewrite Neo4jEntity/Node/
Relationship traits" originally scoped in the migration plan: Neo4j's
instance and validation APIs were already generic (GormInstanceApi/
GormValidationApi, no Neo4j-specific subclass), matching the
DefaultGormApiFactory's base implementations already. Only the static API
needed a factory override - mirroring MongoGormApiFactory's exact shape,
which only overrides createStaticApi() for the same reason.

Neo4jGormApiFactory#createStaticApi() resolves the datastore via
DatastoreResolver#resolve() rather than Neo4j's old bespoke
getDatastoreForQualifier()/datastoresByConnectionSource logic, since
qualifier/multi-datasource routing is now handled generically by
GormRegistry/GormApiResolver (the "O(M+N) scaling" work this plan depends
on). Verified this doesn't regress Neo4j's own multi-tenancy/multi-datasource
tests.

registerApiFactory() is called from Neo4jDatastore#initialize(), before
constructing the GormEnhancer whose constructor eagerly registers this
datastore's entities - registering after would leave those entities bound
to the generic factory forever, since GormEnhancer only registers each
entity once.

17 of the 34 tests marked @PendingFeature in the prior baseline-migration
commit now pass and had their annotations removed (ApiExtensionsSpec,
CypherQueryStringSpec, OneToManyUpdateSpec, MultiTenancySpec, PathSpec,
RelationshipSpec, NativeIdentityGeneratorSpec's saveAll test, whose fix
was already in place but unreachable until this factory was registered).
Full suite: 198/215 passing, 0 failures, 17 skipped (4 genuinely pending
- unrelated to this change - plus pre-existing @ignore'd tests).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@borinquenkid borinquenkid force-pushed the feat/neo4j-gorm-registry-wiring branch from c5ced04 to 087b7bc Compare July 10, 2026 02:27
@borinquenkid borinquenkid changed the base branch from build/neo4j-groovy4-baseline to feat/neo4j-settings-gradle-fold July 10, 2026 02:27
@borinquenkid borinquenkid changed the title Wire Neo4j adapter to GormRegistry's GormApiFactory mechanism (PR2) feat(grails-data-neo4j): wire Neo4j adapter to GormRegistry's GormApiFactory mechanism (PR3) Jul 10, 2026
@testlens-app

testlens-app Bot commented Jul 10, 2026

Copy link
Copy Markdown

✅ All tests passed ✅

🏷️ Commit: 087b7bc
▶️ Tests: 11955 executed
⚪️ Checks: 39/39 completed


Learn more about TestLens at testlens.app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants