#843 - Introduced EntityLinks.link(To|For)ItemResource(Object, Function).

The introduced methods allow clients to pass an identifier extractor function. Also introduced a new (Extended)TypedEntityLinks that exposes the same functionality as EntityLinks but with a shared identifier extractor function so that clients don't have to provide it for each method invocation.

Prepares the fix for #842.
This commit is contained in:
Oliver Drotbohm
2019-02-28 20:29:06 +01:00
parent 33f22e8bc9
commit ff8bc33daf
4 changed files with 215 additions and 13 deletions

View File

@@ -15,17 +15,21 @@
*/
package org.springframework.hateoas.server;
import java.util.function.Function;
import org.springframework.hateoas.IanaLinkRelations;
import org.springframework.hateoas.Identifiable;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.server.TypedEntityLinks.ExtendedTypedEntityLinks;
import org.springframework.plugin.core.Plugin;
import org.springframework.util.Assert;
/**
* Accessor to links pointing to controllers backing an entity type. The {@link IllegalArgumentException} potentially
* thrown by the declared methods will only appear if the {@link #supports(Class)} method has returned {@literal false}
* and the method has been invoked anyway, i.e. if {@link #supports(Class)} returns {@literal true} it's safe to invoke
* the interface methods an the exception will never be thrown.
*
*
* @author Oliver Gierke
*/
public interface EntityLinks extends Plugin<Class<?>> {
@@ -33,7 +37,7 @@ public interface EntityLinks extends Plugin<Class<?>> {
/**
* Returns a {@link LinkBuilder} able to create links to the controller managing the given entity type. Expects a
* controller being mapped to a fully expanded URI template (i.e. not path variables being used).
*
*
* @param type the entity type to point to, must not be {@literal null}.
* @return the {@link LinkBuilder} pointing to the collection resource. Will never be {@literal null}.
* @throws IllegalArgumentException in case the given type is unknown the entity links infrastructure.
@@ -43,7 +47,7 @@ public interface EntityLinks extends Plugin<Class<?>> {
/**
* Returns a {@link LinkBuilder} able to create links to the controller managing the given entity type, unfolding the
* given parameters into the URI template the backing controller is mapped to.
*
*
* @param type the entity type to point to, must not be {@literal null}.
* @return the {@link LinkBuilder} pointing to the collection resource.
* @throws IllegalArgumentException in case the given type is unknown the entity links infrastructure.
@@ -52,19 +56,37 @@ public interface EntityLinks extends Plugin<Class<?>> {
/**
* Returns a {@link LinkBuilder} able to create links to the controller managing the given entity type and id.
* Implementations will know about the URI structure being used to expose single-resource URIs.
*
* Implementations will know about the URI structure being used to expose item-resource URIs.
*
* @param type the entity type to point to, must not be {@literal null}.
* @param id the id of the object of the handed type, {@link Identifiable}s will be unwrapped.
* @return the {@link LinkBuilder} pointing to the single resource identified by the given type and id. Will never be
* @return the {@link LinkBuilder} pointing to the item resource identified by the given type and id. Will never be
* {@literal null}.
* @throws IllegalArgumentException in case the given type is unknown the entity links infrastructure.
*/
LinkBuilder linkForItemResource(Class<?> type, Object id);
/**
* Returns a {@link LinkBuilder} able to create links to the controller managing the given entity type and identifier
* extractor. Implementations will know about the URI structure being used to expose item-resource URIs.
*
* @param type the entity to point to, must not be {@literal null}.
* @param identifierExtractor an extractor function to determine the id of the given entity, must not be
* {@literal null}.
* @return the {@link LinkBuilder} pointing to the item resource identified by the given entity. Will never be
* {@literal null}.
* @throws IllegalArgumentException in case the given type is unknown the entity links infrastructure.
*/
default <T> LinkBuilder linkForItemResource(T entity, Function<T, Object> identifierExtractor) {
Assert.notNull(identifierExtractor, "Identifier extractor must not be null!");
return linkForItemResource(entity.getClass(), identifierExtractor.apply(entity));
}
/**
* Returns a {@link LinkBuilder} able to create links to the controller managing the given entity.
*
*
* @see #linkForItemResource(Class, Object)
* @param entity the entity type to point to, must not be {@literal null}.
* @return the {@link LinkBuilder} pointing the given entity. Will never be {@literal null}.
@@ -75,7 +97,7 @@ public interface EntityLinks extends Plugin<Class<?>> {
/**
* Creates a {@link Link} pointing to the collection resource of the given type. The relation type of the link will be
* determined by the implementation class and should be defaulted to {@link IanaLinkRelations#SELF}.
*
*
* @param type the entity type to point to, must not be {@literal null}.
* @return the {@link Link} pointing to the collection resource exposed for the given entity. Will never be
* {@literal null}.
@@ -84,9 +106,9 @@ public interface EntityLinks extends Plugin<Class<?>> {
Link linkToCollectionResource(Class<?> type);
/**
* Creates a {@link Link} pointing to single resource backing the given entity type and id. The relation type of the
* Creates a {@link Link} pointing to item resource backing the given entity type and id. The relation type of the
* link will be determined by the implementation class and should be defaulted to {@link IanaLinkRelations#SELF}.
*
*
* @param type the entity type to point to, must not be {@literal null}.
* @param id the identifier of the entity of the given type
* @return the {@link Link} pointing to the resource exposed for the entity with the given type and id. Will never be
@@ -96,12 +118,49 @@ public interface EntityLinks extends Plugin<Class<?>> {
Link linkToItemResource(Class<?> type, Object id);
/**
* Creates a {@link Link} pointing to single resource backing the given entity. The relation type of the link will be
* Creates a {@link Link} pointing to item resource backing the given entity and identifier extractor. The relation
* type of the link will be determined by the implementation class and should be defaulted to
* {@link IanaLinkRelations#SELF}.
*
* @param type the entity to point to, must not be {@literal null}.
* @param identifierExtractor an extractor function to determine the id of the given entity.
* @return the {@link Link} pointing to the resource exposed for the given entity. Will never be {@literal null}.
* @throws IllegalArgumentException in case the given type is unknown the entity links infrastructure.
*/
default <T> Link linkToItemResource(T entity, Function<T, Object> identifierExtractor) {
return linkToItemResource(entity.getClass(), identifierExtractor.apply(entity));
}
/**
* Creates a {@link Link} pointing to item resource backing the given entity. The relation type of the link will be
* determined by the implementation class and should be defaulted to {@link IanaLinkRelations#SELF}.
*
*
* @param entity the entity type to point to, must not be {@literal null}.
* @return the {@link Link} pointing to the resource exposed for the given entity. Will never be {@literal null}.
* @throws IllegalArgumentException in case the type of the given entity is unknown the entity links infrastructure.
*/
Link linkToItemResource(Identifiable<?> entity);
/**
* Creates a {@link TypedEntityLinks} instance using the given identifier extractor function.
*
* @param <T> the type of entity to be handled.
* @param extractor the extractor to use to derive an identifier from the given entity.
* @return
*/
default <T> TypedEntityLinks<T> forType(Function<T, ? extends Object> extractor) {
return new TypedEntityLinks<>(extractor, this);
}
/**
* Creates a {@link TypedEntityLinks} instance using the given type and identifier extractor function.
*
* @param <T> the type of entity to be handled.
* @param type the type of entity.
* @param extractor the extractor to use to derive an identifier from the given entity.
* @return
*/
default <T> ExtendedTypedEntityLinks<T> forType(Class<T> type, Function<T, Object> extractor) {
return new ExtendedTypedEntityLinks<>(extractor, this, type);
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.hateoas.server;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import java.util.function.Function;
import org.springframework.hateoas.IanaLinkRelations;
import org.springframework.hateoas.Link;
import org.springframework.util.Assert;
/**
* Entity links API to create {@link Link}s and {@link LinkBuilder} instances based on an identifier function.
*
* @author Oliver Drotbohm
* @see EntityLinks#forType(Function)
* @see EntityLinks#forType(Class, Function)
*/
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public class TypedEntityLinks<T> {
private final @NonNull Function<T, ? extends Object> identifierExtractor;
private final @NonNull EntityLinks entityLinks;
/**
* Returns a {@link LinkBuilder} able to create links to the controller managing the given entity. Implementations
* will know about the URI structure being used to expose item-resource URIs.
*
* @param type the entity to point to, must not be {@literal null}.
* @return the {@link LinkBuilder} pointing to the item resource identified by the given entity. Will never be
* {@literal null}.
* @throws IllegalArgumentException in case the given type is unknown the entity links infrastructure.
*/
public LinkBuilder linkForItemResource(T entity) {
return entityLinks.linkForItemResource(entity.getClass(), identifierExtractor.apply(entity));
}
/**
* Creates a {@link Link} pointing to item resource backing the given entity. The relation type of the link will be
* determined by the implementation class and should be defaulted to {@link IanaLinkRelations#SELF}.
*
* @param type the entity to point to, must not be {@literal null}.
* @return the {@link Link} pointing to the resource exposed for the given entity. Will never be {@literal null}.
* @throws IllegalArgumentException in case the given type is unknown the entity links infrastructure.
*/
public Link linkToItemResource(T entity) {
return entityLinks.linkToItemResource(entity.getClass(), identifierExtractor.apply(entity));
}
/**
* Extension of {@link TypedEntityLinks} that exposes the ability to create links to collection resources as well.
*
* @author Oliver Drotbohm
*/
public static class ExtendedTypedEntityLinks<T> extends TypedEntityLinks<T> {
private final Class<T> type;
private final EntityLinks delegate;
ExtendedTypedEntityLinks(Function<T, ? extends Object> identifierExtractor, EntityLinks delegate, Class<T> type) {
super(identifierExtractor, delegate);
Assert.notNull(type, "Type must not be null!");
this.type = type;
this.delegate = delegate;
}
/**
* Creates a {@link Link} pointing to the collection resource of the configured type. The relation type of the link
* will be determined by the implementation class and should be defaulted to {@link IanaLinkRelations#SELF}.
*
* @param type the entity type to point to, must not be {@literal null}.
* @return the {@link Link} pointing to the collection resource exposed for the configured entity type. Will never
* be {@literal null}.
* @throws IllegalArgumentException in case the given type is unknown the entity links infrastructure.
*/
public Link linkToCollectionResource() {
return delegate.linkToCollectionResource(type);
}
}
}