#849 - HAL Forms template titles can now be internationalized.
We now support _template.$affordanceName.title expressions in the rest-messages resource bundle to internationalize template titles. Some minor cleanups in test cases and consistent naming for MessageSourceResolvable implementations. Related tickets: #979.
This commit is contained in:
@@ -23,6 +23,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.context.MessageSourceResolvable;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
@@ -285,23 +286,77 @@ class HalFormsSerializers {
|
||||
Class<?> type = it.getInputType().resolve(Object.class);
|
||||
|
||||
List<HalFormsProperty> propertiesWithPrompt = it.getInputProperties().stream() //
|
||||
.map(property -> property.withPrompt(accessor.getMessage(PromptLookup.of(type, property))))
|
||||
.map(property -> property.withPrompt(accessor.getMessage(PropertyPrompt.of(type, property))))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
HalFormsTemplate template = HalFormsTemplate.forMethod(it.getHttpMethod()) //
|
||||
.withProperties(propertiesWithPrompt);
|
||||
|
||||
String defaultedName = templates.isEmpty() ? "default" : it.getName();
|
||||
String title = accessor.getMessage(TemplateTitle.of(it, templates.isEmpty()));
|
||||
|
||||
if (StringUtils.hasText(title)) {
|
||||
template = template.withTitle(title);
|
||||
}
|
||||
|
||||
/*
|
||||
* First template in HAL-FORMS is "default".
|
||||
*/
|
||||
templates.put(templates.isEmpty() ? "default" : it.getName(), template);
|
||||
templates.put(defaultedName, template);
|
||||
});
|
||||
|
||||
return templates;
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor(staticName = "of")
|
||||
static class PromptLookup implements MessageSourceResolvable {
|
||||
static class TemplateTitle implements MessageSourceResolvable {
|
||||
|
||||
private static final String TEMPLATE_TEMPLATE = "_templates.%s.title";
|
||||
|
||||
private final HalFormsAffordanceModel affordance;
|
||||
private final boolean soleTemplate;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.MessageSourceResolvable#getCodes()
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public String[] getCodes() {
|
||||
|
||||
Stream<String> seed = Stream.concat(//
|
||||
Stream.of(affordance.getName()), //
|
||||
soleTemplate ? Stream.of("default") : Stream.empty());
|
||||
|
||||
Class<?> type = affordance.getInputType().resolve(Object.class);
|
||||
|
||||
return seed.flatMap(it -> getCodesFor(it, type)) //
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
private static Stream<String> getCodesFor(String name, Class<?> type) {
|
||||
|
||||
String global = String.format(TEMPLATE_TEMPLATE, name);
|
||||
|
||||
return Stream.of(//
|
||||
String.format("%s.%s", type.getName(), global), //
|
||||
String.format("%s.%s", type.getSimpleName(), global), //
|
||||
global);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.context.MessageSourceResolvable#getDefaultMessage()
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public String getDefaultMessage() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor(staticName = "of")
|
||||
static class PropertyPrompt implements MessageSourceResolvable {
|
||||
|
||||
private static final String PROMPT_TEMPLATE = "%s._prompt";
|
||||
|
||||
|
||||
@@ -404,7 +404,7 @@ class Jackson2HalFormsIntegrationTest extends AbstractJackson2MarshallingIntegra
|
||||
@ValueSource(strings = { "firstname._prompt", //
|
||||
"HalFormsPayload.firstname._prompt", //
|
||||
"org.springframework.hateoas.mediatype.hal.forms.Jackson2HalFormsIntegrationTest$HalFormsPayload.firstname._prompt" })
|
||||
public void usesResourceBundleToCreatePropertyPrompts(String key) {
|
||||
void usesResourceBundleToCreatePropertyPrompts(String key) {
|
||||
|
||||
StaticMessageSource source = new StaticMessageSource();
|
||||
source.addMessage(key, Locale.US, "Vorname");
|
||||
@@ -425,6 +425,35 @@ class Jackson2HalFormsIntegrationTest extends AbstractJackson2MarshallingIntegra
|
||||
}).doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
@ParameterizedTest // #849
|
||||
@ValueSource(strings = { //
|
||||
"_templates.postHalFormsPayload.title", //
|
||||
"HalFormsPayload._templates.postHalFormsPayload.title", //
|
||||
"org.springframework.hateoas.mediatype.hal.forms.Jackson2HalFormsIntegrationTest$HalFormsPayload._templates.postHalFormsPayload.title", //
|
||||
"_templates.default.title", //
|
||||
"HalFormsPayload._templates.default.title", //
|
||||
"org.springframework.hateoas.mediatype.hal.forms.Jackson2HalFormsIntegrationTest$HalFormsPayload._templates.default.title" })
|
||||
void usesResourceBundleToCreateTemplateTitle(String key) {
|
||||
|
||||
StaticMessageSource source = new StaticMessageSource();
|
||||
source.addMessage(key, Locale.US, "Template title");
|
||||
|
||||
Link link = new Link("some:link") //
|
||||
.andAffordance(HttpMethod.POST, HalFormsPayload.class, Collections.emptyList(), Object.class);
|
||||
|
||||
EntityModel<HalFormsPayload> model = new EntityModel<>(new HalFormsPayload(), link);
|
||||
ObjectMapper mapper = getCuriedObjectMapper(CurieProvider.NONE, source);
|
||||
|
||||
assertThatCode(() -> {
|
||||
|
||||
String promptString = JsonPath.compile("$._templates.default.title") //
|
||||
.read(mapper.writeValueAsString(model));
|
||||
|
||||
assertThat(promptString).isEqualTo("Template title");
|
||||
|
||||
}).doesNotThrowAnyException();
|
||||
}
|
||||
|
||||
private void verifyResolvedTitle(String resourceBundleKey) throws Exception {
|
||||
|
||||
LocaleContextHolder.setLocale(Locale.US);
|
||||
|
||||
Reference in New Issue
Block a user