diff --git a/build.gradle b/build.gradle index 8e35513f..0bf08fc8 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ allprojects { ext { - springVersion = '4.0.7.RELEASE' + springVersion = '4.1.1.RELEASE' } group = 'org.springframework.restdocs' } diff --git a/rest-notes-spring-data-rest/build.gradle b/rest-notes-spring-data-rest/build.gradle index 80765b54..95b693ae 100644 --- a/rest-notes-spring-data-rest/build.gradle +++ b/rest-notes-spring-data-rest/build.gradle @@ -1,11 +1,13 @@ buildscript { repositories { mavenLocal() - jcenter() + maven { + url 'https://repo.spring.io/libs-milestone' + } } dependencies { classpath 'org.springframework.restdocs:spring-restdocs-gradle-plugin:0.1.0.BUILD-SNAPSHOT' - classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.1.7.RELEASE' + classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.2.0.M2' } } @@ -16,7 +18,9 @@ apply plugin: 'org.springframework.restdocs' repositories { mavenLocal() - jcenter() + maven { + url 'https://repo.spring.io/libs-milestone' + } } group = 'com.example' diff --git a/rest-notes-spring-data-rest/src/documentation/asciidoc/main.asciidoc b/rest-notes-spring-data-rest/src/documentation/asciidoc/main.asciidoc index 63d390f2..8cb4fcc3 100644 --- a/rest-notes-spring-data-rest/src/documentation/asciidoc/main.asciidoc +++ b/rest-notes-spring-data-rest/src/documentation/asciidoc/main.asciidoc @@ -42,12 +42,12 @@ $ java -jar build/libs/*.jar You can check that the service is up and running by executing a simple request using cURL: -include::{generated}/index/access_with_curl_request.asciidoc[] +include::{generated}/GettingStartedDocumentation/indexRequest.asciidoc[] This request should yield the following response in the http://stateless.co/hal_specification.html[Hypertext Application Language (HAL)] format: -include::{generated}/index/access_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/indexResponse.asciidoc[] Note the `_links` in the JSON response. They are key to navigating the API. @@ -59,26 +59,26 @@ Now that you've started the service and verified that it works, the next step is it to create a new note. As you saw above, the URI for working with notes is included as a link when you perform a `GET` request against the root of the service: -include::{generated}/index/access_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/indexResponse.asciidoc[] To create a note, you need to execute a `POST` request to this URI including a JSON payload containing the title and body of the note: -include::{generated}/notes/create_with_curl_request.asciidoc[] +include::{generated}/GettingStartedDocumentation/createNoteRequest.asciidoc[] The response from this request should have a status code of `201 Created` and contain a `Location` header whose value is the URI of the newly created note: -include::{generated}/notes/create_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/createNoteResponse.asciidoc[] To work with the newly created note you use the URI in the `Location` header. For example, you can access the note's details by performing a `GET` request: -include::{generated}/notes/get_with_curl_request.asciidoc[] +include::{generated}/GettingStartedDocumentation/getNoteRequest.asciidoc[] This request will produce a response with the note's details in its body: -include::{generated}/notes/get_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/getNoteResponse.asciidoc[] Note the `tags` link which we'll make use of later. @@ -92,26 +92,26 @@ to tag a note, you must first create the tag. Referring back to the response for the service's index, the URI for working with tags is include as a link: -include::{generated}/index/access_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/indexResponse.asciidoc[] To create a tag you need to execute a `POST` request to this URI, including a JSON payload containing the name of the tag: -include::{generated}/tags/create_with_curl_request.asciidoc[] +include::{generated}/GettingStartedDocumentation/createTagRequest.asciidoc[] The response from this request should have a status code of `201 Created` and contain a `Location` header whose value is the URI of the newly created tag: -include::{generated}/tags/create_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/createTagResponse.asciidoc[] To work with the newly created tag you use the URI in the `Location` header. For example you can access the tag's details by performing a `GET` request: -include::{generated}/tags/get_with_curl_request.asciidoc[] +include::{generated}/GettingStartedDocumentation/getTagRequest.asciidoc[] This request will produce a response with the tag's details in its body: -include::{generated}/tags/get_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/getTagResponse.asciidoc[] @@ -132,24 +132,24 @@ with it. Once again we execute a `POST` request. However, this time, in an array named tags, we include the URI of the tag we just created: -include::{generated}/notes/create_tagged_with_curl_request.asciidoc[] +include::{generated}/GettingStartedDocumentation/createTaggedNoteRequest.asciidoc[] Once again, the response's `Location` header tells us the URI of the newly created note: -include::{generated}/notes/create_tagged_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/createTaggedNoteResponse.asciidoc[] As before, a `GET` request executed against this URI will retrieve the note's details: -include::{generated}/notes/get_tagged_with_curl.asciidoc[] +include::{generated}/GettingStartedDocumentation/getTaggedNoteRequestResponse.asciidoc[] To verify that the tag has been associated with the note, we can perform a `GET` request against the URI from the `tags` link: -include::{generated}/notes/get_tags_for_tagged_with_curl_request.asciidoc[] +include::{generated}/GettingStartedDocumentation/getTagsRequest.asciidoc[] The response embeds information about the tag that we've just associated with the note: -include::{generated}/notes/get_tags_for_tagged_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/getTagsResponse.asciidoc[] @@ -159,17 +159,17 @@ An existing note can be tagged by executing a `PATCH` request against the note's a body that contains the array of tags to be associated with the note. We'll used the URI of the untagged note that we created earlier: -include::{generated}/notes/tag_existing_with_curl_request.asciidoc[] +include::{generated}/GettingStartedDocumentation/tagExistingNoteRequest.asciidoc[] This request should produce a `204 No Content` response: -include::{generated}/notes/tag_existing_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/tagExistingNoteResponse.asciidoc[] When we first created this note, we noted the tags link included in its details: -include::{generated}/notes/get_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/createNoteResponse.asciidoc[] We can use that link now and execute a `GET` request to see that the note now has a single tag: -include::{generated}/notes/get_tags_for_existing_with_curl.asciidoc[] +include::{generated}/GettingStartedDocumentation/getTagsForExistingNoteRequestResponse.asciidoc[] diff --git a/rest-notes-spring-data-rest/src/documentation/java/com/example/notes/GettingStartedDocumentation.java b/rest-notes-spring-data-rest/src/documentation/java/com/example/notes/GettingStartedDocumentation.java index a69b8966..079313d8 100644 --- a/rest-notes-spring-data-rest/src/documentation/java/com/example/notes/GettingStartedDocumentation.java +++ b/rest-notes-spring-data-rest/src/documentation/java/com/example/notes/GettingStartedDocumentation.java @@ -19,14 +19,9 @@ package com.example.notes; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -import static org.springframework.restdocs.core.RestDocumentationRequestPostProcessors.port; -import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlRequest; -import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlRequestAndResponse; -import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlResponse; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -42,7 +37,8 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.hateoas.MediaTypes; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.restdocs.core.RestDocumentationConfiguration; +import org.springframework.restdocs.core.RestDocumentationJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -53,7 +49,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.jayway.jsonpath.JsonPath; -@RunWith(SpringJUnit4ClassRunner.class) +@RunWith(RestDocumentationJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = Application.class) @WebAppConfiguration public class GettingStartedDocumentation { @@ -68,17 +64,16 @@ public class GettingStartedDocumentation { @Before public void setUp() { - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build(); + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) + .apply(new RestDocumentationConfiguration()).build(); } @Test public void index() throws Exception { - this.mockMvc.perform(get("/").with(port(8080)).accept(MediaTypes.HAL_JSON)) + this.mockMvc.perform(get("/").accept(MediaTypes.HAL_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("_links.notes", is(notNullValue()))) - .andExpect(jsonPath("_links.tags", is(notNullValue()))) - .andDo(documentCurlRequest("index/access_with_curl_request.asciidoc")) - .andDo(documentCurlResponse("index/access_with_curl_response.asciidoc")); + .andExpect(jsonPath("_links.tags", is(notNullValue()))); } @Test @@ -98,63 +93,49 @@ public class GettingStartedDocumentation { getTagsForExistingNote(noteLocation); } - private String createNote() throws Exception { + String createNote() throws Exception { Map note = new HashMap(); note.put("title", "Note creation with cURL"); note.put("body", "An example of how to create a note using cURL"); String noteLocation = this.mockMvc .perform( - post("/notes").with(port(8080)).contentType(MediaTypes.HAL_JSON) - .content(objectMapper.writeValueAsString(note))) + post("/notes").contentType(MediaTypes.HAL_JSON).content( + objectMapper.writeValueAsString(note))) .andExpect(status().isCreated()) - .andExpect(header().string("Location", notNullValue())) - .andDo(documentCurlRequest("notes/create_with_curl_request.asciidoc") - .includeResponseHeaders()) - .andDo(documentCurlResponse("notes/create_with_curl_response.asciidoc") - .includeResponseHeaders()).andReturn().getResponse() - .getHeader("Location"); + .andExpect(header().string("Location", notNullValue())).andReturn() + .getResponse().getHeader("Location"); return noteLocation; } - private void getNote(String noteLocation) throws Exception { - this.mockMvc.perform(get(noteLocation).with(port(8080))) - .andExpect(status().isOk()) + void getNote(String noteLocation) throws Exception { + this.mockMvc.perform(get(noteLocation)).andExpect(status().isOk()) .andExpect(jsonPath("title", is(notNullValue()))) .andExpect(jsonPath("body", is(notNullValue()))) - .andExpect(jsonPath("_links.tags", is(notNullValue()))) - .andDo(documentCurlRequest("notes/get_with_curl_request.asciidoc")) - .andDo(documentCurlResponse("notes/get_with_curl_response.asciidoc")); + .andExpect(jsonPath("_links.tags", is(notNullValue()))); } - private String createTag() throws Exception, JsonProcessingException { + String createTag() throws Exception, JsonProcessingException { Map tag = new HashMap(); tag.put("name", "getting-started"); String tagLocation = this.mockMvc .perform( - post("/tags").with(port(8080)).contentType(MediaTypes.HAL_JSON) - .content(objectMapper.writeValueAsString(tag))) + post("/tags").contentType(MediaTypes.HAL_JSON).content( + objectMapper.writeValueAsString(tag))) .andExpect(status().isCreated()) - .andExpect(header().string("Location", notNullValue())) - .andDo(documentCurlRequest("tags/create_with_curl_request.asciidoc") - .includeResponseHeaders()) - .andDo(documentCurlResponse("tags/create_with_curl_response.asciidoc") - .includeResponseHeaders()).andReturn().getResponse() - .getHeader("Location"); + .andExpect(header().string("Location", notNullValue())).andReturn() + .getResponse().getHeader("Location"); return tagLocation; } - private void getTag(String tagLocation) throws Exception { - this.mockMvc.perform(get(tagLocation).with(port(8080))) - .andExpect(status().isOk()) + void getTag(String tagLocation) throws Exception { + this.mockMvc.perform(get(tagLocation)).andExpect(status().isOk()) .andExpect(jsonPath("name", is(notNullValue()))) - .andExpect(jsonPath("_links.notes", is(notNullValue()))) - .andDo(documentCurlRequest("tags/get_with_curl_request.asciidoc")) - .andDo(documentCurlResponse("tags/get_with_curl_response.asciidoc")); + .andExpect(jsonPath("_links.notes", is(notNullValue()))); } - private String createTaggedNote(String tag) throws Exception { + String createTaggedNote(String tag) throws Exception { Map note = new HashMap(); note.put("title", "Tagged note creation with cURL"); note.put("body", "An example of how to create a tagged note using cURL"); @@ -162,77 +143,48 @@ public class GettingStartedDocumentation { String noteLocation = this.mockMvc .perform( - post("/notes").with(port(8080)).contentType(MediaTypes.HAL_JSON) - .content(objectMapper.writeValueAsString(note))) + post("/notes").contentType(MediaTypes.HAL_JSON).content( + objectMapper.writeValueAsString(note))) .andExpect(status().isCreated()) - .andExpect(header().string("Location", notNullValue())) - .andDo(documentCurlRequest( - "notes/create_tagged_with_curl_request.asciidoc") - .includeResponseHeaders()) - .andDo(documentCurlResponse( - "notes/create_tagged_with_curl_response.asciidoc") - .includeResponseHeaders()).andReturn().getResponse() - .getHeader("Location"); + .andExpect(header().string("Location", notNullValue())).andReturn() + .getResponse().getHeader("Location"); return noteLocation; } - private void getTaggedNote(String tagLocation) throws Exception { - this.mockMvc - .perform(get(tagLocation).with(port(8080))) - .andExpect(status().isOk()) + void getTaggedNote(String tagLocation) throws Exception { + this.mockMvc.perform(get(tagLocation)).andExpect(status().isOk()) .andExpect(jsonPath("title", is(notNullValue()))) .andExpect(jsonPath("body", is(notNullValue()))) - .andExpect(jsonPath("_links.tags", is(notNullValue()))) - .andDo(documentCurlRequestAndResponse("notes/get_tagged_with_curl.asciidoc")); + .andExpect(jsonPath("_links.tags", is(notNullValue()))); } - private void getTags(String taggedNoteLocation) throws Exception { + void getTags(String taggedNoteLocation) throws Exception { String tagsLocation = getLink(this.mockMvc.perform(get(taggedNoteLocation)) .andReturn(), "tags"); - this.mockMvc - .perform(get(tagsLocation).with(port(8080))) - .andExpect(status().isOk()) - .andExpect(jsonPath("_embedded.tags", hasSize(1))) - .andDo(documentCurlRequest("notes/get_tags_for_tagged_with_curl_request.asciidoc")) - .andDo(documentCurlResponse("notes/get_tags_for_tagged_with_curl_response.asciidoc")); + this.mockMvc.perform(get(tagsLocation)).andExpect(status().isOk()) + .andExpect(jsonPath("_embedded.tags", hasSize(1))); } - private void tagExistingNote(String noteLocation, String tagLocation) - throws Exception { + void tagExistingNote(String noteLocation, String tagLocation) throws Exception { Map update = new HashMap(); update.put("tags", Arrays.asList(tagLocation)); - this.mockMvc - .perform( - patch(noteLocation).with(port(8080)) - .contentType(MediaTypes.HAL_JSON) - .content(objectMapper.writeValueAsString(update))) - .andExpect(status().isNoContent()) - .andDo(documentCurlRequest( - "notes/tag_existing_with_curl_request.asciidoc") - .includeResponseHeaders()) - .andDo(documentCurlResponse( - "notes/tag_existing_with_curl_response.asciidoc") - .includeResponseHeaders()); + this.mockMvc.perform( + patch(noteLocation).contentType(MediaTypes.HAL_JSON).content( + objectMapper.writeValueAsString(update))).andExpect( + status().isNoContent()); } - private void getTaggedExistingNote(String tagLocation) throws Exception { - this.mockMvc - .perform(get(tagLocation).with(port(8080))) - .andExpect(status().isOk()) - .andDo(documentCurlRequestAndResponse("notes/get_tagged_existing_with_curl.asciidoc")); + void getTaggedExistingNote(String tagLocation) throws Exception { + this.mockMvc.perform(get(tagLocation)).andExpect(status().isOk()); } - private void getTagsForExistingNote(String taggedNoteLocation) throws Exception { + void getTagsForExistingNote(String taggedNoteLocation) throws Exception { String tagsLocation = getLink(this.mockMvc.perform(get(taggedNoteLocation)) .andReturn(), "tags"); - this.mockMvc - .perform(get(tagsLocation).with(port(8080))) - .andExpect(status().isOk()) - .andExpect(jsonPath("_embedded.tags", hasSize(1))) - .andDo(print()) - .andDo(documentCurlRequestAndResponse("notes/get_tags_for_existing_with_curl.asciidoc")); + this.mockMvc.perform(get(tagsLocation)).andExpect(status().isOk()) + .andExpect(jsonPath("_embedded.tags", hasSize(1))); } private String getLink(MvcResult result, String href) diff --git a/rest-notes-spring-hateoas/build.gradle b/rest-notes-spring-hateoas/build.gradle index 70f0e028..eb992a86 100644 --- a/rest-notes-spring-hateoas/build.gradle +++ b/rest-notes-spring-hateoas/build.gradle @@ -1,11 +1,13 @@ buildscript { repositories { mavenLocal() - jcenter() + maven { + url "https://repo.spring.io/libs-milestone" + } } dependencies { classpath 'org.springframework.restdocs:spring-restdocs-gradle-plugin:0.1.0.BUILD-SNAPSHOT' - classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.1.7.RELEASE' + classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.2.0.M2' } } @@ -16,7 +18,9 @@ apply plugin: 'org.springframework.restdocs' repositories { mavenLocal() - jcenter() + maven { + url "https://repo.spring.io/libs-milestone" + } } group = 'com.example' diff --git a/rest-notes-spring-hateoas/src/documentation/asciidoc/main.asciidoc b/rest-notes-spring-hateoas/src/documentation/asciidoc/main.asciidoc index 67788b7c..1f0be40d 100644 --- a/rest-notes-spring-hateoas/src/documentation/asciidoc/main.asciidoc +++ b/rest-notes-spring-hateoas/src/documentation/asciidoc/main.asciidoc @@ -42,11 +42,11 @@ $ java -jar build/libs/*.jar You can check that the service is up and running by executing a simple request using cURL: -include::{generated}/index/access_with_curl_request.asciidoc[] +include::{generated}/GettingStartedDocumentation/indexRequest.asciidoc[] This request should yield the following response: -include::{generated}/index/access_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/indexResponse.asciidoc[] Note the `_links` in the JSON response. They are key to navigating the API. @@ -58,26 +58,26 @@ Now that you've started the service and verified that it works, the next step is it to create a new note. As you saw above, the URI for working with notes is included as a link when you perform a `GET` request against the root of the service: -include::{generated}/index/access_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/indexResponse.asciidoc[] To create a note you need to execute a `POST` request to this URI, including a JSON payload containing the title and body of the note: -include::{generated}/notes/create_with_curl_request.asciidoc[] +include::{generated}/GettingStartedDocumentation/createNoteRequest.asciidoc[] The response from this request should have a status code of `201 Created` and contain a `Location` header whose value is the URI of the newly created note: -include::{generated}/notes/create_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/createNoteResponse.asciidoc[] To work with the newly created note you use the URI in the `Location` header. For example you can access the note's details by performing a `GET` request: -include::{generated}/notes/get_with_curl_request.asciidoc[] +include::{generated}/GettingStartedDocumentation/getNoteRequest.asciidoc[] This request will produce a response with the note's details in its body: -include::{generated}/notes/get_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/getNoteResponse.asciidoc[] Note the `tags` link which we'll make use of later. @@ -91,26 +91,26 @@ to tag a note, you must first create the tag. Referring back to the response for the service's index, the URI for working with tags is include as a link: -include::{generated}/index/access_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/indexResponse.asciidoc[] To create a tag you need to execute a `POST` request to this URI, including a JSON payload containing the name of the tag: -include::{generated}/tags/create_with_curl_request.asciidoc[] +include::{generated}/GettingStartedDocumentation/createTagRequest.asciidoc[] The response from this request should have a status code of `201 Created` and contain a `Location` header whose value is the URI of the newly created tag: -include::{generated}/tags/create_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/createTagResponse.asciidoc[] To work with the newly created tag you use the URI in the `Location` header. For example you can access the tag's details by performing a `GET` request: -include::{generated}/tags/get_with_curl_request.asciidoc[] +include::{generated}/GettingStartedDocumentation/getTagRequest.asciidoc[] This request will produce a response with the tag's details in its body: -include::{generated}/tags/get_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/getTagResponse.asciidoc[] @@ -131,23 +131,23 @@ with it. Once again we execute a `POST` request, but this time, in an array named tags, we include the URI of the tag we just created: -include::{generated}/notes/create_tagged_with_curl_request.asciidoc[] +include::{generated}/GettingStartedDocumentation/createTaggedNoteRequest.asciidoc[] Once again, the response's `Location` header tells use the URI of the newly created note: -include::{generated}/notes/create_tagged_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/createTaggedNoteResponse.asciidoc[] As before, a `GET` request executed against this URI will retrieve the note's details: -include::{generated}/notes/get_tagged_with_curl.asciidoc[] +include::{generated}/GettingStartedDocumentation/getTaggedNoteRequestResponse.asciidoc[] To see the note's tags, execute a `GET` request against the URI of the note's tags link: -include::{generated}/notes/get_tags_for_tagged_with_curl_request.asciidoc[] +include::{generated}/GettingStartedDocumentation/getTagsRequest.asciidoc[] The response shows that, as expected, the note has a single tag: -include::{generated}/notes/get_tags_for_tagged_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/getTagsResponse.asciidoc[] @@ -157,17 +157,17 @@ An existing note can be tagged by executing a `PATCH` request against the note's a body that contains the array of tags to be associated with the note. We'll used the URI of the untagged note that we created earlier: -include::{generated}/notes/tag_existing_with_curl_request.asciidoc[] +include::{generated}/GettingStartedDocumentation/tagExistingNoteRequest.asciidoc[] This request should produce a `200 OK` response: -include::{generated}/notes/tag_existing_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/tagExistingNoteResponse.asciidoc[] When we first created this note, we noted the tags link included in its details: -include::{generated}/notes/get_with_curl_response.asciidoc[] +include::{generated}/GettingStartedDocumentation/createNoteResponse.asciidoc[] We can use that link now and execute a `GET` request to see that the note now has a single tag: -include::{generated}/notes/get_tags_for_existing_with_curl.asciidoc[] +include::{generated}/GettingStartedDocumentation/getTagsForExistingNoteRequestResponse.asciidoc[] diff --git a/rest-notes-spring-hateoas/src/documentation/java/com/example/notes/GettingStartedDocumentation.java b/rest-notes-spring-hateoas/src/documentation/java/com/example/notes/GettingStartedDocumentation.java index 8b533bb4..4c2424a6 100644 --- a/rest-notes-spring-hateoas/src/documentation/java/com/example/notes/GettingStartedDocumentation.java +++ b/rest-notes-spring-hateoas/src/documentation/java/com/example/notes/GettingStartedDocumentation.java @@ -18,10 +18,6 @@ package com.example.notes; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -import static org.springframework.restdocs.core.RestDocumentationRequestPostProcessors.port; -import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlRequest; -import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlRequestAndResponse; -import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlResponse; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -40,8 +36,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.hateoas.MediaTypes; import org.springframework.http.MediaType; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.restdocs.core.RestDocumentationConfiguration; +import org.springframework.restdocs.core.RestDocumentationJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -52,7 +50,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.jayway.jsonpath.JsonPath; -@RunWith(SpringJUnit4ClassRunner.class) +@RunWith(RestDocumentationJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = Application.class) @WebAppConfiguration public class GettingStartedDocumentation { @@ -67,18 +65,16 @@ public class GettingStartedDocumentation { @Before public void setUp() { - this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build(); + this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) + .apply(new RestDocumentationConfiguration()).build(); } @Test public void index() throws Exception { - this.mockMvc - .perform(get("/").with(port(8080)).accept(MediaType.APPLICATION_JSON)) - .andExpect(status().isOk()).andDo(print()) + this.mockMvc.perform(get("/").accept(MediaTypes.HAL_JSON)) + .andExpect(status().isOk()) .andExpect(jsonPath("links[?(@.rel==notes)]", is(notNullValue()))) - .andExpect(jsonPath("links[?(@.rel==tags)]", is(notNullValue()))) - .andDo(documentCurlRequest("index/access_with_curl_request.asciidoc")) - .andDo(documentCurlResponse("index/access_with_curl_response.asciidoc")); + .andExpect(jsonPath("links[?(@.rel==tags)]", is(notNullValue()))); } @Test @@ -98,65 +94,49 @@ public class GettingStartedDocumentation { getTagsForExistingNote(noteLocation); } - private String createNote() throws Exception { + String createNote() throws Exception { Map note = new HashMap(); note.put("title", "Note creation with cURL"); note.put("body", "An example of how to create a note using cURL"); String noteLocation = this.mockMvc .perform( - post("/notes").with(port(8080)) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(note))) + post("/notes").contentType(MediaTypes.HAL_JSON).content( + objectMapper.writeValueAsString(note))) .andExpect(status().isCreated()) - .andExpect(header().string("Location", notNullValue())) - .andDo(documentCurlRequest("notes/create_with_curl_request.asciidoc") - .includeResponseHeaders()) - .andDo(documentCurlResponse("notes/create_with_curl_response.asciidoc") - .includeResponseHeaders()).andReturn().getResponse() - .getHeader("Location"); + .andExpect(header().string("Location", notNullValue())).andReturn() + .getResponse().getHeader("Location"); return noteLocation; } - private void getNote(String noteLocation) throws Exception { - this.mockMvc.perform(get(noteLocation).with(port(8080))) - .andExpect(status().isOk()) + void getNote(String noteLocation) throws Exception { + this.mockMvc.perform(get(noteLocation)).andExpect(status().isOk()) .andExpect(jsonPath("title", is(notNullValue()))) .andExpect(jsonPath("body", is(notNullValue()))) - .andExpect(jsonPath("links[?(@.rel==tags)]", is(notNullValue()))) - .andDo(documentCurlRequest("notes/get_with_curl_request.asciidoc")) - .andDo(documentCurlResponse("notes/get_with_curl_response.asciidoc")); + .andExpect(jsonPath("links[?(@.rel==tags)]", is(notNullValue()))); } - private String createTag() throws Exception, JsonProcessingException { + String createTag() throws Exception, JsonProcessingException { Map tag = new HashMap(); tag.put("name", "getting-started"); String tagLocation = this.mockMvc .perform( - post("/tags").with(port(8080)) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(tag))) + post("/tags").contentType(MediaTypes.HAL_JSON).content( + objectMapper.writeValueAsString(tag))) .andExpect(status().isCreated()) - .andExpect(header().string("Location", notNullValue())) - .andDo(documentCurlRequest("tags/create_with_curl_request.asciidoc") - .includeResponseHeaders()) - .andDo(documentCurlResponse("tags/create_with_curl_response.asciidoc") - .includeResponseHeaders()).andReturn().getResponse() - .getHeader("Location"); + .andExpect(header().string("Location", notNullValue())).andReturn() + .getResponse().getHeader("Location"); return tagLocation; } - private void getTag(String tagLocation) throws Exception { - this.mockMvc.perform(get(tagLocation).with(port(8080))) - .andExpect(status().isOk()) + void getTag(String tagLocation) throws Exception { + this.mockMvc.perform(get(tagLocation)).andExpect(status().isOk()) .andExpect(jsonPath("name", is(notNullValue()))) - .andExpect(jsonPath("links[?(@.rel==notes)]", is(notNullValue()))) - .andDo(documentCurlRequest("tags/get_with_curl_request.asciidoc")) - .andDo(documentCurlResponse("tags/get_with_curl_response.asciidoc")); + .andExpect(jsonPath("links[?(@.rel==notes)]", is(notNullValue()))); } - private String createTaggedNote(String tag) throws Exception { + String createTaggedNote(String tag) throws Exception { Map note = new HashMap(); note.put("title", "Tagged note creation with cURL"); note.put("body", "An example of how to create a tagged note using cURL"); @@ -164,77 +144,49 @@ public class GettingStartedDocumentation { String noteLocation = this.mockMvc .perform( - post("/notes").with(port(8080)) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(note))) + post("/notes").contentType(MediaType.APPLICATION_JSON).content( + objectMapper.writeValueAsString(note))) .andExpect(status().isCreated()) - .andExpect(header().string("Location", notNullValue())) - .andDo(documentCurlRequest( - "notes/create_tagged_with_curl_request.asciidoc") - .includeResponseHeaders()) - .andDo(documentCurlResponse( - "notes/create_tagged_with_curl_response.asciidoc") - .includeResponseHeaders()).andReturn().getResponse() - .getHeader("Location"); + .andExpect(header().string("Location", notNullValue())).andReturn() + .getResponse().getHeader("Location"); return noteLocation; } - private void getTaggedNote(String tagLocation) throws Exception { - this.mockMvc - .perform(get(tagLocation).with(port(8080))) - .andExpect(status().isOk()) + void getTaggedNote(String tagLocation) throws Exception { + this.mockMvc.perform(get(tagLocation)).andExpect(status().isOk()) .andExpect(jsonPath("title", is(notNullValue()))) .andExpect(jsonPath("body", is(notNullValue()))) - .andExpect(jsonPath("links[?(@.rel==tags)]", is(notNullValue()))) - .andDo(documentCurlRequestAndResponse("notes/get_tagged_with_curl.asciidoc")); + .andExpect(jsonPath("links[?(@.rel==tags)]", is(notNullValue()))); } - private void getTags(String taggedNoteLocation) throws Exception { + void getTags(String taggedNoteLocation) throws Exception { String tagsLocation = getLink(this.mockMvc.perform(get(taggedNoteLocation)) .andReturn(), "tags"); - this.mockMvc - .perform(get(tagsLocation).with(port(8080))) - .andExpect(status().isOk()) - .andExpect(jsonPath("links[?(@.rel==tag)]", is(notNullValue()))) - .andDo(documentCurlRequest("notes/get_tags_for_tagged_with_curl_request.asciidoc")) - .andDo(documentCurlResponse("notes/get_tags_for_tagged_with_curl_response.asciidoc")); + this.mockMvc.perform(get(tagsLocation)).andExpect(status().isOk()) + .andExpect(jsonPath("links[?(@.rel==tag)]", is(notNullValue()))); } - private void tagExistingNote(String noteLocation, String tagLocation) - throws Exception { + void tagExistingNote(String noteLocation, String tagLocation) throws Exception { Map update = new HashMap(); update.put("tags", Arrays.asList(tagLocation)); this.mockMvc .perform( - patch(noteLocation).with(port(8080)) - .contentType(MediaType.APPLICATION_JSON) + patch(noteLocation).contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(update))) - .andExpect(status().isOk()) - .andDo(documentCurlRequest( - "notes/tag_existing_with_curl_request.asciidoc") - .includeResponseHeaders()) - .andDo(documentCurlResponse( - "notes/tag_existing_with_curl_response.asciidoc") - .includeResponseHeaders()); + .andDo(print()).andExpect(status().isNoContent()); } - private void getTaggedExistingNote(String tagLocation) throws Exception { - this.mockMvc - .perform(get(tagLocation).with(port(8080))) - .andExpect(status().isOk()) - .andDo(documentCurlRequestAndResponse("notes/get_tagged_existing_with_curl.asciidoc")); + void getTaggedExistingNote(String tagLocation) throws Exception { + this.mockMvc.perform(get(tagLocation)).andExpect(status().isOk()); } - private void getTagsForExistingNote(String taggedNoteLocation) throws Exception { + void getTagsForExistingNote(String taggedNoteLocation) throws Exception { String tagsLocation = getLink(this.mockMvc.perform(get(taggedNoteLocation)) .andReturn(), "tags"); - this.mockMvc - .perform(get(tagsLocation).with(port(8080))) - .andExpect(status().isOk()) - .andExpect(jsonPath("links[?(@.rel==tag)]", is(notNullValue()))) - .andDo(documentCurlRequestAndResponse("notes/get_tags_for_existing_with_curl.asciidoc")); + this.mockMvc.perform(get(tagsLocation)).andExpect(status().isOk()) + .andExpect(jsonPath("links[?(@.rel==tag)]", is(notNullValue()))); } private String getLink(MvcResult result, String rel) diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotesController.java b/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotesController.java index 4d38b2a6..de31f824 100644 --- a/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotesController.java +++ b/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotesController.java @@ -94,6 +94,7 @@ public class NotesController { } @RequestMapping(value = "/{id}", method = RequestMethod.PATCH) + @ResponseStatus(HttpStatus.NO_CONTENT) void updateNote(@PathVariable("id") long id, @RequestBody NotePatchInput noteInput) { Note note = this.noteRepository.findOne(id); if (noteInput.getTagUris() != null) { diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/Documentation.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/Documentation.java deleted file mode 100644 index 32a1d51b..00000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/Documentation.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2014 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.core; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.PrintStream; - -import org.springframework.restdocs.core.DocumentationWriter.DocumentationAction; - -public abstract class Documentation { - - public static void document(String path, DocumentationAction action) throws Exception { - PrintStream printStream = createPrintStream(path); - try { - DocumentationContext.set(new DocumentationContext(printStream)); - action.perform(); - } - finally { - DocumentationContext.set(null); - printStream.close(); - } - } - - private static PrintStream createPrintStream(String name) - throws FileNotFoundException { - File outputFile = new File(name); - if (!outputFile.isAbsolute()) { - outputFile = makeAbsolute(outputFile); - } - outputFile.getParentFile().mkdirs(); - - return new PrintStream(new FileOutputStream(outputFile)); - } - - private static File makeAbsolute(File outputFile) { - return new File(new DocumentationProperties().getOutputDir(), - outputFile.getPath()); - } -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/DocumentationContext.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/DocumentationContext.java index 40ab9496..2b3ef0b1 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/DocumentationContext.java +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/DocumentationContext.java @@ -16,27 +16,47 @@ package org.springframework.restdocs.core; -import java.io.PrintStream; +import java.lang.reflect.Method; +import java.util.Stack; class DocumentationContext { - private static final InheritableThreadLocal CONTEXTS = new InheritableThreadLocal(); + private static final InheritableThreadLocal> CONTEXTS = new InheritableThreadLocal>() { - private final DocumentationWriter writer; + @Override + protected Stack initialValue() { + return new Stack(); + } - public DocumentationContext(PrintStream printStream) { - this.writer = new DocumentationWriter(printStream); - } + }; - public DocumentationWriter getWriter() { - return this.writer; + private final Class documentationClass; + + private final Method documentationMethod; + + public DocumentationContext(Class documentationClass, Method documentationMethod) { + this.documentationClass = documentationClass; + this.documentationMethod = documentationMethod; } public static DocumentationContext current() { - return CONTEXTS.get(); + return CONTEXTS.get().peek(); } - static void set(DocumentationContext context) { - CONTEXTS.set(context); + public Class getDocumentationClass() { + return documentationClass; } + + public Method getDocumentationMethod() { + return documentationMethod; + } + + static void push(DocumentationContext context) { + CONTEXTS.get().push(context); + } + + static void pop() { + CONTEXTS.get().pop(); + } + } diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationConfiguration.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationConfiguration.java new file mode 100644 index 00000000..50093fc3 --- /dev/null +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationConfiguration.java @@ -0,0 +1,76 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.core; + +import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlRequest; +import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlRequestAndResponse; +import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlResponse; + +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.test.web.servlet.request.RequestPostProcessor; +import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; +import org.springframework.test.web.servlet.setup.MockMvcConfigurer; +import org.springframework.web.context.WebApplicationContext; + +public class RestDocumentationConfiguration implements MockMvcConfigurer { + + private String scheme = "http"; + + private String host = "localhost"; + + private int port = 8080; + + public RestDocumentationConfiguration withScheme(String scheme) { + this.scheme = scheme; + return this; + } + + public RestDocumentationConfiguration withHost(String host) { + this.host = host; + return this; + } + + public RestDocumentationConfiguration withPort(int port) { + this.port = port; + return this; + } + + @Override + public void afterConfigurerAdded(ConfigurableMockMvcBuilder builder) { + builder.alwaysDo(documentCurlRequest().includeResponseHeaders()) + .alwaysDo(documentCurlResponse().includeResponseHeaders()) + .alwaysDo(documentCurlRequestAndResponse().includeResponseHeaders()); + } + + @Override + public RequestPostProcessor beforeMockMvcCreated( + ConfigurableMockMvcBuilder builder, WebApplicationContext context) { + return new RequestPostProcessor() { + + @Override + public MockHttpServletRequest postProcessRequest( + MockHttpServletRequest request) { + request.setScheme(scheme); + request.setRemotePort(port); + request.setServerPort(port); + request.setRemoteHost(host); + return request; + } + }; + } + +} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationJUnit4ClassRunner.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationJUnit4ClassRunner.java new file mode 100644 index 00000000..18577e42 --- /dev/null +++ b/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationJUnit4ClassRunner.java @@ -0,0 +1,70 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.restdocs.core; + +import java.lang.reflect.Method; + +import org.junit.runners.model.InitializationError; +import org.springframework.cglib.proxy.Enhancer; +import org.springframework.cglib.proxy.MethodInterceptor; +import org.springframework.cglib.proxy.MethodProxy; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +public class RestDocumentationJUnit4ClassRunner extends SpringJUnit4ClassRunner { + + public RestDocumentationJUnit4ClassRunner(Class clazz) throws InitializationError { + super(clazz); + } + + protected Object createTest() throws Exception { + Object testInstance = createProxiedTestInstance(); + getTestContextManager().prepareTestInstance(testInstance); + return testInstance; + } + + private Object createProxiedTestInstance() { + Enhancer enhancer = new Enhancer(); + enhancer.setSuperclass(getTestClass().getJavaClass()); + enhancer.setClassLoader(getTestClass().getJavaClass().getClassLoader()); + enhancer.setCallback(new DocumentationContextManagingMethodInterceptor( + getTestClass().getJavaClass())); + return enhancer.create(); + } + + private static class DocumentationContextManagingMethodInterceptor implements + MethodInterceptor { + + private final Class testClass; + + private DocumentationContextManagingMethodInterceptor(Class testClass) { + this.testClass = testClass; + } + + @Override + public Object intercept(Object target, Method method, Object[] args, + MethodProxy methodProxy) throws Throwable { + DocumentationContext.push(new DocumentationContext(this.testClass, method)); + try { + return methodProxy.invokeSuper(target, args); + } + finally { + DocumentationContext.pop(); + } + } + + } +} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationRequestPostProcessors.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationRequestPostProcessors.java deleted file mode 100644 index 8f96795c..00000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationRequestPostProcessors.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2014 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.restdocs.core; - -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.test.web.servlet.request.RequestPostProcessor; - -public abstract class RestDocumentationRequestPostProcessors { - - public static RequestPostProcessor port(final int port) { - return new RequestPostProcessor() { - - @Override - public MockHttpServletRequest postProcessRequest( - MockHttpServletRequest request) { - request.setRemotePort(port); - request.setServerPort(port); - return request; - } - }; - - } - - public static RequestPostProcessor host(final String host) { - return new RequestPostProcessor() { - - @Override - public MockHttpServletRequest postProcessRequest( - MockHttpServletRequest request) { - request.setRemoteHost(host); - return request; - } - }; - - } - -} 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 e196d53b..e5e390ce 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 @@ -16,6 +16,8 @@ package org.springframework.restdocs.core; +import static org.springframework.restdocs.core.IterableEnumeration.iterable; + import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -31,12 +33,10 @@ import org.springframework.test.web.servlet.ResultHandler; import org.springframework.util.FileCopyUtils; import org.springframework.web.bind.annotation.RequestMethod; -import static org.springframework.restdocs.core.IterableEnumeration.iterable; - public abstract class RestDocumentationResultHandlers { - public static CurlResultHandler documentCurlRequest(String path) { - return new CurlResultHandler(path) { + public static CurlResultHandler documentCurlRequest() { + return new CurlResultHandler("Request.asciidoc") { @Override public void handle(MvcResult result, DocumentationWriter writer) throws Exception { @@ -46,8 +46,8 @@ public abstract class RestDocumentationResultHandlers { }; } - public static CurlResultHandler documentCurlResponse(String path) { - return new CurlResultHandler(path) { + public static CurlResultHandler documentCurlResponse() { + return new CurlResultHandler("Response.asciidoc") { @Override public void handle(MvcResult result, DocumentationWriter writer) throws Exception { @@ -57,8 +57,8 @@ public abstract class RestDocumentationResultHandlers { }; } - public static CurlResultHandler documentCurlRequestAndResponse(String path) { - return new CurlResultHandler(path) { + public static CurlResultHandler documentCurlRequestAndResponse() { + return new CurlResultHandler("RequestResponse.asciidoc") { @Override public void handle(MvcResult result, DocumentationWriter writer) throws Exception { @@ -70,22 +70,6 @@ public abstract class RestDocumentationResultHandlers { }; } - private static PrintStream createPrintStream(String path) - throws FileNotFoundException { - File outputFile = new File(path); - if (!outputFile.isAbsolute()) { - outputFile = makeAbsolute(outputFile); - } - outputFile.getParentFile().mkdirs(); - - return new PrintStream(new FileOutputStream(outputFile)); - } - - private static File makeAbsolute(File outputFile) { - return new File(new DocumentationProperties().getOutputDir(), - outputFile.getPath()); - } - private static final class CurlRequestDocumentationAction implements DocumentationAction { @@ -182,11 +166,11 @@ public abstract class RestDocumentationResultHandlers { public static abstract class CurlResultHandler implements ResultHandler { private final CurlConfiguration curlConfiguration = new CurlConfiguration(); - - private final String path; - - private CurlResultHandler(String path) { - this.path = path; + + private String suffix; + + public CurlResultHandler(String suffix) { + this.suffix = suffix; } CurlConfiguration getCurlConfiguration() { @@ -200,7 +184,7 @@ public abstract class RestDocumentationResultHandlers { @Override public void handle(MvcResult result) throws Exception { - PrintStream printStream = createPrintStream(this.path); + PrintStream printStream = createPrintStream(this.suffix); try { handle(result, new DocumentationWriter(printStream)); } @@ -208,6 +192,42 @@ public abstract class RestDocumentationResultHandlers { printStream.close(); } } + + private PrintStream createPrintStream(String suffix) + throws FileNotFoundException { + DocumentationContext context = DocumentationContext.current(); + if (context == null) { + throw new IllegalStateException(); + } + + String path = resolveOutputPath(context); + + File outputFile = new File(path); + if (!outputFile.isAbsolute()) { + outputFile = makeAbsolute(outputFile); + } + outputFile.getParentFile().mkdirs(); + + return new PrintStream(new FileOutputStream(outputFile)); + } + + private static File makeAbsolute(File outputFile) { + return new File(new DocumentationProperties().getOutputDir(), + outputFile.getPath()); + } + + private String resolveOutputPath(DocumentationContext context) { + String shortClassName = getShortClassName(context.getDocumentationClass()); + return shortClassName + "/" + context.getDocumentationMethod().getName() + this.suffix; + } + + private String getShortClassName(Class clazz) { + int index = clazz.getName().lastIndexOf('.'); + if (index >= 0) { + return clazz.getName().substring(index + 1); + } + return clazz.getName(); + } abstract void handle(MvcResult result, DocumentationWriter writer) throws Exception;