JAVA-8363: Move etag code to spring-boot-mvc-3

This commit is contained in:
Krzysiek
2021-11-25 13:10:58 +01:00
parent 6919bda856
commit fab1638eeb
13 changed files with 145 additions and 22 deletions

View File

@@ -1,58 +0,0 @@
package com.baeldung.etag;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
@RestController
@RequestMapping(value = "/foos")
public class FooController {
@Autowired
private FooDao fooDao;
@GetMapping(value = "/{id}")
public Foo findById(@PathVariable("id") final Long id, final HttpServletResponse response) {
return fooDao.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
}
// Note: the global filter overrides the ETag value we set here. We can still
// analyze its behaviour in the Integration Test.
@GetMapping(value = "/{id}/custom-etag")
public ResponseEntity<Foo> findByIdWithCustomEtag(@PathVariable("id") final Long id,
final HttpServletResponse response) {
final Foo foo = fooDao.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
return ResponseEntity.ok().eTag(Long.toString(foo.getVersion())).body(foo);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Foo create(@RequestBody final Foo resource, final HttpServletResponse response) {
return fooDao.save(resource);
}
@PutMapping(value = "/{id}")
@ResponseStatus(HttpStatus.OK)
public void update(@PathVariable("id") final Long id, @RequestBody final Foo resource) {
fooDao.save(resource);
}
@DeleteMapping(value = "/{id}")
@ResponseStatus(HttpStatus.OK)
public void delete(@PathVariable("id") final Long id) {
fooDao.deleteById(id);
}
}

View File

@@ -1,8 +0,0 @@
package com.baeldung.etag;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface FooDao extends CrudRepository<Foo, Long>{
}

View File

@@ -1,13 +0,0 @@
package com.baeldung.etag;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootEtagApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootEtagApplication.class, args);
}
}

View File

@@ -1,6 +1,4 @@
package com.baeldung.etag;
import java.io.Serializable;
package com.baeldung.mime;
import javax.persistence.Column;
import javax.persistence.Entity;
@@ -8,6 +6,7 @@ import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Version;
import java.io.Serializable;
@Entity
public class Foo implements Serializable {
@@ -18,7 +17,7 @@ public class Foo implements Serializable {
@Column(nullable = false)
private String name;
@Version
private long version;
@@ -57,7 +56,7 @@ public class Foo implements Serializable {
public void setVersion(long version) {
this.version = version;
}
//
@Override
@@ -91,5 +90,4 @@ public class Foo implements Serializable {
builder.append("Foo [name=").append(name).append("]");
return builder.toString();
}
}

View File

@@ -1,4 +1,4 @@
package com.baeldung.etag;
package com.baeldung.mime;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;

View File

@@ -1,18 +0,0 @@
<!-- NOTE: web.xml is not used in Spring Boot. This is just for guidance, showing how an Etag Filter would be implemented using XML-based configs -->
<!-- <?xml version="1.0" encoding="UTF-8"?> -->
<!-- <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" -->
<!-- xsi:schemaLocation=" -->
<!-- http://java.sun.com/xml/ns/javaee -->
<!-- http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0" -->
<!-- > -->
<!-- <filter> -->
<!-- <filter-name>etagFilter</filter-name> -->
<!-- <filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</filter-class> -->
<!-- </filter> -->
<!-- <filter-mapping> -->
<!-- <filter-name>etagFilter</filter-name> -->
<!-- <url-pattern>/*</url-pattern> -->
<!-- </filter-mapping> -->
<!-- </web-app> -->

View File

@@ -1,123 +0,0 @@
package com.baeldung.etag;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.assertj.core.util.Preconditions;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.response.Response;
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@ComponentScan(basePackageClasses = WebConfig.class)
@EnableAutoConfiguration
public class EtagIntegrationTest {
@LocalServerPort
private int port;
@Test
public void givenResourceExists_whenRetrievingResource_thenEtagIsAlsoReturned() {
// Given
final String uriOfResource = createAsUri();
// When
final Response findOneResponse = RestAssured.given().header("Accept", "application/json").get(uriOfResource);
// Then
assertNotNull(findOneResponse.getHeader(HttpHeaders.ETAG));
}
@Test
public void givenResourceWasRetrieved_whenRetrievingAgainWithEtag_thenNotModifiedReturned() {
// Given
final String uriOfResource = createAsUri();
final Response findOneResponse = RestAssured.given().header("Accept", "application/json").get(uriOfResource);
final String etagValue = findOneResponse.getHeader(HttpHeaders.ETAG);
// When
final Response secondFindOneResponse = RestAssured.given().header("Accept", "application/json")
.headers("If-None-Match", etagValue).get(uriOfResource);
// Then
assertTrue(secondFindOneResponse.getStatusCode() == 304);
}
@Test
public void givenResourceWasRetrievedThenModified_whenRetrievingAgainWithEtag_thenResourceIsReturned() {
// Given
final String uriOfResource = createAsUri();
final Response firstFindOneResponse = RestAssured.given().header("Accept", "application/json")
.get(uriOfResource);
final String etagValue = firstFindOneResponse.getHeader(HttpHeaders.ETAG);
final long createdId = firstFindOneResponse.jsonPath().getLong("id");
Foo updatedFoo = new Foo("updated value");
updatedFoo.setId(createdId);
Response updatedResponse = RestAssured.given().contentType(ContentType.JSON).body(updatedFoo)
.put(uriOfResource);
assertThat(updatedResponse.getStatusCode() == 200);
// When
final Response secondFindOneResponse = RestAssured.given().header("Accept", "application/json")
.headers("If-None-Match", etagValue).get(uriOfResource);
// Then
assertTrue(secondFindOneResponse.getStatusCode() == 200);
}
@Test
@Ignore("Not Yet Implemented By Spring - https://jira.springsource.org/browse/SPR-10164")
public void givenResourceExists_whenRetrievedWithIfMatchIncorrectEtag_then412IsReceived() {
// Given
final String uriOfResource = createAsUri();
// When
final Response findOneResponse = RestAssured.given().header("Accept", "application/json")
.headers("If-Match", randomAlphabetic(8)).get(uriOfResource);
// Then
assertTrue(findOneResponse.getStatusCode() == 412);
}
private final String createAsUri() {
final Response response = createAsResponse(new Foo(randomAlphabetic(6)));
Preconditions.checkState(response.getStatusCode() == 201, "create operation: " + response.getStatusCode());
return getURL() + "/" + response.getBody().as(Foo.class).getId();
}
private Response createAsResponse(final Foo resource) {
String resourceAsString;
try {
resourceAsString = new ObjectMapper().writeValueAsString(resource);
} catch (JsonProcessingException e) {
throw new AssertionError("Error during serialization");
}
return RestAssured.given().contentType(MediaType.APPLICATION_JSON.toString()).body(resourceAsString)
.post(getURL());
}
private String getURL() {
return "http://localhost:" + port + "/foos";
}
}

View File

@@ -14,15 +14,12 @@ import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.baeldung.etag.Foo;
import com.baeldung.etag.WebConfig;
import io.restassured.RestAssured;
import io.restassured.response.Response;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes= WebConfig.class, webEnvironment = WebEnvironment.RANDOM_PORT)
@ComponentScan({"com.baeldung.mime", "com.baeldung.etag"})
@SpringBootTest(classes = WebConfig.class, webEnvironment = WebEnvironment.RANDOM_PORT)
@ComponentScan({"com.baeldung.mime"})
@EnableAutoConfiguration
@ActiveProfiles("test")
public class FooLiveTest {

View File

@@ -7,7 +7,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import com.baeldung.etag.Foo;
import com.baeldung.mime.Foo;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

View File

@@ -4,7 +4,7 @@ import java.util.List;
import org.springframework.http.MediaType;
import com.baeldung.etag.Foo;
import com.baeldung.mime.Foo;
import com.thoughtworks.xstream.XStream;
public final class XStreamMarshaller implements IMarshaller {