#842 - Remove Identifiable.

This commit is contained in:
Oliver Drotbohm
2019-02-28 23:58:42 +01:00
parent 3077211db2
commit 23c0a34c66
11 changed files with 9 additions and 439 deletions

View File

@@ -1,34 +0,0 @@
/*
* Copyright 2012 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;
import java.io.Serializable;
import java.util.Optional;
/**
* Interface to mark objects that are identifiable by an ID of any type.
*
* @author Oliver Gierke
*/
public interface Identifiable<ID extends Serializable> {
/**
* Returns the id identifying the object.
*
* @return the identifier or {@link Optional#empty()} if not available.
*/
Optional<ID> getId();
}

View File

@@ -23,7 +23,6 @@ import java.util.stream.Collectors;
import org.springframework.util.Assert;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
@@ -33,7 +32,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* @author Johhny Lim
* @author Greg Turnquist
*/
public class RepresentationModel<T extends RepresentationModel<? extends T>> implements Identifiable<Link> {
public class RepresentationModel<T extends RepresentationModel<? extends T>> {
private final List<Link> links;
@@ -57,14 +56,6 @@ public class RepresentationModel<T extends RepresentationModel<? extends T>> imp
this.links.addAll(initialLinks);
}
/**
* Returns the {@link Link} with a rel of {@link IanaLinkRelations#SELF}.
*/
@JsonIgnore
public Optional<Link> getId() {
return getLink(IanaLinkRelations.SELF);
}
/**
* Adds the given link to the resource.
*

View File

@@ -18,7 +18,6 @@ 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;
@@ -59,7 +58,7 @@ public interface EntityLinks extends Plugin<Class<?>> {
* 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.
* @param id the id of the object of the handed type, must not be {@literal null}.
* @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.
@@ -84,16 +83,6 @@ public interface EntityLinks extends Plugin<Class<?>> {
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}.
* @throws IllegalArgumentException in case the type of the given entity is unknown the entity links infrastructure.
*/
LinkBuilder linkForItemResource(Identifiable<?> entity);
/**
* 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}.
@@ -131,16 +120,6 @@ public interface EntityLinks extends Plugin<Class<?>> {
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.
*

View File

@@ -18,7 +18,6 @@ package org.springframework.hateoas.server;
import java.net.URI;
import org.springframework.hateoas.IanaLinkRelations;
import org.springframework.hateoas.Identifiable;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.LinkRelation;
@@ -30,23 +29,13 @@ import org.springframework.hateoas.LinkRelation;
public interface LinkBuilder {
/**
* Adds the given object's {@link String} representation as sub-resource to the current URI. Will unwrap
* {@link Identifiable}s to their id value (see {@link Identifiable#getId()}).
* Adds the given object's {@link String} representation as sub-resource to the current URI.
*
* @param object
* @return
*/
LinkBuilder slash(Object object);
/**
* Adds the given {@link Identifiable}'s id as sub-resource. Will simply return the {@link LinkBuilder} as is if the
* given entity is {@literal null}.
*
* @param identifiable
* @return
*/
LinkBuilder slash(Identifiable<?> identifiable);
/**
* Creates a URI of the link built by the current builder instance.
*

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012 the original author or authors.
* Copyright 2012-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.
@@ -15,31 +15,18 @@
*/
package org.springframework.hateoas.server.core;
import org.springframework.hateoas.Identifiable;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.server.EntityLinks;
import org.springframework.hateoas.server.LinkBuilder;
import org.springframework.util.Assert;
/**
* Implementation base class to delegate the higher level methods of {@link EntityLinks} by delegating to the more fine
* grained ones to reduce the implementation effort for actual implementation classes.
*
*
* @author Oliver Gierke
*/
public abstract class AbstractEntityLinks implements EntityLinks {
/*
* (non-Javadoc)
* @see org.springframework.hateoas.EntityLinks#getLinkToSingleResource(org.springframework.hateoas.Identifiable)
*/
@Override
public Link linkToItemResource(Identifiable<?> entity) {
Assert.notNull(entity, "Entity must not be null!");
return linkToItemResource(entity.getClass(), entity.getId());
}
/*
/*
* (non-Javadoc)
* @see org.springframework.hateoas.EntityLinks#linkForSingleResource(java.lang.Class, java.lang.Object)
*/
@@ -47,14 +34,4 @@ public abstract class AbstractEntityLinks implements EntityLinks {
public LinkBuilder linkForItemResource(Class<?> type, Object id) {
return linkFor(type).slash(id);
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.EntityLinks#linkForSingleResource(org.springframework.hateoas.Identifiable)
*/
@Override
public LinkBuilder linkForItemResource(Identifiable<?> entity) {
Assert.notNull(entity, "Entity must not be null!");
return linkForItemResource(entity.getClass(), entity.getId());
}
}

View File

@@ -29,7 +29,6 @@ import java.util.function.Function;
import org.springframework.hateoas.Affordance;
import org.springframework.hateoas.IanaLinkRelations;
import org.springframework.hateoas.Identifiable;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.LinkRelation;
import org.springframework.hateoas.server.LinkBuilder;
@@ -93,10 +92,6 @@ public abstract class LinkBuilderSupport<T extends LinkBuilder> implements LinkB
return getThis();
}
if (object instanceof Identifiable) {
return slash((Identifiable<?>) object);
}
String path = object.toString();
if (path.endsWith("#")) {
@@ -130,19 +125,6 @@ public abstract class LinkBuilderSupport<T extends LinkBuilder> implements LinkB
});
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.LinkBuilder#slash(org.springframework.hateoas.Identifiable)
*/
public T slash(Identifiable<?> identifyable) {
if (identifyable == null) {
return getThis();
}
return slash(identifyable.getId());
}
/*
* (non-Javadoc)
* @see org.springframework.hateoas.LinkBuilder#toUri()

View File

@@ -1,87 +0,0 @@
/*
* Copyright 2012-2017 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.mvc;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
import java.util.Arrays;
import org.springframework.hateoas.Identifiable;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.server.RepresentationModelAssembler;
import org.springframework.util.Assert;
/**
* Base class to implement {@link RepresentationModelAssembler}s. Will automate {@link RepresentationModel} instance
* creation and make sure a self-link is always added.
*
* @author Oliver Gierke
*/
public abstract class IdentifiableRepresentationModelAssemblerSupport<T extends Identifiable<?>, D extends RepresentationModel<D>>
extends RepresentationModelAssemblerSupport<T, D> {
private final Class<?> controllerClass;
/**
* Creates a new {@link RepresentationModelAssemblerSupport} using the given controller class and resource type.
*
* @param controllerClass must not be {@literal null}.
* @param resourceType must not be {@literal null}.
*/
public IdentifiableRepresentationModelAssemblerSupport(Class<?> controllerClass, Class<D> resourceType) {
super(controllerClass, resourceType);
this.controllerClass = controllerClass;
}
/**
* Creates a new resource and adds a self link to it consisting using the {@link Identifiable}'s id.
*
* @param entity must not be {@literal null}.
* @return
*/
protected D createModel(T entity) {
return createModel(entity, new Object[0]);
}
protected D createModel(T entity, Object... parameters) {
return createModelWithId(entity.getId(), entity, parameters);
}
@Override
protected D createModelWithId(Object id, T entity, Object... parameters) {
Assert.notNull(entity, "Entity must not be null!");
Assert.notNull(id, "Id must not be null!");
D instance = instantiateModel(entity);
instance.add(linkTo(controllerClass, unwrapIdentifyables(parameters)).slash(id).withSelfRel());
return instance;
}
/**
* Extracts the ids of the given values in case they're {@link Identifiable}s. Returns all other objects as they are.
*
* @param values must not be {@literal null}.
* @return
*/
private Object[] unwrapIdentifyables(Object[] values) {
return Arrays.stream(values) //
.map(element -> element instanceof Identifiable ? ((Identifiable<?>) element).getId().get() : element) //
.toArray();
}
}

View File

@@ -32,6 +32,7 @@ public class RepresentationModelUnitTest {
public void setsUpWithEmptyLinkList() {
RepresentationModel<?> support = new RepresentationModel<>();
assertThat(support.hasLinks()).isFalse();
assertThat(support.hasLink(IanaLinkRelations.SELF.value())).isFalse();
assertThat(support.getLinks().isEmpty()).isTrue();
@@ -45,7 +46,6 @@ public class RepresentationModelUnitTest {
RepresentationModel<?> support = new RepresentationModel<>();
support.add(link);
assertThat(support.getId()).isEmpty();
assertThat(support.hasLinks()).isTrue();
assertThat(support.hasLink(link.getRel())).isTrue();
assertThat(support.getLink(link.getRel())).hasValue(link);
@@ -75,7 +75,6 @@ public class RepresentationModelUnitTest {
RepresentationModel<?> support = new RepresentationModel<>();
support.add(Arrays.asList(first, second));
assertThat(support.getId()).isEmpty();
assertThat(support.hasLinks()).isTrue();
assertThat(support.getLinks()).contains(first, second);
assertThat(support.getLinks()).hasSize(2);
@@ -83,16 +82,6 @@ public class RepresentationModelUnitTest {
assertThat(support.getLinks(IanaLinkRelations.NEXT.value())).contains(second);
}
@Test
public void selfLinkBecomesId() {
Link link = new Link("foo");
RepresentationModel<?> support = new RepresentationModel<>();
support.add(link);
assertThat(support.getId()).hasValue(link);
}
@Test(expected = IllegalArgumentException.class)
public void preventsNullLinkBeingAdded() {

View File

@@ -28,13 +28,10 @@ import java.util.Optional;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mockito;
import org.springframework.hateoas.IanaLinkRelations;
import org.springframework.hateoas.Identifiable;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.TemplateVariable;
import org.springframework.hateoas.TemplateVariable.VariableType;
import org.springframework.hateoas.server.mvc.ControllerLinkBuilder;
import org.springframework.hateoas.TestUtils;
import org.springframework.http.HttpEntity;
import org.springframework.util.MultiValueMap;
@@ -122,17 +119,6 @@ public class ControllerLinkBuilderUnitTest extends TestUtils {
assertThat(link.getHref()).isEqualTo("http://localhost");
}
@Test
@SuppressWarnings("unchecked")
public void usesIdOfIdentifyableForPathSegment() {
Identifiable<Long> identifyable = Mockito.mock(Identifiable.class);
Mockito.when(identifyable.getId()).thenReturn(Optional.of(10L));
Link link = linkTo(PersonControllerImpl.class).slash(identifyable).withSelfRel();
assertThat(link.getHref()).endsWith("/people/10");
}
@Test
public void appendingNullIsANoOp() {
@@ -626,14 +612,8 @@ public class ControllerLinkBuilderUnitTest extends TestUtils {
return UriComponentsBuilder.fromUriString(link.expand().getHref()).build();
}
static class Person implements Identifiable<Long> {
static class Person {
Long id;
@Override
public Optional<Long> getId() {
return Optional.ofNullable(id);
}
}
@RequestMapping("/people")

View File

@@ -1,177 +0,0 @@
/*
* Copyright 2012-2018 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.mvc;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.IanaLinkRelations;
import org.springframework.hateoas.Identifiable;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.RepresentationModel;
import org.springframework.hateoas.TestUtils;
import org.springframework.hateoas.server.LinkBuilder;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* Unit tests for {@link IdentifiableRepresentationModelAssemblerSupport}.
*
* @author Oliver Gierke
* @author Greg Turnquist
*/
public class IdentifiableResourceAssemblerSupportUnitTest extends TestUtils {
PersonResourceAssembler assembler = new PersonResourceAssembler();
Person person;
@Override
@Before
public void setUp() {
super.setUp();
this.person = new Person();
this.person.id = 10L;
this.person.alternateId = "id";
}
@Test
public void createsInstanceWithSelfLinkToController() {
PersonResource resource = assembler.createModel(person);
Link link = resource.getRequiredLink(IanaLinkRelations.SELF.value());
assertThat(link).isNotNull();
assertThat(resource.getLinks()).hasSize(1);
}
@Test
public void usesAlternateIdIfGivenExplicitly() {
PersonResource resource = assembler.createModelWithId(person.alternateId, person);
Optional<Link> selfLink = resource.getId();
assertThat(selfLink.map(Link::getHref)) //
.hasValueSatisfying(it -> assertThat(it).endsWith("/people/id"));
}
@Test
public void unwrapsIdentifyablesForParameters() {
PersonResource resource = new PersonResourceAssembler(ParameterizedController.class).createModel(person, person,
"bar");
Optional<Link> selfLink = resource.getId();
assertThat(selfLink.map(Link::getHref)) //
.hasValueSatisfying(it -> assertThat(it).endsWith("/people/10/bar/addresses/10"));
}
/**
* @see #416
*/
@Test
public void convertsEntitiesToListOfResources() {
Person first = new Person();
first.id = 1L;
Person second = new Person();
second.id = 2L;
List<PersonResource> result = assembler.map(Arrays.asList(first, second)).toListOfResources();
LinkBuilder builder = linkTo(PersonController.class);
PersonResource firstResource = new PersonResource();
firstResource.add(builder.slash(1L).withSelfRel());
PersonResource secondResource = new PersonResource();
secondResource.add(builder.slash(1L).withSelfRel());
assertThat(result).hasSize(2);
assertThat(result).contains(firstResource, secondResource);
}
/**
* @see #416
*/
@Test
public void convertsEntitiesToResources() {
Person first = new Person();
first.id = 1L;
Person second = new Person();
second.id = 2L;
CollectionModel<PersonResource> result = assembler.map(Arrays.asList(first, second)).toResources();
LinkBuilder builder = linkTo(PersonController.class);
PersonResource firstResource = new PersonResource();
firstResource.add(builder.slash(1L).withSelfRel());
PersonResource secondResource = new PersonResource();
secondResource.add(builder.slash(1L).withSelfRel());
assertThat(result).hasSize(2);
assertThat(result).contains(firstResource, secondResource);
}
@RequestMapping("/people")
static class PersonController {
}
@RequestMapping("/people/{id}/{foo}/addresses")
static class ParameterizedController {
}
static class Person implements Identifiable<Long> {
Long id;
String alternateId;
@Override
public Optional<Long> getId() {
return Optional.ofNullable(id);
}
}
static class PersonResource extends RepresentationModel<PersonResource> {
}
class PersonResourceAssembler extends IdentifiableRepresentationModelAssemblerSupport<Person, PersonResource> {
PersonResourceAssembler() {
this(PersonController.class);
}
PersonResourceAssembler(Class<?> controllerType) {
super(controllerType, PersonResource.class);
}
@Override
public PersonResource toModel(Person entity) {
return createModel(entity);
}
}
}

View File

@@ -28,9 +28,7 @@ import java.util.Optional;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mockito;
import org.springframework.hateoas.IanaLinkRelations;
import org.springframework.hateoas.Identifiable;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.TemplateVariable;
import org.springframework.hateoas.TemplateVariable.VariableType;
@@ -120,17 +118,6 @@ public class WebMvcLinkBuilderUnitTest extends TestUtils {
assertThat(link.getHref()).isEqualTo("http://localhost");
}
@Test
@SuppressWarnings("unchecked")
public void usesIdOfIdentifyableForPathSegment() {
Identifiable<Long> identifyable = Mockito.mock(Identifiable.class);
Mockito.when(identifyable.getId()).thenReturn(Optional.of(10L));
Link link = linkTo(PersonControllerImpl.class).slash(identifyable).withSelfRel();
assertThat(link.getHref()).endsWith("/people/10");
}
@Test
public void appendingNullIsANoOp() {
@@ -624,14 +611,8 @@ public class WebMvcLinkBuilderUnitTest extends TestUtils {
return UriComponentsBuilder.fromUriString(link.expand().getHref()).build();
}
static class Person implements Identifiable<Long> {
static class Person {
Long id;
@Override
public Optional<Long> getId() {
return Optional.ofNullable(id);
}
}
@RequestMapping("/people")