Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support customizing additional HAL-FORMS properties #1018

Closed
colin-young opened this issue Jul 10, 2019 · 9 comments
Closed

Support customizing additional HAL-FORMS properties #1018

colin-young opened this issue Jul 10, 2019 · 9 comments
Assignees
Milestone

Comments

@colin-young
Copy link
Contributor

Now that we've got support for prompt thanks to #979, it would be nice to add support for the remaining "standard" HAL-Forms properties. I did notice that it adds the prompt everywhere using the name, rather than only adding it when defined. According to the spec, if the prompt is missing, clients MAY act as if it was set to the value of name. #979 kind of short-circuits that.

Some thoughts:

  • readOnly - could use absence of setter to auto-set
  • regex, required - Add JSR-303 support to HAL-FORMS #619 suggests using JSR-303, which seems like a clear and elegant solution
  • templated, value - honestly, I'm not certain at the moment what these would be used for, so I don't feel qualified to make any suggestions

It seems easy enough to simply add the rest from a resource bundle, and I briefly considered creating a PR until I remembered the corporate proxy I'm stuck behind that blocks the git protocol, which, in this case, is probably a good thing since I think the solutions above are much more elegant and better for long-term maintenance of the consuming codebase. It might still be beneficial to allow overriding through resources, but I don't know how that fits into the Spring philosophy (I've only just picked up Java again after a 15 year absence and Spring has changed just a little bit since then).

@gregturn
Copy link
Contributor

gregturn commented Jul 10, 2019

The thing I’ve tinkered with was whether to create media type specific annotations that allow you to apply such settings or where it makes sense to have vendor neutral options.

I’ve also looked at whether you can have one annotation that covers them all vs separate annotation for each one. Given an annotation attribute can NOT have a null value pushes me toward more/smaller annotations.

Also need @odrotbohm ‘s opinion as well.

@colin-young
Copy link
Contributor Author

Some thoughts:

  • JSR-303 communicates intents about a property and is definitely vendor-neutral (but might require translation, i.e. one regex language to another, maybe?), so taking advantage of those annotations automatically as appropriate for the media type seems natural
  • ditto for read-only. If that can be inferred it can be automatically provided for media types that support it
  • as for any others, you should probably have some idea of what media types your code is intended to support, so it isn't unreasonable to expect the developer to specify the appropriate ones they need, either through annotations or through resources. Maybe even a way to configure mapping, e.g. through HalFormsConfiguration to say for HAL-Forms use the @Foobar annotation to specify the prompt property, or use *.foobar from resources for the prompt property.

Personally, I'd rather see something that communicates intent rather than a specific output on my resources, and handle mapping to a specific media type through convention (well-known annotations) and configuration.

@odrotbohm
Copy link
Member

I think resource bundles are a great and established tool when it comes to what they were intended for: i18n. That's why the prompts are pulled from that. We unfortunately can't switch to return null as default as that would cause the i18n resolution to fail with a NoSuchMessageException. However, I think we can tweak the Jackson serialization to only render the attribute if a non-empty String is the value.

Generally speaking, JSR-303 seems fine if we assume that dedicated DTOs are used to capture payloads and describe responses. However, the tricky bit is that people usually expect Jackson customizations to work on those, and there has to be a way to keep those in sync. Also, @JsonProperty(access = READ_ONLY) is often used to express that a property is considered to be read only.

We can always end up adding detailed knobs to the media type specific configuration but I'd rather make this a process based on actual user requirements VS. coming up with ideas from thin air.

Here are the immediate steps I'm gonna take:

  • Remove the prompt field if no value is found in the resource bundle.
  • Remove the required field unless it's true (as the spec defines it to be considered false if not present anyway).

I'll also have a look at how to integrate the JSR-303 inspections.

@odrotbohm odrotbohm self-assigned this Jul 11, 2019
@colin-young
Copy link
Contributor Author

colin-young commented Jul 11, 2019

Regarding JSR-303 and dedicated DTOs, how would one return a resource without a "DTO" of some sort? Even if that "DTO" was actually a full-blown heavy-weight class with full business logic, there's still something that is going to get serialized and sent back as the data for the resource. It makes sense to take advantage of any declarative information that is relevant to that media type. Similarly with the read-only. You could infer read-only by the absence of a setter, but if there is a declarative read-only annotation that would take precedence. I'm not familiar enough with Jackson customizations to speak to that -- can you specify a regex pattern with them? (Full disclosure: I've been doing .Net for the last ~20 years with a brief diversion into Java around the time Rod Johnson published Expert One-on-One J2EE Design and Development, and currently I've been working with it for about 5 weeks.) Where I could see an issue is if somebody provided conflicting constraints through both mechanisms, but that's going to cause problems outside HATEOAS, so I think that's outside the scope of what Spring-HATEOAS should handle, other than to have some order of precedence (e.g. if a Jackson constraint is found, stop looking or something like that).

In my mind what I'm describing is completely neutral to both the selected media type and the choice of declarative constraints, assuming, of course, that Spring-HATEOAS knows about and supports those constraints.

As for an actual user requirement: I need to be able to describe a regex that applies to the allowed values of a data field to the client. At the moment, I happen to be using the HAL-Forms media type. That's it. I don't much care how, as long as it's declarative, easy to understand, and easy to maintain.

odrotbohm added a commit that referenced this issue Jul 18, 2019
…tation model classes.

We now consider additional Jackson and JSR-303 annotations on representation models to enrich the HAL Forms template properties with additional information:

- @NotNull on a property flips its `required` flag to `true`.
- The "regexp" attribute of @pattern on a property is forwarded into the "regex" field.
- @JsonProperty(Access.READ_ONLY) on a property is translated into the "readOnly" flag.

The default for the "required" flag have been changed to false even for PUT and POST as whether they're required or not is completely determined by the model, not the HTTP method.

All of this is backed by significant refactoring in the way that Affordance instances are build internally. The new API is centered around the Affordances type in the mediatype package. The methods on Link to create the Affordance` from its details have been removed and replaced by builder style APIs on Affordance. AffordanceModelFactory has been moved to the mediatype as well.

PropertyUtils has been significantly revamped to expose a PayloadMetadata/PropertyMetadata model to abstract the individual traits of a property. This allows to optionally plug in the support for JSR-303 annotations. AffordanceModelFactory has been refactored to rather work with those instead of ResolvableType.
odrotbohm added a commit that referenced this issue Jul 18, 2019
…tation model classes.

We now consider additional Jackson and JSR-303 annotations on representation models to enrich the HAL Forms template properties with additional information:

- @NotNull on a property flips its `required` flag to `true`.
- The "regexp" attribute of @pattern on a property is forwarded into the "regex" field.
- @JsonProperty(Access.READ_ONLY) on a property is translated into the "readOnly" flag.

The default for the "required" flag have been changed to false even for PUT and POST as whether they're required or not is completely determined by the model, not the HTTP method.

All of this is backed by significant refactoring in the way that Affordance instances are build internally. The new API is centered around the Affordances type in the mediatype package. The methods on Link to create the Affordance` from its details have been removed and replaced by builder style APIs on Affordance. AffordanceModelFactory has been moved to the mediatype as well.

PropertyUtils has been significantly revamped to expose a PayloadMetadata/PropertyMetadata model to abstract the individual traits of a property. This allows to optionally plug in the support for JSR-303 annotations. AffordanceModelFactory has been refactored to rather work with those instead of ResolvableType.
@odrotbohm
Copy link
Member

The reason I made this distinction in the first place is that in domain types (as opposed to DTOs), JSR-303 is often used to "implement" domain constraints when a value type would've been the better alternative:

@Email
String emailAddress;

While this is perfectly fine in a DTO, it will probably have to be able to capture invalid values as well to produce proper validation messages, in the context of a domain object as nobody using the emailAddress property will be able to know whether it actually contains an email address or an arbitrary string. I.e. in a domain objects you'd definitely prepare:

EmailAddress emailAddress

I went ahead and added the support for JSR-303s @NotNull to mark required properties as well as @Pattern to define regular expressions. The later can also be used on types which will in turn render properties of those with the registered pattern. Also, HalFormsConfiguration now has a method to register patterns by type globally. See more on that in the updated reference documentation.

Please give the current snapshots a try. We still have one week until RC1 and I'm happy to tweak things further.

@odrotbohm odrotbohm added this to the 1.0 RC1 milestone Jul 18, 2019
@odrotbohm odrotbohm changed the title Support all HAL-Forms properties Support customizing additional HAL-FORMS properties Jul 18, 2019
@colin-young
Copy link
Contributor Author

Looks fantastic. Thanks! Could anything around this change have caused rest-messages.properties to no longer work?

@odrotbohm
Copy link
Member

odrotbohm commented Jul 31, 2019

Yes, there was a glitch introduced in the course of the optimizations made for #1019. That should be fixed with the latest commits. The snapshots should be available in a couple of minutes. Would love to hear your feedback as we're shooting for the RC release by the end of the week.

@colin-young
Copy link
Contributor Author

I'll take a look as soon as I get into the office tomorrow morning (US, Eastern time). Thanks!

@colin-young
Copy link
Contributor Author

Looks good. Working as expected again.

One minor error in the docs: in Example 35 it shows Employee.firstName=Firstname but I think that should be Employee.firstName._prompt=Firstname. I created #1028 to fix it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants