From cae7e3fb582d024f924943828b6721494610730f Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 13 Jan 2015 16:41:38 +0000 Subject: [PATCH] Add support for determining the link extractor from the content type Previously, when documenting links, it was necessary to provide the link extractor that should be used to get the links from the response. This commit improves on this by adding support for determining the link extractor to use based on the response's content type. Two content types are currently supported; application/hal+json and application/json. For other content types, an extractor can be provided as before when calling withLinks. Closes #7 --- .../com/example/notes/ApiDocumentation.java | 7 +++---- .../com/example/notes/ApiDocumentation.java | 7 +++---- .../.settings/org.eclipse.jdt.ui.prefs | 2 +- .../restdocs/core/LinkExtractors.java | 21 +++++++++++++++++-- .../core/RestDocumentationResultHandler.java | 4 ++++ .../core/RestDocumentationResultHandlers.java | 20 ++++++++++++++++-- 6 files changed, 48 insertions(+), 13 deletions(-) diff --git a/rest-notes-spring-data-rest/src/test/java/com/example/notes/ApiDocumentation.java b/rest-notes-spring-data-rest/src/test/java/com/example/notes/ApiDocumentation.java index f3f95d8e..c7ae0946 100644 --- a/rest-notes-spring-data-rest/src/test/java/com/example/notes/ApiDocumentation.java +++ b/rest-notes-spring-data-rest/src/test/java/com/example/notes/ApiDocumentation.java @@ -19,7 +19,6 @@ package com.example.notes; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.springframework.restdocs.core.RestDocumentation.document; -import static org.springframework.restdocs.core.LinkExtractors.halLinks; import static org.springframework.restdocs.core.RestDocumentation.linkWithRel; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; @@ -95,7 +94,7 @@ public class ApiDocumentation { public void indexExample() throws Exception { this.mockMvc.perform(get("/")) .andExpect(status().isOk()) - .andDo(document("index-example").withLinks(halLinks(), + .andDo(document("index-example").withLinks( linkWithRel("notes").description( "The <>"), linkWithRel("tags").description( @@ -175,7 +174,7 @@ public class ApiDocumentation { .andExpect(jsonPath("_links.self.href", is(noteLocation))) .andExpect(jsonPath("_links.tags", is(notNullValue()))) .andDo(document("note-get-example") - .withLinks(halLinks(), + .withLinks( linkWithRel("self").description("This <>"), linkWithRel("tags").description( "This note's <>"))); @@ -262,7 +261,7 @@ public class ApiDocumentation { this.mockMvc.perform(get(tagLocation)) .andExpect(status().isOk()) .andExpect(jsonPath("name", is(tag.get("name")))) - .andDo(document("tag-get-example").withLinks(halLinks(), + .andDo(document("tag-get-example").withLinks( linkWithRel("self").description("This <>"), linkWithRel("notes") .description( diff --git a/rest-notes-spring-hateoas/src/test/java/com/example/notes/ApiDocumentation.java b/rest-notes-spring-hateoas/src/test/java/com/example/notes/ApiDocumentation.java index da8ca724..7e201ed2 100644 --- a/rest-notes-spring-hateoas/src/test/java/com/example/notes/ApiDocumentation.java +++ b/rest-notes-spring-hateoas/src/test/java/com/example/notes/ApiDocumentation.java @@ -18,7 +18,6 @@ package com.example.notes; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -import static org.springframework.restdocs.core.LinkExtractors.halLinks; import static org.springframework.restdocs.core.RestDocumentation.document; import static org.springframework.restdocs.core.RestDocumentation.linkWithRel; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -95,7 +94,7 @@ public class ApiDocumentation { public void indexExample() throws Exception { this.mockMvc.perform(get("/")) .andExpect(status().isOk()) - .andDo(document("index-example").withLinks(halLinks(), + .andDo(document("index-example").withLinks( linkWithRel("notes").description( "The <>"), linkWithRel("tags").description( @@ -171,7 +170,7 @@ public class ApiDocumentation { .andExpect(jsonPath("body", is(note.get("body")))) .andExpect(jsonPath("_links.self.href", is(noteLocation))) .andExpect(jsonPath("_links.note-tags", is(notNullValue()))) - .andDo(document("note-get-example").withLinks(halLinks(), + .andDo(document("note-get-example").withLinks( linkWithRel("self").description("This <>"), linkWithRel("note-tags").description( "This note's <>"))); @@ -258,7 +257,7 @@ public class ApiDocumentation { this.mockMvc.perform(get(tagLocation)) .andExpect(status().isOk()) .andExpect(jsonPath("name", is(tag.get("name")))) - .andDo(document("tag-get-example").withLinks(halLinks(), + .andDo(document("tag-get-example").withLinks( linkWithRel("self").description("This <>"), linkWithRel("tagged-notes") .description( diff --git a/spring-restdocs-core/.settings/org.eclipse.jdt.ui.prefs b/spring-restdocs-core/.settings/org.eclipse.jdt.ui.prefs index ca171a90..dc84ae74 100644 --- a/spring-restdocs-core/.settings/org.eclipse.jdt.ui.prefs +++ b/spring-restdocs-core/.settings/org.eclipse.jdt.ui.prefs @@ -120,6 +120,6 @@ sp_cleanup.use_lambda=true sp_cleanup.use_parentheses_in_expressions=false sp_cleanup.use_this_for_non_static_field_access=true sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=false -sp_cleanup.use_this_for_non_static_method_access=true +sp_cleanup.use_this_for_non_static_method_access=false sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=false sp_cleanup.use_type_arguments=false diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/LinkExtractors.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/LinkExtractors.java index f736e07d..df35102e 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/LinkExtractors.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/LinkExtractors.java @@ -24,16 +24,16 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletResponse; import com.fasterxml.jackson.databind.ObjectMapper; /** - * Static factory methods provided a selection of {@link LinkExtractor link extractors} + * Static factory methods providing a selection of {@link LinkExtractor link extractors} * for use when documentating a hypermedia-based API. * * @author Andy Wilkinson - * */ public class LinkExtractors { @@ -58,6 +58,23 @@ public class LinkExtractors { return new AtomLinkExtractor(); } + /** + * Returns the {@code LinkExtractor} for the given {@code contentType} or {@code null} + * if there is no extractor for the content type. + * + * @param contentType The content type + * @return The extractor for the content type, or {@code null} + */ + public static LinkExtractor extractorForContentType(String contentType) { + if (MediaType.APPLICATION_JSON_VALUE.equals(contentType)) { + return atomLinks(); + } + else if ("application/hal+json".equals(contentType)) { + return halLinks(); + } + return null; + } + private static abstract class JsonContentLinkExtractor implements LinkExtractor { private final ObjectMapper objectMapper = new ObjectMapper(); diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationResultHandler.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationResultHandler.java index 71eca294..a5464bfd 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationResultHandler.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationResultHandler.java @@ -47,6 +47,10 @@ public class RestDocumentationResultHandler implements ResultHandler { } } + public RestDocumentationResultHandler withLinks(LinkDescriptor... descriptors) { + return withLinks(null, descriptors); + } + public RestDocumentationResultHandler withLinks(LinkExtractor linkExtractor, LinkDescriptor... descriptors) { this.linkDocumentingResultHandler = new LinkDocumentingResultHandler( diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationResultHandlers.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationResultHandlers.java index 256279d0..7b066cc2 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationResultHandlers.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationResultHandlers.java @@ -260,8 +260,24 @@ public abstract class RestDocumentationResultHandlers { @Override void handle(MvcResult result, DocumentationWriter writer) throws Exception { - Map> links = this.extractor.extractLinks(result - .getResponse()); + Map> links; + if (this.extractor != null) { + links = this.extractor.extractLinks(result.getResponse()); + } + else { + String contentType = result.getResponse().getContentType(); + LinkExtractor extractorForContentType = LinkExtractors + .extractorForContentType(contentType); + if (extractorForContentType != null) { + links = extractorForContentType.extractLinks(result.getResponse()); + } + else { + throw new IllegalStateException( + "No LinkExtractor has been provided and one is not available for the content type " + + contentType); + } + + } Set actualRels = links.keySet(); Set expectedRels = this.descriptorsByRel.keySet();