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.Page;
import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Resource; import org.springframework.hateoas.Resource;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service; 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. * @throws ClassCastException if the resource type is not compatible with the inferred return type.
*/ */
public <T extends HALResource> T toResource(RestModel restObject) { 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); T halResource = getResource(restObject);
if (restObject instanceof RestAddressableModel) { if (restObject instanceof RestAddressableModel) {
utils.embedOrLinkClassLevelRels(halResource); utils.embedOrLinkClassLevelRels(halResource, oldLinks);
halLinkService.addLinks(halResource); halLinkService.addLinks(halResource);
Projection projection = ((RestAddressableModel) restObject).getProjection(); Projection projection = ((RestAddressableModel) restObject).getProjection();
return projection.transformResource(halResource); 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.RestAddressableModel;
import org.dspace.app.rest.model.RestModel; import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.hateoas.HALResource; import org.dspace.app.rest.model.hateoas.HALResource;
import org.springframework.hateoas.Link;
/** /**
* Abstract base class for projections. * Abstract base class for projections.
@@ -35,7 +36,7 @@ public abstract class AbstractProjection implements Projection {
} }
@Override @Override
public boolean allowEmbedding(HALResource<? extends RestAddressableModel> halResource, LinkRest linkRest) { public boolean allowEmbedding(HALResource<? extends RestAddressableModel> halResource, LinkRest linkRest, Link... oldLinks) {
return false; 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.RestAddressableModel;
import org.dspace.app.rest.model.RestModel; import org.dspace.app.rest.model.RestModel;
import org.dspace.app.rest.model.hateoas.HALResource; import org.dspace.app.rest.model.hateoas.HALResource;
import org.springframework.hateoas.Link;
/** /**
* A projection that combines the behavior of multiple projections. * A projection that combines the behavior of multiple projections.
@@ -61,9 +62,9 @@ public class CompositeProjection implements Projection {
} }
@Override @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) { for (Projection projection : projections) {
if (projection.allowEmbedding(halResource, linkRest)) { if (projection.allowEmbedding(halResource, linkRest, oldLinks)) {
return true; 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.LinkRest;
import org.dspace.app.rest.model.RestAddressableModel; import org.dspace.app.rest.model.RestAddressableModel;
import org.dspace.app.rest.model.hateoas.HALResource; import org.dspace.app.rest.model.hateoas.HALResource;
import org.springframework.hateoas.Link;
/** /**
* Projection that allows a given set of rels to be embedded. * Projection that allows a given set of rels to be embedded.
@@ -32,9 +33,22 @@ public class EmbedRelsProjection extends AbstractProjection {
} }
@Override @Override
public boolean allowEmbedding(HALResource<? extends RestAddressableModel> halResource, LinkRest linkRest) { public boolean allowEmbedding(HALResource<? extends RestAddressableModel> halResource, LinkRest linkRest, Link... oldLinks) {
if (halResource.getContent().getEmbedLevel() == 0) if (halResource.getContent().getEmbedLevel() == 0 && embedRels.contains(linkRest.name()))
return 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; 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.LinkRest;
import org.dspace.app.rest.model.hateoas.HALResource; import org.dspace.app.rest.model.hateoas.HALResource;
import org.springframework.hateoas.Link;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
@@ -24,7 +25,7 @@ public class FullProjection extends AbstractProjection {
} }
@Override @Override
public boolean allowEmbedding(HALResource halResource, LinkRest linkRest) { public boolean allowEmbedding(HALResource halResource, LinkRest linkRest, Link... oldLinks) {
return true; 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.model.hateoas.HALResource;
import org.dspace.app.rest.repository.DSpaceRestRepository; import org.dspace.app.rest.repository.DSpaceRestRepository;
import org.dspace.app.rest.utils.Utils; import org.dspace.app.rest.utils.Utils;
import org.springframework.hateoas.Link;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RestController; 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. * @param linkRest the LinkRest annotation through which the related resource was discovered on the rest object.
* @return true if allowed, false otherwise. * @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. * 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. * Adds embeds or links for all class-level LinkRel annotations for which embeds or links are allowed.
* *
* @param halResource the resource. * @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(); Projection projection = halResource.getContent().getProjection();
getLinkRests(halResource.getContent().getClass()).stream().forEach((linkRest) -> { getLinkRests(halResource.getContent().getClass()).stream().forEach((linkRest) -> {
Link link = linkToSubResource(halResource.getContent(), linkRest.name()); Link link = linkToSubResource(halResource.getContent(), linkRest.name());
if (projection.allowEmbedding(halResource, linkRest)) { if (projection.allowEmbedding(halResource, linkRest, oldLinks)) {
embedRelFromRepository(halResource, linkRest.name(), link, linkRest); embedRelFromRepository(halResource, linkRest.name(), link, linkRest, oldLinks);
halResource.add(link); // unconditionally link if embedding was allowed halResource.add(link); // unconditionally link if embedding was allowed
} else if (projection.allowLinking(halResource, linkRest)) { } else if (projection.allowLinking(halResource, linkRest)) {
halResource.add(link); halResource.add(link);
@@ -561,6 +562,10 @@ public class Utils {
*/ */
void embedRelFromRepository(HALResource<? extends RestAddressableModel> resource, void embedRelFromRepository(HALResource<? extends RestAddressableModel> resource,
String rel, Link link, LinkRest linkRest) { 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) { if (resource.getContent().getEmbedLevel() == EMBED_MAX_LEVELS) {
return; return;
} }
@@ -572,7 +577,7 @@ public class Utils {
Object contentId = getContentIdForLinkMethod(resource.getContent(), method); Object contentId = getContentIdForLinkMethod(resource.getContent(), method);
try { try {
Object linkedObject = method.invoke(linkRepository, null, contentId, null, projection); 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) { } catch (InvocationTargetException e) {
if (e.getTargetException() instanceof RuntimeException) { if (e.getTargetException() instanceof RuntimeException) {
throw (RuntimeException) e.getTargetException(); throw (RuntimeException) e.getTargetException();
@@ -677,17 +682,23 @@ public class Utils {
*/ */
private Object wrapForEmbedding(HALResource<? extends RestAddressableModel> resource, private Object wrapForEmbedding(HALResource<? extends RestAddressableModel> resource,
Object linkedObject, Link link) { 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; int childEmbedLevel = resource.getContent().getEmbedLevel() + 1;
Link[] newList = Arrays.copyOf(oldLinks, oldLinks.length+1);
newList[oldLinks.length] = link;
if (linkedObject instanceof RestAddressableModel) { if (linkedObject instanceof RestAddressableModel) {
RestAddressableModel restObject = (RestAddressableModel) linkedObject; RestAddressableModel restObject = (RestAddressableModel) linkedObject;
restObject.setEmbedLevel(childEmbedLevel); restObject.setEmbedLevel(childEmbedLevel);
return converter.toResource(restObject); return converter.toResource(restObject, newList);
} else if (linkedObject instanceof Page) { } else if (linkedObject instanceof Page) {
// The first page has already been constructed by a link repository and we only need to wrap it // The first page has already been constructed by a link repository and we only need to wrap it
Page<RestAddressableModel> page = (Page<RestAddressableModel>) linkedObject; Page<RestAddressableModel> page = (Page<RestAddressableModel>) linkedObject;
return new EmbeddedPage(link.getHref(), page.map((restObject) -> { return new EmbeddedPage(link.getHref(), page.map((restObject) -> {
restObject.setEmbedLevel(childEmbedLevel); restObject.setEmbedLevel(childEmbedLevel);
return converter.toResource(restObject); return converter.toResource(restObject, newList);
}), null, link.getRel()); }), null, link.getRel());
} else if (linkedObject instanceof List) { } else if (linkedObject instanceof List) {
// The full list has been retrieved and we need to provide the first page for embedding // 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(), return new EmbeddedPage(link.getHref(),
page.map((restObject) -> { page.map((restObject) -> {
restObject.setEmbedLevel(childEmbedLevel); restObject.setEmbedLevel(childEmbedLevel);
return converter.toResource(restObject); return converter.toResource(restObject, newList);
}), }),
list, link.getRel()); list, link.getRel());
} else { } else {

View File

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