Moved al HATEOAS related code from spring-rest-full module to spring-boot-rest

This commit is contained in:
Ger Roza
2019-02-14 11:54:35 -02:00
parent 69e29b364d
commit f8f78afe85
11 changed files with 12 additions and 125 deletions

View File

@@ -8,8 +8,6 @@ The "REST With Spring" Classes: http://bit.ly/restwithspring
The "Learn Spring Security" Classes: http://github.learnspringsecurity.com
### Relevant Articles:
- [HATEOAS for a Spring REST Service](http://www.baeldung.com/rest-api-discoverability-with-spring)
- [REST API Discoverability and HATEOAS](http://www.baeldung.com/restful-web-service-discoverability)
- [ETags for REST with Spring](http://www.baeldung.com/etags-for-rest-with-spring)
- [Integration Testing with the Maven Cargo plugin](http://www.baeldung.com/integration-testing-with-the-maven-cargo-plugin)
- [Introduction to Spring Data JPA](http://www.baeldung.com/the-persistence-layer-with-spring-data-jpa)

View File

@@ -7,7 +7,6 @@ import javax.servlet.http.HttpServletResponse;
import org.baeldung.persistence.model.Foo;
import org.baeldung.persistence.service.IFooService;
import org.baeldung.web.hateoas.event.ResourceCreatedEvent;
import org.baeldung.web.hateoas.event.SingleResourceRetrievedEvent;
import org.baeldung.web.util.RestPreconditions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
@@ -53,7 +52,6 @@ public class FooController {
public Foo findById(@PathVariable("id") final Long id, final HttpServletResponse response) {
final Foo resourceById = RestPreconditions.checkFound(service.findOne(id));
eventPublisher.publishEvent(new SingleResourceRetrievedEvent(this, response));
return resourceById;
}

View File

@@ -1,22 +1,14 @@
package org.baeldung.web.controller;
import java.net.URI;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.baeldung.web.metric.IActuatorMetricService;
import org.baeldung.web.metric.IMetricService;
import org.baeldung.web.util.LinkUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.util.UriTemplate;
@Controller
@RequestMapping(value = "/auth/")
@@ -34,18 +26,6 @@ public class RootController {
// API
// discover
@RequestMapping(value = "admin", method = RequestMethod.GET)
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public void adminRoot(final HttpServletRequest request, final HttpServletResponse response) {
final String rootUri = request.getRequestURL().toString();
final URI fooUri = new UriTemplate("{rootUri}/{resource}").expand(rootUri, "foo");
final String linkToFoo = LinkUtil.createLinkHeader(fooUri.toASCIIString(), "collection");
response.addHeader("Link", linkToFoo);
}
@RequestMapping(value = "/metric", method = RequestMethod.GET)
@ResponseBody
public Map getMetric() {

View File

@@ -1,22 +0,0 @@
package org.baeldung.web.hateoas.event;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.ApplicationEvent;
public class SingleResourceRetrievedEvent extends ApplicationEvent {
private final HttpServletResponse response;
public SingleResourceRetrievedEvent(final Object source, final HttpServletResponse response) {
super(source);
this.response = response;
}
// API
public HttpServletResponse getResponse() {
return response;
}
}

View File

@@ -1,34 +0,0 @@
package org.baeldung.web.hateoas.listener;
import javax.servlet.http.HttpServletResponse;
import org.baeldung.web.hateoas.event.SingleResourceRetrievedEvent;
import org.baeldung.web.util.LinkUtil;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import com.google.common.base.Preconditions;
import com.google.common.net.HttpHeaders;
@Component
class SingleResourceRetrievedDiscoverabilityListener implements ApplicationListener<SingleResourceRetrievedEvent> {
@Override
public void onApplicationEvent(final SingleResourceRetrievedEvent resourceRetrievedEvent) {
Preconditions.checkNotNull(resourceRetrievedEvent);
final HttpServletResponse response = resourceRetrievedEvent.getResponse();
addLinkHeaderOnSingleResourceRetrieval(response);
}
void addLinkHeaderOnSingleResourceRetrieval(final HttpServletResponse response) {
final String requestURL = ServletUriComponentsBuilder.fromCurrentRequestUri().build().toUri().toASCIIString();
final int positionOfLastSlash = requestURL.lastIndexOf("/");
final String uriForResourceCreation = requestURL.substring(0, positionOfLastSlash);
final String linkHeaderValue = LinkUtil.createLinkHeader(uriForResourceCreation, "collection");
response.addHeader(HttpHeaders.LINK, linkHeaderValue);
}
}

View File

@@ -1,81 +0,0 @@
package org.baeldung.common.web;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import java.io.Serializable;
import org.baeldung.persistence.model.Foo;
import org.baeldung.web.util.HTTPLinkHeaderUtil;
import org.hamcrest.core.AnyOf;
import org.junit.Test;
import org.springframework.http.MediaType;
import com.google.common.net.HttpHeaders;
import io.restassured.RestAssured;
import io.restassured.response.Response;
public abstract class AbstractDiscoverabilityLiveTest<T extends Serializable> extends AbstractLiveTest<T> {
public AbstractDiscoverabilityLiveTest(final Class<T> clazzToSet) {
super(clazzToSet);
}
// tests
// discoverability
@Test
public void whenInvalidPOSTIsSentToValidURIOfResource_thenAllowHeaderListsTheAllowedActions() {
// Given
final String uriOfExistingResource = createAsUri();
// When
final Response res = RestAssured.post(uriOfExistingResource);
// Then
final String allowHeader = res.getHeader(HttpHeaders.ALLOW);
assertThat(allowHeader, AnyOf.anyOf(containsString("GET"), containsString("PUT"), containsString("DELETE")));
}
@Test
public void whenResourceIsCreated_thenUriOfTheNewlyCreatedResourceIsDiscoverable() {
// When
final Foo newResource = new Foo(randomAlphabetic(6));
final Response createResp = RestAssured.given()
.contentType(MediaType.APPLICATION_JSON_VALUE)
.body(newResource)
.post(getURL());
final String uriOfNewResource = createResp.getHeader(HttpHeaders.LOCATION);
// Then
final Response response = RestAssured.given()
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.get(uriOfNewResource);
final Foo resourceFromServer = response.body().as(Foo.class);
assertThat(newResource, equalTo(resourceFromServer));
}
@Test
public void whenResourceIsRetrieved_thenUriToGetAllResourcesIsDiscoverable() {
// Given
final String uriOfExistingResource = createAsUri();
// When
final Response getResponse = RestAssured.get(uriOfExistingResource);
// Then
final String uriToAllResources = HTTPLinkHeaderUtil.extractURIByRel(getResponse.getHeader("Link"), "collection");
final Response getAllResponse = RestAssured.get(uriToAllResources);
assertThat(getAllResponse.getStatusCode(), is(200));
}
// template method
}

View File

@@ -1,35 +0,0 @@
package org.baeldung.web;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
import org.baeldung.common.web.AbstractDiscoverabilityLiveTest;
import org.baeldung.persistence.model.Foo;
import org.baeldung.spring.ConfigIntegrationTest;
import org.junit.runner.RunWith;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { ConfigIntegrationTest.class }, loader = AnnotationConfigContextLoader.class)
@ActiveProfiles("test")
public class FooDiscoverabilityLiveTest extends AbstractDiscoverabilityLiveTest<Foo> {
public FooDiscoverabilityLiveTest() {
super(Foo.class);
}
// API
@Override
public final void create() {
create(new Foo(randomAlphabetic(6)));
}
@Override
public final String createAsUri() {
return createAsUri(new Foo(randomAlphabetic(6)));
}
}

View File

@@ -6,8 +6,7 @@ import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
// @formatter:off
FooDiscoverabilityLiveTest.class
,FooLiveTest.class
FooLiveTest.class
}) //
public class LiveTestSuiteLiveTest {

View File

@@ -1,36 +0,0 @@
package org.baeldung.web.util;
public final class HTTPLinkHeaderUtil {
private HTTPLinkHeaderUtil() {
throw new AssertionError();
}
//
public static String extractURIByRel(final String linkHeader, final String rel) {
if (linkHeader == null) {
return null;
}
String uriWithSpecifiedRel = null;
final String[] links = linkHeader.split(", ");
String linkRelation;
for (final String link : links) {
final int positionOfSeparator = link.indexOf(';');
linkRelation = link.substring(positionOfSeparator + 1, link.length()).trim();
if (extractTypeOfRelation(linkRelation).equals(rel)) {
uriWithSpecifiedRel = link.substring(1, positionOfSeparator - 1);
break;
}
}
return uriWithSpecifiedRel;
}
private static Object extractTypeOfRelation(final String linkRelation) {
final int positionOfEquals = linkRelation.indexOf('=');
return linkRelation.substring(positionOfEquals + 2, linkRelation.length() - 1).trim();
}
}