Skip to content

Spring Boot 3.3 upgrade causes serialization loss of DatastorePageable.urlSafeCursor #2940

@burkedavison

Description

@burkedavison

@odrotbohm , could you verify that the below understanding is correct? Do you have any recommendations to resolve this issue?


PR #2923 shows the following test failure when updating from Spring Boot 3.2 to 3.3:

Error: 2:038 [ERROR] com.example.DatastoreBookshelfExampleIntegrationTests.testSerializedPage -- Time elapsed: 0.474 s <<< FAILURE!
java.lang.AssertionError: 

Expecting actual:
  "***"content":[***"id":12345678***],"page":***"size":1,"number":0,"totalElements":4,"totalPages":4***"
to contain pattern:
  ""pageable":.+"urlSafeCursor":".+""

	at com.example.DatastoreBookshelfExampleIntegrationTests.testSerializedPage(DatastoreBookshelfExampleIntegrationTests.java:82)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

The test is verifying that urlSafeCursor (defined in Spring Cloud GCP's DatastorePageable) is part of the response body. I've tracked the change in behavior down to Spring Data's new registration of PagedModel which is now used when serializing a PageImpl with Jackson. This PageModel class produces the serialized page entry in the above 'actual' test results. This change was made "to make sure the representations stay stable and do not expose unnecessary implementation details".

I believe the intent was to introduce this new converter as opt-in only to preserve backward compatibility, based on the documentation for this change

Configures how to render PageImpl instances. Defaults to PageSerializationMode#DIRECT for backward compatibility reasons.

and the commit message

@EnableSpringDataWeb support now contains a pageSerializationMode attribute set to an enum with two possible values: DIRECT, which is the default for backwards compatibility reasons.

However, the logic determining the activation of this converter is only backwardly compatible for users of the @EnableSpringDataWebSupport annotation. If the annotation is not being used, the default behavior is to now use the PagedModel converter.

if (settings != null // When using the @EnableSpringDataWebSupport annotation...
    && settings.pageSerializationMode() == PageSerializationMode.DIRECT) 
{
    setMixInAnnotation(PageImpl.class, WarningMixing.class);
} else {
    // When not using the @EnableSpringDataWebSupport annotation...
    setMixInAnnotation(PageImpl.class, WrappingMixing.class);
}
// ...
@JsonSerialize(converter = PlainPageSerializationWarning.class)
abstract class WarningMixing {}

@JsonSerialize(converter = PageModelConverter.class)
abstract class WrappingMixing {} // This class uses the new PageModelConverter.

I believe the intent was for the logic to be:

if (settings == null || settings.pageSerializationMode() == PageSerializationMode.DIRECT) {
    // Use the PlainPageSerializationWarning converter
} else {
    // Use the PageModelConverter
}

Add'l References

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions