Add previously processed links

This commit is contained in:
Ben Bosman
2020-02-19 17:31:10 +01:00
parent c6e3758a40
commit 48c6bcb88b
8 changed files with 50 additions and 17 deletions

View File

@@ -32,6 +32,7 @@ import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resource;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
@@ -155,9 +156,12 @@ public class ConverterService {
* @throws ClassCastException if the resource type is not compatible with the inferred return type.
*/
public <T extends HALResource> T toResource(RestModel restObject) {
return toResource(restObject, new Link[] {});
}
public <T extends HALResource> T toResource(RestModel restObject, Link... oldLinks) {
T halResource = getResource(restObject);
if (restObject instanceof RestAddressableModel) {
utils.embedOrLinkClassLevelRels(halResource);
utils.embedOrLinkClassLevelRels(halResource, oldLinks);
halLinkService.addLinks(halResource);
Projection projection = ((RestAddressableModel) restObject).getProjection();
return projection.transformResource(halResource);

View File

@@ -11,6 +11,7 @@ import org.dspace.app.rest.model.LinkRest;
import org.dspace.app.rest.model.RestAddressableModel;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.hateoas.HALResource;
import org.springframework.hateoas.Link;
/**
* Abstract base class for projections.
@@ -35,7 +36,7 @@ public abstract class AbstractProjection implements Projection {
}
@Override
public boolean allowEmbedding(HALResource<? extends RestAddressableModel> halResource, LinkRest linkRest) {
public boolean allowEmbedding(HALResource<? extends RestAddressableModel> halResource, LinkRest linkRest, Link... oldLinks) {
return false;
}

View File

@@ -13,6 +13,7 @@ import org.dspace.app.rest.model.LinkRest;
import org.dspace.app.rest.model.RestAddressableModel;
import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.hateoas.HALResource;
import org.springframework.hateoas.Link;
/**
* A projection that combines the behavior of multiple projections.
@@ -61,9 +62,9 @@ public class CompositeProjection implements Projection {
}
@Override
public boolean allowEmbedding(HALResource<? extends RestAddressableModel> halResource, LinkRest linkRest) {
public boolean allowEmbedding(HALResource<? extends RestAddressableModel> halResource, LinkRest linkRest, Link... oldLinks) {
for (Projection projection : projections) {
if (projection.allowEmbedding(halResource, linkRest)) {
if (projection.allowEmbedding(halResource, linkRest, oldLinks)) {
return true;
}
}

View File

@@ -12,6 +12,7 @@ import java.util.Set;
import org.dspace.app.rest.model.LinkRest;
import org.dspace.app.rest.model.RestAddressableModel;
import org.dspace.app.rest.model.hateoas.HALResource;
import org.springframework.hateoas.Link;
/**
* Projection that allows a given set of rels to be embedded.
@@ -32,9 +33,22 @@ public class EmbedRelsProjection extends AbstractProjection {
}
@Override
public boolean allowEmbedding(HALResource<? extends RestAddressableModel> halResource, LinkRest linkRest) {
if (halResource.getContent().getEmbedLevel() == 0)
return embedRels.contains(linkRest.name());
public boolean allowEmbedding(HALResource<? extends RestAddressableModel> halResource, LinkRest linkRest, Link... oldLinks) {
if (halResource.getContent().getEmbedLevel() == 0 && embedRels.contains(linkRest.name()))
return true;
StringBuilder fullName = new StringBuilder();
for (Link oldLink : oldLinks) {
fullName.append(oldLink.getRel()).append("/");
}
fullName.append(linkRest.name());
if (embedRels.contains(fullName.toString())) {
return true;
}
fullName.append("/");
for (String embedRel : embedRels) {
if (embedRel.startsWith(fullName.toString()))
return true;
}
return false;
}
}

View File

@@ -9,6 +9,7 @@ package org.dspace.app.rest.projection;
import org.dspace.app.rest.model.LinkRest;
import org.dspace.app.rest.model.hateoas.HALResource;
import org.springframework.hateoas.Link;
import org.springframework.stereotype.Component;
/**
@@ -24,7 +25,7 @@ public class FullProjection extends AbstractProjection {
}
@Override
public boolean allowEmbedding(HALResource halResource, LinkRest linkRest) {
public boolean allowEmbedding(HALResource halResource, LinkRest linkRest, Link... oldLinks) {
return true;
}

View File

@@ -15,6 +15,7 @@ import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.hateoas.HALResource;
import org.dspace.app.rest.repository.DSpaceRestRepository;
import org.dspace.app.rest.utils.Utils;
import org.springframework.hateoas.Link;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController;
@@ -118,7 +119,7 @@ public interface Projection {
* @param linkRest the LinkRest annotation through which the related resource was discovered on the rest object.
* @return true if allowed, false otherwise.
*/
boolean allowEmbedding(HALResource<? extends RestAddressableModel> halResource, LinkRest linkRest);
boolean allowEmbedding(HALResource<? extends RestAddressableModel> halResource, LinkRest linkRest, Link... oldLinks);
/**
* Tells whether this projection permits the linking of a particular linkable subresource.

View File

@@ -516,13 +516,14 @@ public class Utils {
* Adds embeds or links for all class-level LinkRel annotations for which embeds or links are allowed.
*
* @param halResource the resource.
* @param oldLinks previously traversed links
*/
public void embedOrLinkClassLevelRels(HALResource<RestAddressableModel> halResource) {
public void embedOrLinkClassLevelRels(HALResource<RestAddressableModel> halResource, Link... oldLinks) {
Projection projection = halResource.getContent().getProjection();
getLinkRests(halResource.getContent().getClass()).stream().forEach((linkRest) -> {
Link link = linkToSubResource(halResource.getContent(), linkRest.name());
if (projection.allowEmbedding(halResource, linkRest)) {
embedRelFromRepository(halResource, linkRest.name(), link, linkRest);
if (projection.allowEmbedding(halResource, linkRest, oldLinks)) {
embedRelFromRepository(halResource, linkRest.name(), link, linkRest, oldLinks);
halResource.add(link); // unconditionally link if embedding was allowed
} else if (projection.allowLinking(halResource, linkRest)) {
halResource.add(link);
@@ -561,6 +562,10 @@ public class Utils {
*/
void embedRelFromRepository(HALResource<? extends RestAddressableModel> resource,
String rel, Link link, LinkRest linkRest) {
embedRelFromRepository(resource, rel, link, linkRest, new Link[] {});
}
void embedRelFromRepository(HALResource<? extends RestAddressableModel> resource,
String rel, Link link, LinkRest linkRest, Link... oldLinks) {
if (resource.getContent().getEmbedLevel() == EMBED_MAX_LEVELS) {
return;
}
@@ -572,7 +577,7 @@ public class Utils {
Object contentId = getContentIdForLinkMethod(resource.getContent(), method);
try {
Object linkedObject = method.invoke(linkRepository, null, contentId, null, projection);
resource.embedResource(rel, wrapForEmbedding(resource, linkedObject, link));
resource.embedResource(rel, wrapForEmbedding(resource, linkedObject, link, oldLinks));
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof RuntimeException) {
throw (RuntimeException) e.getTargetException();
@@ -677,17 +682,23 @@ public class Utils {
*/
private Object wrapForEmbedding(HALResource<? extends RestAddressableModel> resource,
Object linkedObject, Link link) {
return wrapForEmbedding(resource, linkedObject, link, new Link[] {});
}
private Object wrapForEmbedding(HALResource<? extends RestAddressableModel> resource,
Object linkedObject, Link link, Link... oldLinks) {
int childEmbedLevel = resource.getContent().getEmbedLevel() + 1;
Link[] newList = Arrays.copyOf(oldLinks, oldLinks.length+1);
newList[oldLinks.length] = link;
if (linkedObject instanceof RestAddressableModel) {
RestAddressableModel restObject = (RestAddressableModel) linkedObject;
restObject.setEmbedLevel(childEmbedLevel);
return converter.toResource(restObject);
return converter.toResource(restObject, newList);
} else if (linkedObject instanceof Page) {
// The first page has already been constructed by a link repository and we only need to wrap it
Page<RestAddressableModel> page = (Page<RestAddressableModel>) linkedObject;
return new EmbeddedPage(link.getHref(), page.map((restObject) -> {
restObject.setEmbedLevel(childEmbedLevel);
return converter.toResource(restObject);
return converter.toResource(restObject, newList);
}), null, link.getRel());
} else if (linkedObject instanceof List) {
// The full list has been retrieved and we need to provide the first page for embedding
@@ -699,7 +710,7 @@ public class Utils {
return new EmbeddedPage(link.getHref(),
page.map((restObject) -> {
restObject.setEmbedLevel(childEmbedLevel);
return converter.toResource(restObject);
return converter.toResource(restObject, newList);
}),
list, link.getRel());
} else {

View File

@@ -87,7 +87,7 @@ public class MockProjection implements Projection {
return halResource;
}
public boolean allowEmbedding(HALResource halResource, LinkRest linkRest) {
public boolean allowEmbedding(HALResource halResource, LinkRest linkRest, Link... oldLinks) {
return true;
}