Description
HTTP PATCH requests with an "op": "test" operation on an enum field always fail because the JSON String's String value is compared to the enum's enum-type value.
(In all versions checked: probably-latest of each Spring Boot 3.x and 4.0, 4.1)
Steps to Reproduce
Minimally-reproducing test repository, against each of Spring Boot 3.{0-5} and 4.{0-1}:
https://github.com/ebrannin-bw/spring-patch-test-enum-bug-report
(The above repo is mostly Claude-written. This issue is human-written, except for the code-blocks, which were either from the Claude-written test code or copied from test output.)
Data Class
(With Lombok)
@Entity
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Widget {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Integer amount;
@Enumerated(EnumType.STRING)
@Column(columnDefinition = "varchar(255)")
private JobStatus status;
}
Basic JpaRepository
@RepositoryRestResource
public interface WidgetRepository extends JpaRepository<Widget, Long> {
}
Request payload
[
{ "op": "test", "path": "/status", "value": "NEW" },
{ "op": "replace", "path": "/name", "value": "Updated Widget" }
]
Expected Result
The PATCH succeeds if widget.getStatus() == NEW
Observed Result
The PATCH always fails, with a stack trace like this:
org.springframework.http.converter.HttpMessageNotReadableException: Could not read an object of type class com.example.spring.patch.enumbug.Widget from the request
at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.readPatch(PersistentEntityResourceHandlerMethodArgumentResolver.java:226) ~[spring-data-rest-webmvc-5.1.0.jar:5.1.0]
at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.lambda$read$0(PersistentEntityResourceHandlerMethodArgumentResolver.java:196) ~[spring-data-rest-webmvc-5.1.0.jar:5.1.0]
at java.base/java.util.Optional.map(Optional.java:260) ~[na:na]
at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.read(PersistentEntityResourceHandlerMethodArgumentResolver.java:193) ~[spring-data-rest-webmvc-5.1.0.jar:5.1.0]
at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.resolveArgument(PersistentEntityResourceHandlerMethodArgumentResolver.java:136) ~[spring-data-rest-webmvc-5.1.0.jar:5.1.0]
...
Caused by: org.springframework.data.rest.webmvc.json.patch.PatchException: Test against path '/status' failed
at org.springframework.data.rest.webmvc.json.patch.TestOperation.perform(TestOperation.java:78) ~[spring-data-rest-webmvc-5.1.0.jar:5.1.0]
at org.springframework.data.rest.webmvc.json.patch.Patch.apply(Patch.java:66) ~[spring-data-rest-webmvc-5.1.0.jar:5.1.0]
at org.springframework.data.rest.webmvc.config.JsonPatchHandler.applyPatch(JsonPatchHandler.java:94) ~[spring-data-rest-webmvc-5.1.0.jar:5.1.0]
at org.springframework.data.rest.webmvc.config.JsonPatchHandler.apply(JsonPatchHandler.java:82) ~[spring-data-rest-webmvc-5.1.0.jar:5.1.0]
at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.readPatch(PersistentEntityResourceHandlerMethodArgumentResolver.java:217) ~[spring-data-rest-webmvc-5.1.0.jar:5.1.0]
... 113 common frames omitted
Description
HTTP PATCH requests with an
"op": "test"operation on an enum field always fail because the JSON String'sStringvalue is compared to the enum'senum-typevalue.(In all versions checked: probably-latest of each Spring Boot 3.x and 4.0, 4.1)
Steps to Reproduce
Minimally-reproducing test repository, against each of Spring Boot 3.{0-5} and 4.{0-1}:
https://github.com/ebrannin-bw/spring-patch-test-enum-bug-report
(The above repo is mostly Claude-written. This issue is human-written, except for the code-blocks, which were either from the Claude-written test code or copied from test output.)
Data Class
(With Lombok)
Basic
JpaRepositoryRequest payload
[ { "op": "test", "path": "/status", "value": "NEW" }, { "op": "replace", "path": "/name", "value": "Updated Widget" } ]Expected Result
The
PATCHsucceeds ifwidget.getStatus() == NEWObserved Result
The
PATCHalways fails, with a stack trace like this: