From 36c689e43e32ffbd5dbbfb0bf8f7e11431fe556b Mon Sep 17 00:00:00 2001 From: HPacquee Date: Thu, 28 Sep 2017 21:57:26 +0200 Subject: [PATCH] #255, #634 - Add key operations to Traverson --- .../hateoas/client/Traverson.java | 60 +++++++++++--- .../hateoas/client/Server.java | 29 +++++++ .../hateoas/client/TraversonTest.java | 83 +++++++++++++++++-- 3 files changed, 153 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/springframework/hateoas/client/Traverson.java b/src/main/java/org/springframework/hateoas/client/Traverson.java index 1db7c7048..679ebc286 100644 --- a/src/main/java/org/springframework/hateoas/client/Traverson.java +++ b/src/main/java/org/springframework/hateoas/client/Traverson.java @@ -36,10 +36,7 @@ import org.springframework.hateoas.client.Rels.Rel; import org.springframework.hateoas.hal.HalLinkDiscoverer; import org.springframework.hateoas.hal.Jackson2HalModule; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; +import org.springframework.http.*; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; @@ -61,6 +58,7 @@ * @author Dietrich Schulten * @author Greg Turnquist * @author Tom Bunting + * @author Haroun Pacquee * @since 0.11 */ public class Traverson { @@ -235,7 +233,7 @@ public TraversalBuilder follow(Hop hop) { return new TraversalBuilder().follow(hop); } - private HttpEntity prepareRequest(HttpHeaders headers) { + private HttpEntity prepareRequest(HttpHeaders headers, Object payload) { HttpHeaders toSend = new HttpHeaders(); toSend.putAll(headers); @@ -244,7 +242,11 @@ private HttpEntity prepareRequest(HttpHeaders headers) { toSend.setAccept(mediaTypes); } - return new HttpEntity(toSend); + if(payload == null) { + return new HttpEntity(toSend); + } else { + return new HttpEntity(payload, toSend); + } } /** @@ -257,6 +259,8 @@ public class TraversalBuilder { private List rels = new ArrayList(); private Map templateParameters = new HashMap(); private HttpHeaders headers = new HttpHeaders(); + private HttpMethod method = GET; + private Object payload; private TraversalBuilder() {} @@ -328,7 +332,7 @@ public TraversalBuilder withHeaders(HttpHeaders headers) { public T toObject(Class type) { Assert.notNull(type, "Target type must not be null!"); - return operations.exchange(traverseToExpandedFinalUrl(), GET, prepareRequest(headers), type).getBody(); + return operations.exchange(traverseToExpandedFinalUrl(), method, prepareRequest(headers, payload), type).getBody(); } /** @@ -341,7 +345,7 @@ public T toObject(Class type) { public T toObject(ParameterizedTypeReference type) { Assert.notNull(type, "Target type must not be null!"); - return operations.exchange(traverseToExpandedFinalUrl(), GET, prepareRequest(headers), type).getBody(); + return operations.exchange(traverseToExpandedFinalUrl(), method, prepareRequest(headers, payload), type).getBody(); } /** @@ -355,7 +359,7 @@ public T toObject(String jsonPath) { Assert.hasText(jsonPath, "JSON path must not be null or empty!"); - String forObject = operations.exchange(traverseToExpandedFinalUrl(), GET, prepareRequest(headers), String.class) + String forObject = operations.exchange(traverseToExpandedFinalUrl(), method, prepareRequest(headers, payload), String.class) .getBody(); return JsonPath.read(forObject, jsonPath); } @@ -369,7 +373,7 @@ public T toObject(String jsonPath) { public ResponseEntity toEntity(Class type) { Assert.notNull(type, "Target type must not be null!"); - return operations.exchange(traverseToExpandedFinalUrl(), GET, prepareRequest(headers), type); + return operations.exchange(traverseToExpandedFinalUrl(), method, prepareRequest(headers, payload), type); } /** @@ -419,7 +423,7 @@ private String getAndFindLinkWithRel(String uri, Iterator rels) { return uri; } - HttpEntity request = prepareRequest(headers); + HttpEntity request = prepareRequest(headers, payload); UriTemplate template = new UriTemplate(uri); ResponseEntity responseEntity = operations.exchange(template.expand(), GET, request, String.class); @@ -445,5 +449,39 @@ private String getAndFindLinkWithRel(String uri, Iterator rels) { return getAndFindLinkWithRel(link.expand(thisHop.getMergedParameters(templateParameters)).getHref(), rels); } } + + public TraversalBuilder post(T payload, MediaType contentType) { + addWriteContent(payload, contentType); + this.method = POST; + return this; + } + + public TraversalBuilder put(T payload, MediaType contentType) { + addWriteContent(payload, contentType); + this.method = PUT; + return this; + } + + private void addWriteContent(T payload, MediaType contentType) { + Assert.notNull(payload, "Payload must not be null!"); + Assert.notNull(contentType, "ContentType must not be null!"); + this.payload = payload; + this.headers.setContentType(contentType); + } + + public TraversalBuilder head() { + this.method = HEAD; + return this; + } + + public TraversalBuilder delete() { + this.method = DELETE; + return this; + } + + public TraversalBuilder get() { + this.method = GET; + return this; + } } } diff --git a/src/test/java/org/springframework/hateoas/client/Server.java b/src/test/java/org/springframework/hateoas/client/Server.java index 4a17fc482..0bb8c7442 100644 --- a/src/test/java/org/springframework/hateoas/client/Server.java +++ b/src/test/java/org/springframework/hateoas/client/Server.java @@ -46,6 +46,7 @@ * * @author Oliver Gierke * @author Greg Turnquist + * @author Haroun Pacquee */ public class Server implements Closeable { @@ -96,6 +97,34 @@ public Server() { withBody("{ \"_links\" : { \"self\" : { \"href\" : \"/{?template}\" }}}"). // withContentType(MediaTypes.HAL_JSON.toString()); + //For POST HttpMethod + + onRequest(). // + havingPathEqualTo("/225"). // + havingMethodEqualTo("POST"). // + respond(). // + withStatus(201). // + withHeader("location", "1"); + + + //For PUT HttpMethod + + onRequest(). // + havingPathEqualTo("/225"). // + havingMethodEqualTo("PUT"). // + respond(). // + withStatus(204). // + withHeader("location", "1"); + + //For DELETE HttpMethod + + onRequest(). // + havingPathEqualTo("/225"). // + havingMethodEqualTo("DELETE"). // + respond(). // + withStatus(202); + + // Sample traversal of HAL docs based on Spring-a-Gram showcase org.springframework.core.io.Resource springagramRoot = resourceLoader.getResource("classpath:springagram-root.json"); org.springframework.core.io.Resource springagramItems = resourceLoader.getResource("classpath:springagram-items.json"); diff --git a/src/test/java/org/springframework/hateoas/client/TraversonTest.java b/src/test/java/org/springframework/hateoas/client/TraversonTest.java index 8534646ed..5ee95fa17 100644 --- a/src/test/java/org/springframework/hateoas/client/TraversonTest.java +++ b/src/test/java/org/springframework/hateoas/client/TraversonTest.java @@ -22,11 +22,7 @@ import java.io.IOException; import java.net.URI; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import org.junit.After; import org.junit.Before; @@ -37,9 +33,7 @@ import org.springframework.hateoas.Resource; import org.springframework.hateoas.client.Traverson.TraversalBuilder; import org.springframework.hateoas.core.JsonPathLinkDiscoverer; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpRequest; -import org.springframework.http.MediaType; +import org.springframework.http.*; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; @@ -53,6 +47,7 @@ * * @author Oliver Gierke * @author Greg Turnquist + * @author Haroun Pacquee * @since 0.11 */ public class TraversonTest { @@ -410,4 +405,76 @@ public GitHubLinkDiscoverer() { super("$.%s_url", MediaType.APPLICATION_JSON); } } + + + /** + * @see #225 + */ + @Test + public void doesPost() { + + RestTemplate restTemplate = new RestTemplate(); + + List> messageConverters = new ArrayList>(); + messageConverters.add(new MappingJackson2HttpMessageConverter()); + restTemplate.setMessageConverters(messageConverters); + + traverson = new Traverson(URI.create(server.rootResource() + "/225"), MediaTypes.HAL_JSON); + traverson.setRestOperations(restTemplate); + + final Item item = new Item("img", "desc"); + ResponseEntity voidResponseEntity = traverson.follow() + .post(item, MediaType.APPLICATION_JSON) + .toEntity(Void.class); + + assertThat(voidResponseEntity.getStatusCode(), is(HttpStatus.CREATED)); + assertThat(voidResponseEntity.getHeaders().get("location"), contains("1")); + } + + /** + * @see #225 + */ + @Test + public void doesPatch() { + + RestTemplate restTemplate = new RestTemplate(); + + List> messageConverters = new ArrayList>(); + messageConverters.add(new MappingJackson2HttpMessageConverter()); + restTemplate.setMessageConverters(messageConverters); + + traverson = new Traverson(URI.create(server.rootResource() + "/225"), MediaTypes.HAL_JSON); + traverson.setRestOperations(restTemplate); + + ResponseEntity voidResponseEntity = traverson.follow() + .delete() + .toEntity(Void.class); + + assertThat(voidResponseEntity.getStatusCode(), is(HttpStatus.ACCEPTED)); + } + + /** + * @see #225 + */ + @Test + public void doesPut() { + + RestTemplate restTemplate = new RestTemplate(); + + List> messageConverters = new ArrayList>(); + messageConverters.add(new MappingJackson2HttpMessageConverter()); + restTemplate.setMessageConverters(messageConverters); + + traverson = new Traverson(URI.create(server.rootResource() + "/225"), MediaTypes.HAL_JSON); + traverson.setRestOperations(restTemplate); + + final Item item = new Item("img", "desc"); + ResponseEntity voidResponseEntity = traverson.follow() + .put(item, MediaType.APPLICATION_JSON) + .toEntity(Void.class); + + assertThat(voidResponseEntity.getStatusCode(), is(HttpStatus.NO_CONTENT)); + assertThat(voidResponseEntity.getHeaders().get("location"), contains("1")); + } + }