moved Rest Pagination article code from spring-rest-full to spring-boot-rest

This commit is contained in:
Ger Roza
2019-01-20 18:19:52 -02:00
parent 5d6640a87b
commit 14b65b327f
48 changed files with 1125 additions and 164 deletions
@@ -0,0 +1,5 @@
package com.baeldung;
public interface Consts {
int APPLICATION_PORT = 8082;
}
@@ -1,4 +1,4 @@
package com.baeldung.web;
package com.baeldung;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -0,0 +1,103 @@
package com.baeldung.common.web;
import static com.baeldung.web.util.HTTPLinkHeaderUtil.extractURIByRel;
import static org.apache.commons.lang3.RandomStringUtils.randomNumeric;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import java.io.Serializable;
import java.util.List;
import org.junit.Test;
import com.google.common.net.HttpHeaders;
import io.restassured.RestAssured;
import io.restassured.response.Response;
public abstract class AbstractBasicLiveTest<T extends Serializable> extends AbstractLiveTest<T> {
public AbstractBasicLiveTest(final Class<T> clazzToSet) {
super(clazzToSet);
}
// find - all - paginated
@Test
public void whenResourcesAreRetrievedPaged_then200IsReceived() {
create();
final Response response = RestAssured.get(getURL() + "?page=0&size=10");
assertThat(response.getStatusCode(), is(200));
}
@Test
public void whenPageOfResourcesAreRetrievedOutOfBounds_then404IsReceived() {
final String url = getURL() + "?page=" + randomNumeric(5) + "&size=10";
final Response response = RestAssured.get(url);
assertThat(response.getStatusCode(), is(404));
}
@Test
public void givenResourcesExist_whenFirstPageIsRetrieved_thenPageContainsResources() {
create();
final Response response = RestAssured.get(getURL() + "?page=0&size=10");
assertFalse(response.body().as(List.class).isEmpty());
}
@Test
public void whenFirstPageOfResourcesAreRetrieved_thenSecondPageIsNext() {
create();
create();
create();
final Response response = RestAssured.get(getURL() + "?page=0&size=2");
final String uriToNextPage = extractURIByRel(response.getHeader(HttpHeaders.LINK), "next");
assertEquals(getURL() + "?page=1&size=2", uriToNextPage);
}
@Test
public void whenFirstPageOfResourcesAreRetrieved_thenNoPreviousPage() {
final Response response = RestAssured.get(getURL() + "?page=0&size=2");
final String uriToPrevPage = extractURIByRel(response.getHeader(HttpHeaders.LINK), "prev");
assertNull(uriToPrevPage);
}
@Test
public void whenSecondPageOfResourcesAreRetrieved_thenFirstPageIsPrevious() {
create();
create();
final Response response = RestAssured.get(getURL() + "?page=1&size=2");
final String uriToPrevPage = extractURIByRel(response.getHeader(HttpHeaders.LINK), "prev");
assertEquals(getURL() + "?page=0&size=2", uriToPrevPage);
}
@Test
public void whenLastPageOfResourcesIsRetrieved_thenNoNextPageIsDiscoverable() {
create();
create();
create();
final Response first = RestAssured.get(getURL() + "?page=0&size=2");
final String uriToLastPage = extractURIByRel(first.getHeader(HttpHeaders.LINK), "last");
final Response response = RestAssured.get(uriToLastPage);
final String uriToNextPage = extractURIByRel(response.getHeader(HttpHeaders.LINK), "next");
assertNull(uriToNextPage);
}
// count
}
@@ -0,0 +1,65 @@
package com.baeldung.common.web;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import static com.baeldung.Consts.APPLICATION_PORT;
import java.io.Serializable;
import org.springframework.beans.factory.annotation.Autowired;
import com.baeldung.test.IMarshaller;
import com.google.common.base.Preconditions;
import com.google.common.net.HttpHeaders;
public abstract class AbstractLiveTest<T extends Serializable> {
protected final Class<T> clazz;
@Autowired
protected IMarshaller marshaller;
public AbstractLiveTest(final Class<T> clazzToSet) {
super();
Preconditions.checkNotNull(clazzToSet);
clazz = clazzToSet;
}
// template method
public abstract void create();
public abstract String createAsUri();
protected final void create(final T resource) {
createAsUri(resource);
}
protected final String createAsUri(final T resource) {
final Response response = createAsResponse(resource);
Preconditions.checkState(response.getStatusCode() == 201, "create operation: " + response.getStatusCode());
final String locationOfCreatedResource = response.getHeader(HttpHeaders.LOCATION);
Preconditions.checkNotNull(locationOfCreatedResource);
return locationOfCreatedResource;
}
final Response createAsResponse(final T resource) {
Preconditions.checkNotNull(resource);
final String resourceAsString = marshaller.encode(resource);
return RestAssured.given()
.contentType(marshaller.getMime())
.body(resourceAsString)
.post(getURL());
}
//
protected String getURL() {
return "http://localhost:" + APPLICATION_PORT + "/spring-boot-rest/auth/foos";
}
}
@@ -0,0 +1,17 @@
package com.baeldung.spring;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@ComponentScan("com.baeldung.test")
public class ConfigIntegrationTest implements WebMvcConfigurer {
public ConfigIntegrationTest() {
super();
}
// API
}
@@ -0,0 +1,15 @@
package com.baeldung.test;
import java.util.List;
public interface IMarshaller {
<T> String encode(final T entity);
<T> T decode(final String entityAsString, final Class<T> clazz);
<T> List<T> decodeList(final String entitiesAsString, final Class<T> clazz);
String getMime();
}
@@ -0,0 +1,81 @@
package com.baeldung.test;
import java.io.IOException;
import java.util.List;
import com.baeldung.persistence.model.Foo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Preconditions;
public final class JacksonMarshaller implements IMarshaller {
private final Logger logger = LoggerFactory.getLogger(JacksonMarshaller.class);
private final ObjectMapper objectMapper;
public JacksonMarshaller() {
super();
objectMapper = new ObjectMapper();
}
// API
@Override
public final <T> String encode(final T resource) {
Preconditions.checkNotNull(resource);
String entityAsJSON = null;
try {
entityAsJSON = objectMapper.writeValueAsString(resource);
} catch (final IOException ioEx) {
logger.error("", ioEx);
}
return entityAsJSON;
}
@Override
public final <T> T decode(final String resourceAsString, final Class<T> clazz) {
Preconditions.checkNotNull(resourceAsString);
T entity = null;
try {
entity = objectMapper.readValue(resourceAsString, clazz);
} catch (final IOException ioEx) {
logger.error("", ioEx);
}
return entity;
}
@SuppressWarnings("unchecked")
@Override
public final <T> List<T> decodeList(final String resourcesAsString, final Class<T> clazz) {
Preconditions.checkNotNull(resourcesAsString);
List<T> entities = null;
try {
if (clazz.equals(Foo.class)) {
entities = objectMapper.readValue(resourcesAsString, new TypeReference<List<Foo>>() {
// ...
});
} else {
entities = objectMapper.readValue(resourcesAsString, List.class);
}
} catch (final IOException ioEx) {
logger.error("", ioEx);
}
return entities;
}
@Override
public final String getMime() {
return MediaType.APPLICATION_JSON.toString();
}
}
@@ -0,0 +1,49 @@
package com.baeldung.test;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@Component
@Profile("test")
public class TestMarshallerFactory implements FactoryBean<IMarshaller> {
@Autowired
private Environment env;
public TestMarshallerFactory() {
super();
}
// API
@Override
public IMarshaller getObject() {
final String testMime = env.getProperty("test.mime");
if (testMime != null) {
switch (testMime) {
case "json":
return new JacksonMarshaller();
case "xml":
// If we need to implement xml marshaller we can include spring-rest-full XStreamMarshaller
throw new IllegalStateException();
default:
throw new IllegalStateException();
}
}
return new JacksonMarshaller();
}
@Override
public Class<IMarshaller> getObjectType() {
return IMarshaller.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
@@ -0,0 +1,36 @@
package com.baeldung.web;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
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;
import com.baeldung.common.web.AbstractBasicLiveTest;
import com.baeldung.persistence.model.Foo;
import com.baeldung.spring.ConfigIntegrationTest;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { ConfigIntegrationTest.class }, loader = AnnotationConfigContextLoader.class)
@ActiveProfiles("test")
public class FooLiveTest extends AbstractBasicLiveTest<Foo> {
public FooLiveTest() {
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)));
}
}
@@ -0,0 +1,80 @@
package com.baeldung.web;
import static com.baeldung.Consts.APPLICATION_PORT;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
import static org.apache.commons.lang3.RandomStringUtils.randomNumeric;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import java.util.List;
import org.junit.Test;
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;
import com.baeldung.common.web.AbstractBasicLiveTest;
import com.baeldung.persistence.model.Foo;
import com.baeldung.spring.ConfigIntegrationTest;
import io.restassured.RestAssured;
import io.restassured.response.Response;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { ConfigIntegrationTest.class }, loader = AnnotationConfigContextLoader.class)
@ActiveProfiles("test")
public class FooPageableLiveTest extends AbstractBasicLiveTest<Foo> {
public FooPageableLiveTest() {
super(Foo.class);
}
// API
@Override
public final void create() {
super.create(new Foo(randomAlphabetic(6)));
}
@Override
public final String createAsUri() {
return createAsUri(new Foo(randomAlphabetic(6)));
}
@Override
@Test
public void whenResourcesAreRetrievedPaged_then200IsReceived() {
this.create();
final Response response = RestAssured.get(getPageableURL() + "?page=0&size=10");
assertThat(response.getStatusCode(), is(200));
}
@Override
@Test
public void whenPageOfResourcesAreRetrievedOutOfBounds_then404IsReceived() {
final String url = getPageableURL() + "?page=" + randomNumeric(5) + "&size=10";
final Response response = RestAssured.get(url);
assertThat(response.getStatusCode(), is(404));
}
@Override
@Test
public void givenResourcesExist_whenFirstPageIsRetrieved_thenPageContainsResources() {
create();
final Response response = RestAssured.get(getPageableURL() + "?page=0&size=10");
assertFalse(response.body().as(List.class).isEmpty());
}
protected String getPageableURL() {
return "http://localhost:" + APPLICATION_PORT + "/spring-boot-rest/auth/foos/pageable";
}
}
@@ -0,0 +1,14 @@
package com.baeldung.web;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
// @formatter:off
FooLiveTest.class
,FooPageableLiveTest.class
}) //
public class LiveTestSuiteLiveTest {
}
@@ -6,6 +6,7 @@ import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isA;
import static org.hamcrest.Matchers.not;
import static com.baeldung.Consts.APPLICATION_PORT;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
@@ -16,8 +17,8 @@ import com.gargoylesoftware.htmlunit.html.HtmlPage;
public class ErrorHandlingLiveTest {
private static final String BASE_URL = "http://localhost:8080";
private static final String EXCEPTION_ENDPOINT = "/exception";
private static final String BASE_URL = "http://localhost:" + APPLICATION_PORT + "/spring-boot-rest";
private static final String EXCEPTION_ENDPOINT = BASE_URL + "/exception";
private static final String ERROR_RESPONSE_KEY_PATH = "error";
private static final String XML_RESPONSE_KEY_PATH = "xmlkey";
@@ -57,7 +58,7 @@ public class ErrorHandlingLiveTest {
try (WebClient webClient = new WebClient()) {
webClient.getOptions()
.setThrowExceptionOnFailingStatusCode(false);
HtmlPage page = webClient.getPage(BASE_URL + EXCEPTION_ENDPOINT);
HtmlPage page = webClient.getPage(EXCEPTION_ENDPOINT);
assertThat(page.getBody()
.asText()).contains("Whitelabel Error Page");
}
@@ -0,0 +1,36 @@
package com.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();
}
}