spring-projects / spring-projects/spring-hateoas
It is hard to add dynamic HalFormsOptions
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 1.1k
- Forks
- 476
- PR merge metrics
- No merged PRs in 30d
Description
Caveat: The Spring HATEOAS project is rather new to me, so I may have missed something obvious / this may be a documentation issue.
I have been fiddling with adding dynamic HalFormsOptions to my API. The route I ended up taking was the following:
- Seeing that there is a hook for static
HalFormsOptionsin theHalFormsConfigurationI decided to lean on that and did:
new HalFormsConfiguration(halConfiguration)
.withOptions(RespondRequest.class, "responseKey",
metadata -> {
if(metadata instanceof HalFormsPropertyMetadata halFormsPropertyMetadata) { // HalFormsPropertyMetadata is my own delegating impl
return halFormsPropertyMetadata.getOptions();
} else {
return null; // could also throw here as the metadata does not live up to the expectations
}});
- As the
Affordancetype fromWebMvcLinkBuilder.afford()is locked down, I create the affordance manually, like the docs indicate and try to setup the properAffordanceModel.InputPayloadMetadata:
AffordanceModel.InputPayloadMetadata input =
PropertyUtils.getExposedProperties(ResolvableType.forClass(RespondRequest.class));
- Next problem was to customize my property inside the input metadata? (The
.customize()method does not mutate anything). I created my own InputPayloadMetadata to mutate the properties:
final class HalFormsPayloadMetadata implements AffordanceModel.InputPayloadMetadata {
private final Class<?> type;
private final SortedMap<String, AffordanceModel.PropertyMetadata> properties;
private final List<MediaType> mediaTypes;
private final List<String> i18nCodes;
public HalFormsPayloadMetadata(AffordanceModel.InputPayloadMetadata payload) {
this(payload.getType(),
new TreeMap<>(payload.stream().collect(Collectors.toMap(AffordanceModel.Named::getName, Function.identity()))),
payload.getMediaTypes(),
payload.getI18nCodes());
}
private HalFormsPayloadMetadata(Class<?> type, SortedMap<String, AffordanceModel.PropertyMetadata> properties,
List<MediaType> mediaTypes, List<String> i18nCodes) {
this.type = type;
this.properties = properties;
this.mediaTypes = List.copyOf(mediaTypes);
this.i18nCodes = List.copyOf(i18nCodes);
}
/**
* Manipulate the existing properties, the mapper should just return the properties that it is not interested in
* @param mapper
* @return a new payload metadata
*/
public HalFormsPayloadMetadata mapProperty(Function<AffordanceModel.PropertyMetadata, AffordanceModel.PropertyMetadata> mapper) {
return new HalFormsPayloadMetadata(this.type,
new TreeMap<>(this.properties.values().stream().map(mapper)
.collect(Collectors.toMap(AffordanceModel.Named::getName, Function.identity()))),
this.mediaTypes, this.i18nCodes);
}
// the other methods from the interface
..
}
- In my assembler i can now create my custom
HalFormsPropertyMetadatawith the dynamic options, create a newHalFormsPayloadMetadatawith the normal property descriptor replaced with my custom one and let the framework do its thing.
I find the dance around manipulating the state of the InputPayloadMetadata a bit unnecessary (having to implement my own version just to manipulate a property. Some variant of the InputPayloadMetadata.customize() that just returns a new InputPayloadMetadata instance would have gone a long way.
I can live with having to implement a custom property implementation, but it also seems like having a Map<String,Object> getCustomData() on AffordanceModel.PropertyMetadata could offer a lot of exiting options.
Ideally it would be possible to obtain a builder from an Affordance and just manipulate everything through that. "custom data" does not have to be tied to a media type, so it could go into the general representation. That would allow me to use the provided stuff for most of the work and take over, when something else, like dynamic options or other manipulation is needed.
I kind of hope that I missed some mechanism for setting up Input payload metadata without having to create my own, so thoughts are very welcome!
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reviewing HalFormsConfiguration, Affordance, and AffordanceModel.InputPayloadMetadata, especially the customize() behavior described in the issue. The scope needs agreement before implementation; done would be a supported, documented way to customize dynamic HalFormsOptions and input payload metadata without reimplementing the metadata interface.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, spring
- Domain
- api, backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100