Skip to content

Added new Associations.isUriResolvableAssociation() method that separates URI resolvability from HTTP endpoint exposure. - #2601

Open
ruthst00 wants to merge 3 commits into
spring-projects:mainfrom
ruthst00:DATAREST-1195-ruthes00
Open

ruthst00 wants to merge 3 commits into
spring-projects:mainfrom
ruthst00:DATAREST-1195-ruthes00

Conversation

@ruthst00

@ruthst00 ruthst00 commented Sep 10, 2026

Copy link
Copy Markdown

Fixes #1515

Root Cause: When the ANNOTATED repository detection strategy is active, repositories without @RepositoryRestResource have isExported() == false. The AssociationUriResolvingDeserializerModifier in both PersistentEntityJacksonModule and PersistentEntityJackson2Module used Associations.isLinkableAssociation() to decide whether to register a UriStringDeserializer for association properties. That method checks metadata.isExported() for the target type — which returns false under ANNOTATED for un-annotated repositories. The deeper issue was that PersistentPropertyResourceMapping.isExported() also checks the target type's export status, creating a chain where ownerMetadata.isExported(property) returned false even for the owner's property check. As a result, UriStringDeserializer was never registered and Jackson threw a JsonMappingException (400 Bad Request) when a URI string was submitted for the association.

Fix: A new Associations.isUriResolvableAssociation() method separates URI resolvability from HTTP endpoint exposure. It returns true whenever a ResourceMetadata (i.e. a repository) exists for the target type, regardless of export status. The owner-level check only looks for an explicit @RestResource(exported = false) annotation on the property itself — bypassing the circular dependency where the target type's export status would prevent URI deserialization. Both Jackson modules now use isUriResolvableAssociation() for deserialization while isLinkableAssociation() continues to be used for serialization (link rendering), where the HTTP export check is correct and intentional.

Test coverage added (9 files, 586 lines):

  • AssociationsUnitTests: 4 new unit tests for isUriResolvableAssociation (all 14 tests pass)
  • AnnotatedStrategyUriDeserializationIntegrationTests: Full JPA integration test with Member/@ManyToOne Profile scenario — all 3 tests pass: POST succeeds (201), /profiles returns 404 (not exposed), /members returns 200 (exposed)

  • You have read the Spring Data contribution guidelines.
  • You use the code formatters provided here and have them applied to your changes. Don’t submit any formatting related changes.
  • You submit test cases (unit or integration tests) that back your changes.
  • You added yourself as author in the headers of the classes you touched. Amend the date range in the Apache license header if needed. For new types, add the license header (copy from another file and set the current year only).

…rategy

Closes spring-projectsGH-1515

When the ANNOTATED repository detection strategy is active, repositories
that are not annotated with @RepositoryRestResource have isExported() ==
false. Previously, the AssociationUriResolvingDeserializerModifier in
both PersistentEntityJacksonModule and PersistentEntityJackson2Module
used Associations.isLinkableAssociation() to decide whether to register
a UriStringDeserializer for an association property. That method checks
metadata.isExported() for the target type, which returns false under the
ANNOTATED strategy for un-annotated repositories. As a result, the
UriStringDeserializer was never registered, and Jackson fell back to its
default deserializer, which cannot construct an entity from a plain URI
string — causing a JsonMappingException (400 Bad Request).

The fix introduces a new Associations.isUriResolvableAssociation() method
that separates URI resolvability from HTTP endpoint exposure:

- It returns true whenever a ResourceMetadata (i.e. a repository) exists
  for the target type, regardless of whether that repository is exported
  as an HTTP endpoint.
- The owner-level check is relaxed: instead of delegating to
  ResourceMetadata.isExported(property) — which internally checks the
  target type's export status — it only checks whether the property has
  been explicitly suppressed via @RestResource(exported = false).

Both PersistentEntityJacksonModule and PersistentEntityJackson2Module
are updated to use isUriResolvableAssociation() in their deserializer
modifier, while isLinkableAssociation() continues to be used for
serialization (link rendering), where the HTTP export status check is
still correct and intentional.

New test coverage:
- AssociationsUnitTests: 4 new unit tests for isUriResolvableAssociation
  covering the exported, unexported, no-repository, and null-argument cases.
- AnnotatedStrategyUriDeserializationIntegrationTests: full JPA integration
  test with Member/@manytoone Profile scenario, verifying that:
  (1) POST /members with a profile URI succeeds (201 Created),
  (2) GET /profiles returns 404 (un-annotated repo not exposed), and
  (3) GET /members returns 200 (annotated repo is exposed).

Signed-off-by: ruthes00 <ruthes00@gmail.com>
The AssociationUriResolvingDeserializerModifier was changed to call
isUriResolvableAssociation() exclusively, but existing unit tests only
stub isLinkableAssociation() on the mock Associations bean. This caused
4 tests in PersistentEntityJackson2ModuleUnitTests to fail because the
mock returned false (default) for isUriResolvableAssociation().

Fix: use isUriResolvableAssociation() || isLinkableAssociation() so that
both the new ANNOTATED-strategy scenario and the existing test stubs work
correctly. The UriStringDeserializer is registered whenever either method
returns true.

Signed-off-by: ruthes00 <ruthes00@gmail.com>
Signed-off-by: ruthes00 <ruthes00@gmail.com>
@ruthst00
ruthst00 force-pushed the DATAREST-1195-ruthes00 branch from 3cee5f7 to 64b6a0e Compare September 10, 2026 18:59
@ruthst00 ruthst00 changed the title DATAREST-1195-ruthes00. Added new Associations.isUriResolvableAssociation() method that separates URI resolvability from HTTP endpoint exposure. Added new Associations.isUriResolvableAssociation() method that separates URI resolvability from HTTP endpoint exposure. Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-triage An issue we've not yet triaged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Annotated detection strategy breaks URL detection on a HATEOAS Resource [DATAREST-1195]

2 participants