-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #99 from xenit-eu/generic-link-relations
Add support for generic link relations based on datamodel
- Loading branch information
Showing
21 changed files
with
749 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
...ata-rest/src/main/java/com/contentgrid/spring/data/rest/links/AggregateLinkCollector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package com.contentgrid.spring.data.rest.links; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.data.rest.webmvc.mapping.LinkCollector; | ||
import org.springframework.hateoas.Links; | ||
|
||
@RequiredArgsConstructor | ||
class AggregateLinkCollector implements LinkCollector { | ||
private final LinkCollector delegate; | ||
private final Iterable<ContentGridLinkCollector> collectors; | ||
|
||
@Override | ||
public Links getLinksFor(Object object) { | ||
return getLinksFor(object, Links.NONE); | ||
} | ||
|
||
@Override | ||
public Links getLinksFor(Object object, Links existing) { | ||
existing = delegate.getLinksFor(object, existing); | ||
for (var collector : collectors) { | ||
existing = collector.getLinksFor(object, existing); | ||
} | ||
return existing; | ||
} | ||
|
||
@Override | ||
public Links getLinksForNested(Object object, Links existing) { | ||
existing = delegate.getLinksFor(object, existing); | ||
for (var collector : collectors) { | ||
existing = collector.getLinksForNested(object, existing); | ||
} | ||
return existing; | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
...a-rest/src/main/java/com/contentgrid/spring/data/rest/links/ContentGridLinkCollector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.contentgrid.spring.data.rest.links; | ||
|
||
import org.springframework.hateoas.Links; | ||
|
||
public interface ContentGridLinkCollector { | ||
|
||
Links getLinksFor(Object object, Links existing); | ||
|
||
Links getLinksForNested(Object object, Links existing); | ||
} |
17 changes: 17 additions & 0 deletions
17
...a-rest/src/main/java/com/contentgrid/spring/data/rest/links/ContentGridLinkRelations.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.contentgrid.spring.data.rest.links; | ||
|
||
import lombok.experimental.UtilityClass; | ||
import org.springframework.hateoas.LinkRelation; | ||
import org.springframework.hateoas.UriTemplate; | ||
import org.springframework.hateoas.mediatype.hal.HalLinkRelation; | ||
|
||
@UtilityClass | ||
public class ContentGridLinkRelations { | ||
final static String CURIE = "cg"; | ||
final static UriTemplate TEMPLATE = UriTemplate.of("https://contentgrid.com/rels/contentgrid/{rel}"); | ||
|
||
public final static LinkRelation ENTITY = HalLinkRelation.curied(CURIE, "entity"); | ||
public final static LinkRelation RELATION = HalLinkRelation.curied(CURIE, "relation"); | ||
public final static LinkRelation CONTENT = HalLinkRelation.curied(CURIE, "content"); | ||
|
||
} |
21 changes: 21 additions & 0 deletions
21
...om/contentgrid/spring/data/rest/links/ContentGridSpringContentRestLinksConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.contentgrid.spring.data.rest.links; | ||
|
||
import internal.org.springframework.content.rest.mappingcontext.ContentPropertyToLinkrelMappingContext; | ||
import internal.org.springframework.content.rest.mappingcontext.ContentPropertyToRequestMappingContext; | ||
import org.springframework.content.commons.mappingcontext.MappingContext; | ||
import org.springframework.content.commons.storeservice.Stores; | ||
import org.springframework.content.rest.config.RestConfiguration; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.context.annotation.Import; | ||
import org.springframework.data.mapping.context.PersistentEntities; | ||
|
||
@Configuration(proxyBeanMethods = false) | ||
@Import(ContentGridSpringDataLinksConfiguration.class) | ||
public class ContentGridSpringContentRestLinksConfiguration { | ||
@Bean | ||
ContentGridLinkCollector contentGridSpringContentLinkCollector( | ||
PersistentEntities entities, Stores stores, MappingContext mappingContext, RestConfiguration restConfiguration, ContentPropertyToRequestMappingContext requestMappingContext, ContentPropertyToLinkrelMappingContext linkrelMappingContext) { | ||
return new SpringContentLinkCollector(entities, stores, mappingContext, restConfiguration, requestMappingContext, linkrelMappingContext); | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
.../java/com/contentgrid/spring/data/rest/links/ContentGridSpringDataLinksConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package com.contentgrid.spring.data.rest.links; | ||
|
||
import com.contentgrid.spring.data.rest.hal.CurieProviderCustomizer; | ||
import com.contentgrid.spring.data.rest.webmvc.ProfileLinksResource; | ||
import org.springframework.beans.factory.ObjectProvider; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.data.mapping.context.PersistentEntities; | ||
import org.springframework.data.repository.support.Repositories; | ||
import org.springframework.data.rest.core.config.RepositoryRestConfiguration; | ||
import org.springframework.data.rest.core.mapping.ResourceMappings; | ||
import org.springframework.data.rest.core.support.SelfLinkProvider; | ||
import org.springframework.data.rest.webmvc.RepositoryLinksResource; | ||
import org.springframework.data.rest.webmvc.RestControllerConfiguration; | ||
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurer; | ||
import org.springframework.data.rest.webmvc.mapping.Associations; | ||
import org.springframework.data.rest.webmvc.mapping.LinkCollector; | ||
import org.springframework.hateoas.server.EntityLinks; | ||
import org.springframework.hateoas.server.RepresentationModelProcessor; | ||
|
||
@Configuration(proxyBeanMethods = false) | ||
public class ContentGridSpringDataLinksConfiguration { | ||
@Bean | ||
RepositoryRestConfigurer contentGridLinkCollectorConfigurer( | ||
ObjectProvider<ContentGridLinkCollector> collectors | ||
) { | ||
return new RepositoryRestConfigurer() { | ||
@Override | ||
public LinkCollector customizeLinkCollector(LinkCollector collector) { | ||
return new AggregateLinkCollector(collector, collectors); | ||
} | ||
}; | ||
} | ||
|
||
@Bean | ||
ContentGridLinkCollector contentGridRelationLinkCollector(PersistentEntities entities, Associations associations, SelfLinkProvider selfLinkProvider) { | ||
return new SpringDataAssociationLinkCollector(entities, associations, selfLinkProvider); | ||
} | ||
|
||
@Bean | ||
CurieProviderCustomizer contentGridCurieProviderCustomizer() { | ||
return CurieProviderCustomizer.register(ContentGridLinkRelations.CURIE, ContentGridLinkRelations.TEMPLATE); | ||
} | ||
|
||
@Bean | ||
RepresentationModelProcessor<RepositoryLinksResource> contentGridRepositoryLinksResourceProcessor(Repositories repositories, ResourceMappings resourceMappings, EntityLinks entityLinks) { | ||
return new SpringDataRepositoryLinksResourceProcessor(repositories, resourceMappings, entityLinks); | ||
} | ||
|
||
@Bean | ||
RepresentationModelProcessor<ProfileLinksResource> contentGridProfileLinksResourceProcessor(Repositories repositories, ResourceMappings resourceMappings, RepositoryRestConfiguration configuration) { | ||
return new SpringDataProfileLinksResourceProcessor(repositories, resourceMappings, configuration); | ||
} | ||
} |
97 changes: 97 additions & 0 deletions
97
...rest/src/main/java/com/contentgrid/spring/data/rest/links/SpringContentLinkCollector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package com.contentgrid.spring.data.rest.links; | ||
|
||
import internal.org.springframework.content.rest.links.ContentLinksResourceProcessor.StoreLinkBuilder; | ||
import internal.org.springframework.content.rest.mappingcontext.ContentPropertyToLinkrelMappingContext; | ||
import internal.org.springframework.content.rest.mappingcontext.ContentPropertyToRequestMappingContext; | ||
import java.util.ArrayList; | ||
import java.util.Map; | ||
import java.util.Map.Entry; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.content.commons.mappingcontext.ContentProperty; | ||
import org.springframework.content.commons.mappingcontext.MappingContext; | ||
import org.springframework.content.commons.repository.AssociativeStore; | ||
import org.springframework.content.commons.storeservice.Stores; | ||
import org.springframework.content.rest.config.RestConfiguration; | ||
import org.springframework.data.mapping.context.PersistentEntities; | ||
import org.springframework.data.rest.webmvc.BaseUri; | ||
import org.springframework.hateoas.Link; | ||
import org.springframework.hateoas.LinkRelation; | ||
import org.springframework.hateoas.Links; | ||
import org.springframework.hateoas.mediatype.hal.HalLinkRelation; | ||
import org.springframework.util.StringUtils; | ||
|
||
/** | ||
* Collects links to spring-content content objects into the {@link ContentGridLinkRelations#CONTENT} link-relation | ||
*/ | ||
@RequiredArgsConstructor | ||
class SpringContentLinkCollector implements ContentGridLinkCollector { | ||
private final PersistentEntities entities; | ||
private final Stores stores; | ||
private final MappingContext mappingContext; | ||
private final RestConfiguration restConfiguration; | ||
private final ContentPropertyToRequestMappingContext requestMappingContext; | ||
private final ContentPropertyToLinkrelMappingContext linkrelMappingContext; | ||
|
||
@Override | ||
public Links getLinksFor(Object object, Links existing) { | ||
// This implementation is inspired on the ContentLinksResourceProcessor from spring-content, but adapted to the context of a LinkCollector | ||
|
||
var persistentEntity = entities.getRequiredPersistentEntity(object.getClass()); | ||
|
||
var entityId = persistentEntity.getIdentifierAccessor(object).getIdentifier(); | ||
|
||
if(entityId == null) { | ||
// No entity ID, so no content links (because they reference the entity ID) | ||
return existing; | ||
} | ||
|
||
var storeInfo = stores.getStore(AssociativeStore.class, Stores.withDomainClass(persistentEntity.getType())); | ||
if(storeInfo == null) { | ||
// No store, we don't have to add any links | ||
return existing; | ||
} | ||
|
||
Map<String, ContentProperty> contentProperties = mappingContext.getContentPropertyMap(persistentEntity.getType()); | ||
|
||
var links = new ArrayList<Link>(contentProperties.size()); | ||
|
||
for (Entry<String, ContentProperty> contentProperty : contentProperties.entrySet()) { | ||
var linkBuilder = StoreLinkBuilder.linkTo(new BaseUri(restConfiguration.getBaseUri()), storeInfo); | ||
linkBuilder = linkBuilder.slash(entityId); | ||
|
||
String requestMapping = requestMappingContext.getMappings(storeInfo.getDomainObjectClass()).get(contentProperty.getKey()); | ||
|
||
if(StringUtils.hasText(requestMapping)) { | ||
linkBuilder = linkBuilder.slash(requestMapping); | ||
} else { | ||
linkBuilder = linkBuilder.slash(contentProperty.getKey()); | ||
} | ||
|
||
String linkRel = linkrelMappingContext.getMappings(storeInfo.getDomainObjectClass()).get(contentProperty.getKey()); | ||
if(!StringUtils.hasLength(linkRel)) { | ||
linkRel = contentProperty.getKey(); | ||
} | ||
// Cut off a potential CURIE prefix from the link relation | ||
var linkName = HalLinkRelation.of(LinkRelation.of(linkRel)).getLocalPart(); | ||
|
||
|
||
var link = linkBuilder | ||
.withRel(ContentGridLinkRelations.CONTENT) | ||
.withName(linkName); | ||
|
||
// var mimeType = contentProperty.getValue().getMimeType(object); | ||
// if(mimeType != null) { | ||
// link = link.withType(String.valueOf(mimeType)); | ||
// } | ||
links.add(link); | ||
} | ||
|
||
|
||
return existing.and(links); | ||
} | ||
|
||
@Override | ||
public Links getLinksForNested(Object object, Links existing) { | ||
return existing; | ||
} | ||
} |
Oops, something went wrong.