From cc732c08c9ea047abe64a99380f735345f5a7024 Mon Sep 17 00:00:00 2001 From: niels cappelle Date: Mon, 23 Dec 2024 14:51:33 +0100 Subject: [PATCH] fix titles and descriptions of nested properties This makes the titles and descriptions of the HAL-FORMS profile endpoint consistent with the titles and descriptions of the json-schema profile endpoint --- ...AttributeRepresentationModelAssembler.java | 20 ++++++++++++-- .../DescriptionMessageSourceResolvable.java | 22 +++++++-------- .../EntityRepresentationModelAssembler.java | 5 ++-- .../RelationRepresentationModelAssembler.java | 3 ++- .../TitleMessageSourceResolvable.java | 22 +++++---------- ...AbstractHalFormsProfileControllerTest.java | 27 ++++++++++++------- .../rest-default-messages.properties | 6 +++++ 7 files changed, 64 insertions(+), 41 deletions(-) diff --git a/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/AttributeRepresentationModelAssembler.java b/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/AttributeRepresentationModelAssembler.java index ba91ad98..0dba12f5 100644 --- a/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/AttributeRepresentationModelAssembler.java +++ b/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/AttributeRepresentationModelAssembler.java @@ -13,6 +13,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.context.MessageSourceResolvable; import org.springframework.data.rest.webmvc.RootResourceInformation; +import org.springframework.data.util.TypeInformation; import org.springframework.hateoas.mediatype.MessageResolver; @RequiredArgsConstructor @@ -92,12 +93,27 @@ private String getType(Property property) { } private String readDescription(RootResourceInformation information, List properties) { - var description = messageResolver.resolve(DescriptionMessageSourceResolvable.forNestedProperty(information, properties)); + String description = null; + if (properties.size() == 1) { + description = messageResolver.resolve(DescriptionMessageSourceResolvable.forProperty(information, + properties.get(0))); + } else if (properties.size() > 1) { + var type = properties.get(properties.size() - 2).getTypeInformation(); + var property = properties.get(properties.size() - 1); + description = messageResolver.resolve(DescriptionMessageSourceResolvable.forNestedProperty(type, property)); + } return description == null ? "" : description; } private String readTitle(RootResourceInformation information, List properties) { - return messageResolver.resolve(TitleMessageSourceResolvable.forNestedProperty(information, properties)); + TypeInformation type; + if (properties.size() > 1) { + type = properties.get(properties.size() - 2).getTypeInformation(); + } else { + type = information.getPersistentEntity().getTypeInformation(); + } + return messageResolver.resolve(TitleMessageSourceResolvable.forProperty(type, properties.get( + properties.size() - 1))); } private String readPrompt(RootResourceInformation information, String path) { diff --git a/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/DescriptionMessageSourceResolvable.java b/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/DescriptionMessageSourceResolvable.java index e8fdae7d..af56ad4a 100644 --- a/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/DescriptionMessageSourceResolvable.java +++ b/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/DescriptionMessageSourceResolvable.java @@ -1,31 +1,31 @@ package com.contentgrid.spring.data.rest.webmvc.blueprint; import com.contentgrid.spring.data.rest.mapping.Property; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; import lombok.Value; import org.springframework.context.MessageSourceResolvable; import org.springframework.data.rest.webmvc.RootResourceInformation; +import org.springframework.data.util.TypeInformation; +import org.springframework.util.StringUtils; @Value public class DescriptionMessageSourceResolvable implements MessageSourceResolvable { String[] codes; public static DescriptionMessageSourceResolvable forEntity(RootResourceInformation information) { - return forNestedProperty(information, List.of()); + // e.g. "rest.description.d\:person" + return new DescriptionMessageSourceResolvable(information.getResourceMetadata().getItemResourceDescription().getMessage()); } public static DescriptionMessageSourceResolvable forProperty(RootResourceInformation information, Property property) { - return forNestedProperty(information, List.of(property)); + // e.g. "rest.description.d\:person.firstName" + return new DescriptionMessageSourceResolvable(information.getResourceMetadata().getItemResourceDescription().getMessage() + "." + property.getName()); } - public static DescriptionMessageSourceResolvable forNestedProperty(RootResourceInformation information, Collection properties) { - var message = properties.stream() - .map(Property::getName) - .map(name -> "." + name) - .collect(Collectors.joining()); - return new DescriptionMessageSourceResolvable(information.getResourceMetadata().getItemResourceDescription().getMessage() + message); + public static DescriptionMessageSourceResolvable forNestedProperty(TypeInformation information, Property property) { + // e.g. "rest.description.auditMetadata.createdDate" + var className = StringUtils.uncapitalize(information.getType().getSimpleName()); + var code = "rest.description.%s.%s".formatted(className, property.getName()); + return new DescriptionMessageSourceResolvable(code); } private DescriptionMessageSourceResolvable(String... codes) { diff --git a/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/EntityRepresentationModelAssembler.java b/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/EntityRepresentationModelAssembler.java index 4f5f8b3f..928a60ab 100644 --- a/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/EntityRepresentationModelAssembler.java +++ b/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/EntityRepresentationModelAssembler.java @@ -12,6 +12,7 @@ import org.springframework.data.rest.core.config.RepositoryRestConfiguration; import org.springframework.data.rest.core.mapping.ResourceMappings; import org.springframework.data.rest.webmvc.RootResourceInformation; +import org.springframework.data.util.TypeInformation; import org.springframework.hateoas.LinkRelation; import org.springframework.hateoas.mediatype.MessageResolver; import org.springframework.hateoas.mediatype.hal.HalLinkRelation; @@ -63,7 +64,7 @@ public EntityRepresentationModel toModel(RootResourceInformation information) { return EntityRepresentationModel.builder() .name(name) - .title(readTitle(information)) + .title(readTitle(entityContainer.getTypeInformation())) .description(readDescription(information)) .attributes(attributes) .relations(relations) @@ -75,7 +76,7 @@ private String readDescription(RootResourceInformation information) { return description == null ? "" : description; } - private String readTitle(RootResourceInformation information) { + private String readTitle(TypeInformation information) { return messageResolver.resolve(TitleMessageSourceResolvable.forEntity(information)); } diff --git a/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/RelationRepresentationModelAssembler.java b/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/RelationRepresentationModelAssembler.java index fa3bb248..ef3cdd2c 100644 --- a/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/RelationRepresentationModelAssembler.java +++ b/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/RelationRepresentationModelAssembler.java @@ -62,7 +62,8 @@ private String readDescription(RootResourceInformation information, Property pro } private String readTitle(RootResourceInformation information, Property property) { - return messageResolver.resolve(TitleMessageSourceResolvable.forProperty(information, property)); + return messageResolver.resolve(TitleMessageSourceResolvable.forProperty( + information.getPersistentEntity().getTypeInformation(), property)); } } diff --git a/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/TitleMessageSourceResolvable.java b/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/TitleMessageSourceResolvable.java index 11c522b5..750ca9fc 100644 --- a/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/TitleMessageSourceResolvable.java +++ b/contentgrid-spring-data-rest/src/main/java/com/contentgrid/spring/data/rest/webmvc/blueprint/TitleMessageSourceResolvable.java @@ -1,31 +1,21 @@ package com.contentgrid.spring.data.rest.webmvc.blueprint; import com.contentgrid.spring.data.rest.mapping.Property; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; import lombok.Value; import org.springframework.context.MessageSourceResolvable; -import org.springframework.data.rest.webmvc.RootResourceInformation; +import org.springframework.data.util.TypeInformation; @Value public class TitleMessageSourceResolvable implements MessageSourceResolvable { String[] codes; - public static TitleMessageSourceResolvable forEntity(RootResourceInformation information) { - return forNestedProperty(information, List.of()); + public static TitleMessageSourceResolvable forEntity(TypeInformation information) { + return new TitleMessageSourceResolvable(information.getType().getName() + "._title"); } - public static TitleMessageSourceResolvable forProperty(RootResourceInformation information, Property property) { - return forNestedProperty(information, List.of(property)); - } - - public static TitleMessageSourceResolvable forNestedProperty(RootResourceInformation information, Collection properties) { - var message = properties.stream() - .map(Property::getName) - .map(name -> "." + name) - .collect(Collectors.joining()); - return new TitleMessageSourceResolvable(information.getDomainType().getName() + message + "._title"); + public static TitleMessageSourceResolvable forProperty(TypeInformation information, Property property) { + var code = information.getType().getName() + "." + property.getName() + "._title"; + return new TitleMessageSourceResolvable(code); } private TitleMessageSourceResolvable(String... codes) { diff --git a/contentgrid-spring-data-rest/src/test/java/com/contentgrid/spring/data/rest/webmvc/AbstractHalFormsProfileControllerTest.java b/contentgrid-spring-data-rest/src/test/java/com/contentgrid/spring/data/rest/webmvc/AbstractHalFormsProfileControllerTest.java index e9f041ef..93763a50 100644 --- a/contentgrid-spring-data-rest/src/test/java/com/contentgrid/spring/data/rest/webmvc/AbstractHalFormsProfileControllerTest.java +++ b/contentgrid-spring-data-rest/src/test/java/com/contentgrid/spring/data/rest/webmvc/AbstractHalFormsProfileControllerTest.java @@ -354,8 +354,9 @@ void profileController_entityInformation_withEmbeddedContent() throws Exception "blueprint:attribute": [ { name: "length", + title: "Content Length", type: "long", - description: "", + description: "Length of the content", readOnly: true, required: false, _embedded: { @@ -369,8 +370,9 @@ void profileController_entityInformation_withEmbeddedContent() throws Exception }, { name: "mimetype", + title: "Content Mimetype", type: "string", - description: "", + description: "Mimetype of the content", readOnly: false, required: false, _embedded: { @@ -385,8 +387,9 @@ void profileController_entityInformation_withEmbeddedContent() throws Exception }, { name: "filename", + title: "Content Filename", type: "string", - description: "", + description: "Filename of the content", readOnly: false, required: false, _embedded: { @@ -737,8 +740,9 @@ void profileController_entityInformation_entityNameWithDashes() throws Exception "blueprint:attribute": [ { name: "length", + title: "Content Length", type: "long", - description: "", + description: "Length of the content", readOnly: true, required: false, _embedded: { @@ -747,8 +751,9 @@ void profileController_entityInformation_entityNameWithDashes() throws Exception }, { name: "mimetype", + title: "Content Mimetype", type: "string", - description: "", + description: "Mimetype of the content", readOnly: false, required: false, _embedded: { @@ -757,8 +762,9 @@ void profileController_entityInformation_entityNameWithDashes() throws Exception }, { name: "filename", + title: "Content Filename", type: "string", - description: "", + description: "Filename of the content", readOnly: false, required: false, _embedded: { @@ -778,8 +784,9 @@ void profileController_entityInformation_entityNameWithDashes() throws Exception "blueprint:attribute": [ { name: "length", + title: "Content Length", type: "long", - description: "", + description: "Length of the content", readOnly: true, required: false, _embedded: { @@ -788,8 +795,9 @@ void profileController_entityInformation_entityNameWithDashes() throws Exception }, { name: "mimetype", + title: "Content Mimetype", type: "string", - description: "", + description: "Mimetype of the content", readOnly: false, required: false, _embedded: { @@ -798,8 +806,9 @@ void profileController_entityInformation_entityNameWithDashes() throws Exception }, { name: "filename", + title: "Content Filename", type: "string", - description: "", + description: "Filename of the content", readOnly: false, required: false, _embedded: { diff --git a/contentgrid-spring-data-rest/src/testFixtures/resources/rest-default-messages.properties b/contentgrid-spring-data-rest/src/testFixtures/resources/rest-default-messages.properties index 0d7a61d6..695937a5 100644 --- a/contentgrid-spring-data-rest/src/testFixtures/resources/rest-default-messages.properties +++ b/contentgrid-spring-data-rest/src/testFixtures/resources/rest-default-messages.properties @@ -11,6 +11,9 @@ com.contentgrid.spring.test.fixture.invoicing.model.Invoice.attachment_filename. com.contentgrid.spring.test.fixture.invoicing.model.Invoice.attachment_mimetype._prompt=Attached File Mimetype com.contentgrid.spring.test.fixture.invoicing.model.Customer.content.filename._prompt=Customer Document Filename com.contentgrid.spring.test.fixture.invoicing.model.Customer.content.mimetype._prompt=Customer Document Mimetype +com.contentgrid.spring.test.fixture.invoicing.model.support.Content.filename._title=Content Filename +com.contentgrid.spring.test.fixture.invoicing.model.support.Content.mimetype._title=Content Mimetype +com.contentgrid.spring.test.fixture.invoicing.model.support.Content.length._title=Content Length rest.description.d\:customer=Company or person that acts like a client rest.description.d\:customer.name=Full name of the customer rest.description.d\:customer.vat=VAT number of the customer @@ -19,3 +22,6 @@ rest.description.d\:invoice=A bill containing a counterparty and several orders rest.description.d\:invoice.number=Identifier of the invoice rest.description.d\:invoice.counterparty=Client that has to pay the invoice rest.description.d\:invoice.orders=Orders of the invoice +rest.description.content.filename=Filename of the content +rest.description.content.mimetype=Mimetype of the content +rest.description.content.length=Length of the content