We now use a dedicated implementation of MessageSourceResolvable to avoid missing enum value translations actually causing exceptions. That also allows us to defer the calculation of the default (potentially translated in a default way).
Previously we assumed lookup types to always result in String based values. We now loosen that constraint to also allow other scalar types, mostly targeting numeric types like long and integer.
MappedProperties now exposes a factory method that uses the Jackson introspector for serialization (instead of deserialization) which now also includes read-only properties. Previously, read-only ones were not considered as they are excluded from the metadata if it is looked up for deserialization.
We now support adding a to a collection via JsonPatch expressions by using the collection's size as target index. Contrary to paths pointing to existing collection elements we cannot determine the type of the object to be created. Thus, we fall back to the common element type of all existing collection elements. We also reject indexes pointing to elements with an index greater than the current collection's size.
ResourceMetadataHandlerMethodArgumentResolver.supports(…) now properly checks for ResourceMetadata. It previously checked for RepositoryInformation which is probably a left-over from a previous state of the codebase and has been unnoticed so far as the class is only used as a direct dependency of other HandlerMethodArgumentResolvers.
The default exposure of repository methods is now controlled via RepositoryRestConfiguration.setExposeRepositoryMethodsByDefault(…). If set to true (the default), a repository that is detected for exposure (which in turn can be controlled via RepositoryDetectionStrategy) will have default resources exposed in case of the mere presence of CRUD methods. Setting this to false will require you to explicitly annotated those methods with @RestResource.
Added RepositoryRestConfiguration.disableDefaultExposure() to set the RepositoryDetectionStategy to ANNOTATED and disables default method exposure in one go. That can be exposed via a Spring Boot configuration property downstream.
Removed Lombok constructor and accessor generation to avoid having to deal with the changed default in Lombok 1.16.20 not generating @ConstructorProperties by default anymore.
Significantly refactored the way that patch path expressions are handled and evaluated. The new design is centered around SpelPath that is aware of the original path as well as the derived SpEL expression. That SpelPath then requires clients to bind it to a type so that the original path can be validated (and rejected if invalid) and provide API to read, set, copy and move values backed by the original path. Both SpelPath and TypedSpelPath instances are cached to avoid repeated creation.
PatchOperation implementations now provide more fluent factory methods, in some cases via intermediate builders. Removed a lot of obsolete code that created JsonNodes from a list of PatchOperations as we don't actually use that functionality anywhere. Removed obsolete generics where possible.
We now inspect the user class of the given bean type in BasePathAwareHandlerMapping.isHandler(…). We can't use AnnotationUtils as @RepositoryRestController is also annotated with @BasePathAwareController but must not be handled by this mapping.
Previously, the SpEL expression created from JSON Patch path expressions were executed without double checking whether these paths actually exist on the target object in the first place. This check is now in place.
We now also catch SpelEvaluationException in the attempt to set a null value for an expression as Spring 5 now reports a failed attempt (e.g. if the target type is not nullable) using that type whereas Spring 4.3 threw a NullPointerException.
Fixed the setup of our custom ExceptionHandlerExceptionResolver to conclude with a call to afterPropertiesSet() to make sure it's properly initialized.
We now expose the constructor that takes a RelProvider in RepositoryResourceMappings so that clients can tweak the default relation names. Changed the order of constructor parameters of (previously) non-public constructors for consistency.
The RelProvider to be used with the mappings can now be configured via RepositoryRestConfiguration and defaults to the EvoInflector based one.
When a PATCH call using JSON Patch tried to append an item to an empty collection, it previously failed to look up the type to unmarshal the incoming payload to. We now inspect the declared types on the wrapped object's property to determine that type for append operations.
Also, an uninitialized collection is now initialized using Spring's CollectionFactory.
We now consider the base URI when resolving CORS configuration from repository interfaces. The base URI is now stripped from the request. Previously the base URI was not stripped from the request and was used to determine an exported resource.
So far, we handed the plain instance deserialized from the request body to the repository to persist it. That caused issues in PUT for create scenarios where the URI contains the identifier to be used for the aggregate to create and identifier generation being used in the backend. In that case the identifier submitted was never considered and subsequent requests would've created new instances, effectively breaking the idempotent nature of PUT.
We now make sure the backend identifier derived from the resource is set on the aggregate instance about to be created, so that backend can either accept that situation (new entity + manually defined identifier) or reject it (in case it insists on identifier generation).
Previously, when a request was sending an Accept header of some arbitrary *+json, the request was routed through the controllers and might have ended up producing a PersistentEntityResource that was then mapped using an uncustomized Jackson ObjectMapper. That has caused a huge JSON object to be unfolded which is highly undesirable.
We now only answer JSON requests to repository resources that contain an Accept header with any of the explicit JSON media types we got registered.
Tweaked the setup of the ExceptionHandlerExceptionResolver to not expose a bean in the first place but rather use the Spring MVC provided callbacks to register custom ones. We also now make sure MVC is bootstrapped property for integration tests through the inclusion of DelegatingWebMvcConfiguration.
We previously erroneously looked for a ~ in a JSON Pointer to indicate collection append in e.g. an add operation. We now also support the actually correct - keeping the original behavior for now to not break clients that currently make use of it.
Repositories in RepositoryCorsConfigurationAccessor may be null now. findCorsConfiguration returns null when no repositories are provided.
Original pull request: #257.
We now explicitly merge associations skipping the linkable ones. We also try to reuse the existing collections and maps if they're mutable falling back to a completely new one if not.
Extracted PropertyHandler and AssociationHandler implementations.
When merging collections on PATCH we now don't use the first collection item's type for all elements but inspect the values for each existing element found. When it comes to appending elements to the collection, wen now just stick to the declared component type as type hint for reading the provided value.
When copying the transient properties of an aggregate, we now try field based access first and fall back to accessor based copying in case both a setter and getter are exposed on the type.
Previously we always expected a field to be present which doesn't necessarily has to be the case.
Related ticket: DATAREST-986.
Jacksons ObjectMapper.readerForUpdate(…) unfortunately doesn't handle nested objects properly. We already have a manual merge process in place for PATCH requests but tweaking that to also handle PUT requests gracefully caused more complexity than anticipated.
We now switched to an object based merge so that we can read in the source JSON structure into a new object and then merge the objects.
Related pull request: #247.
We now use a Map property's generic type information to make sure we convert both the key and the value into the declared types. Previously we just used Object if the source value to map was null. Object is still used as fallback for raw maps though.
We now allow sorting by properties of embedded objects. An embedded object is not linkable to a root object but embedded in the resource itself. If any part of the sort property path points to a linkable association, the whole sort property path is discarded silently and not used for sorting any further.
Original pull request: #251.
We now issue the user registration of Jackson modules before any of the default modules Spring Data registers get applied. Testing module registration seems to be rather difficult as Jackson doesn't actually expose API to do so. An issue [0] was filed for Jackson to improve on this.
[0] https://github.com/FasterXML/jackson-databind/issues/1478
Skip all merge logic if the source value is null. That frees all nested logic from handling with that case and us falling back to plain Jackson reading.
The array handling now also opts out if the source value is not a collection or array in the first place as it means we need to let Jackson override the value with the collection given to be deserialized.
Original pull request: #246.