diff --git a/spring-rest-docs/pom.xml b/spring-rest-docs/pom.xml
index decdd3a5df..04ee11d0de 100644
--- a/spring-rest-docs/pom.xml
+++ b/spring-rest-docs/pom.xml
@@ -21,6 +21,7 @@
UTF-8
1.8
+ ${project.build.directory}/generated-snippets
@@ -44,16 +45,53 @@
1.0.1.RELEASE
test
+
+ com.jayway.jsonpath
+ json-path
+ 2.0.0
+
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ **/*Documentation.java
+
+
+
+
+ org.asciidoctor
+ asciidoctor-maven-plugin
+ 1.5.2
+
+
+ generate-docs
+ package
+
+ process-asciidoc
+
+
+ html
+ book
+
+ ${snippetsDirectory}
+
+ src/docs/asciidocs
+ target/generated-docs
+
+
+
+
+
+
diff --git a/spring-rest-docs/src/docs/asciidocs/api-guide.adoc b/spring-rest-docs/src/docs/asciidocs/api-guide.adoc
new file mode 100644
index 0000000000..9fbe74c072
--- /dev/null
+++ b/spring-rest-docs/src/docs/asciidocs/api-guide.adoc
@@ -0,0 +1,203 @@
+= RESTful Notes API Guide
+Baeldung;
+:doctype: book
+:icons: font
+:source-highlighter: highlightjs
+:toc: left
+:toclevels: 4
+:sectlinks:
+
+[[overview]]
+= Overview
+
+[[overview-http-verbs]]
+== HTTP verbs
+
+RESTful notes tries to adhere as closely as possible to standard HTTP and REST conventions in its
+use of HTTP verbs.
+
+|===
+| Verb | Usage
+
+| `GET`
+| Used to retrieve a resource
+
+| `POST`
+| Used to create a new resource
+
+| `PATCH`
+| Used to update an existing resource, including partial updates
+
+| `DELETE`
+| Used to delete an existing resource
+|===
+
+RESTful notes tries to adhere as closely as possible to standard HTTP and REST conventions in its
+use of HTTP status codes.
+
+|===
+| Status code | Usage
+
+| `200 OK`
+| The request completed successfully
+
+| `201 Created`
+| A new resource has been created successfully. The resource's URI is available from the response's
+`Location` header
+
+| `204 No Content`
+| An update to an existing resource has been applied successfully
+
+| `400 Bad Request`
+| The request was malformed. The response body will include an error providing further information
+
+| `404 Not Found`
+| The requested resource did not exist
+|===
+
+[[overview-headers]]
+== Headers
+
+Every response has the following header(s):
+
+include::{snippets}/headers-example/response-headers.adoc[]
+
+[[overview-hypermedia]]
+== Hypermedia
+
+RESTful Notes uses hypermedia and resources include links to other resources in their
+responses. Responses are in http://stateless.co/hal_specification.html[Hypertext Application
+from resource to resource.
+Language (HAL)] format. Links can be found beneath the `_links` key. Users of the API should
+not create URIs themselves, instead they should use the above-described links to navigate
+
+[[resources]]
+= Resources
+
+
+
+[[resources-index]]
+== Index
+
+The index provides the entry point into the service.
+
+[[resources-index-access]]
+=== Accessing the index
+
+A `GET` request is used to access the index
+
+==== Response structure
+
+include::{snippets}/index-example/http-response.adoc[]
+
+==== Example response
+
+include::{snippets}/index-example/http-response.adoc[]
+
+==== Example request
+
+include::{snippets}/index-example/http-request.adoc[]
+
+==== CURL request
+
+include::{snippets}/index-example/curl-request.adoc[]
+
+[[resources-index-links]]
+==== Links
+
+include::{snippets}/index-example/links.adoc[]
+
+
+[[resources-CRUD]]
+== CRUD REST Service
+
+The CRUD provides the entry point into the service.
+
+[[resources-crud-access]]
+=== Accessing the crud GET
+
+A `GET` request is used to access the CRUD read
+
+==== Response structure
+
+include::{snippets}/crud-get-example/http-request.adoc[]
+
+==== Example response
+
+include::{snippets}/crud-get-example/http-response.adoc[]
+
+==== CURL request
+
+include::{snippets}/crud-get-example/curl-request.adoc[]
+
+[[resources-crud-access]]
+=== Accessing the crud POST
+
+A `POST` request is used to access the CRUD create
+
+==== Response structure
+
+include::{snippets}/crud-create-example/http-request.adoc[]
+
+==== Example response
+
+include::{snippets}/crud-create-example/http-response.adoc[]
+
+==== CURL request
+
+include::{snippets}/crud-create-example/curl-request.adoc[]
+
+[[resources-crud-access]]
+=== Accessing the crud DELETE
+
+A `DELETE` request is used to access the CRUD create
+
+==== Response structure
+
+include::{snippets}/crud-delete-example/http-request.adoc[]
+
+==== Example response
+
+include::{snippets}/crud-delete-example/http-response.adoc[]
+
+==== CURL request
+
+include::{snippets}/crud-delete-example/curl-request.adoc[]
+
+[[resources-crud-access]]
+=== Accessing the crud PATCH
+
+A `PATCH` request is used to access the CRUD create
+
+==== Response structure
+
+include::{snippets}/crud-patch-example/http-request.adoc[]
+
+==== Example response
+
+include::{snippets}/crud-patch-example/http-response.adoc[]
+
+==== CURL request
+
+include::{snippets}/crud-patch-example/curl-request.adoc[]
+
+[[resources-crud-access]]
+=== Accessing the crud PUT
+
+A `PUT` request is used to access the CRUD create
+
+==== Response structure
+
+include::{snippets}/crud-put-example/http-request.adoc[]
+
+==== Example response
+
+include::{snippets}/crud-put-example/http-response.adoc[]
+
+==== CURL request
+
+include::{snippets}/crud-put-example/curl-request.adoc[]
+
+
+
+
diff --git a/spring-rest-docs/src/main/java/com/example/CRUDController.java b/spring-rest-docs/src/main/java/com/example/CRUDController.java
new file mode 100644
index 0000000000..818b29d3a6
--- /dev/null
+++ b/spring-rest-docs/src/main/java/com/example/CRUDController.java
@@ -0,0 +1,55 @@
+package com.example;
+
+import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/crud")
+public class CRUDController {
+
+ @RequestMapping(method=RequestMethod.GET)
+ @ResponseStatus(HttpStatus.OK)
+ public List read(@RequestBody CrudInput crudInput) {
+ List returnList=new ArrayList();
+ returnList.add(crudInput);
+ return returnList;
+ }
+
+ @ResponseStatus(HttpStatus.CREATED)
+ @RequestMapping(method=RequestMethod.POST)
+ public HttpHeaders save(@RequestBody CrudInput crudInput) {
+ HttpHeaders httpHeaders = new HttpHeaders();
+ httpHeaders.setLocation(linkTo(CRUDController.class).slash(crudInput.getTitle()).toUri());
+ return httpHeaders;
+ }
+
+ @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
+ @ResponseStatus(HttpStatus.OK)
+ HttpHeaders delete(@RequestBody CrudInput crudInput) {
+ HttpHeaders httpHeaders = new HttpHeaders();
+ return httpHeaders;
+ }
+
+ @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
+ @ResponseStatus(HttpStatus.ACCEPTED)
+ void put(@PathVariable("id") long id, @RequestBody CrudInput crudInput) {
+
+ }
+
+ @RequestMapping(value = "/{id}", method = RequestMethod.PATCH)
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ void patch(@PathVariable("id") long id, @RequestBody CrudInput crudInput) {
+
+ }
+}
diff --git a/spring-rest-docs/src/main/java/com/example/CrudInput.java b/spring-rest-docs/src/main/java/com/example/CrudInput.java
new file mode 100644
index 0000000000..3d91b7d45a
--- /dev/null
+++ b/spring-rest-docs/src/main/java/com/example/CrudInput.java
@@ -0,0 +1,42 @@
+package com.example;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+
+import org.hibernate.validator.constraints.NotBlank;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class CrudInput {
+
+ //@NotBlank
+ private final String title;
+
+ private final String body;
+
+ private final List tagUris;
+
+ @JsonCreator
+ public CrudInput(@JsonProperty("title") String title,
+ @JsonProperty("body") String body, @JsonProperty("tags") List tagUris) {
+ this.title = title;
+ this.body = body;
+ this.tagUris = tagUris == null ? Collections.emptyList() : tagUris;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getBody() {
+ return body;
+ }
+
+ @JsonProperty("tags")
+ public List getTagUris() {
+ return this.tagUris;
+ }
+
+}
\ No newline at end of file
diff --git a/spring-rest-docs/src/main/java/com/example/IndexController.java b/spring-rest-docs/src/main/java/com/example/IndexController.java
index 92d987f05d..a6b4537c43 100644
--- a/spring-rest-docs/src/main/java/com/example/IndexController.java
+++ b/spring-rest-docs/src/main/java/com/example/IndexController.java
@@ -1,23 +1,22 @@
package com.example;
+import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
+
import org.springframework.hateoas.ResourceSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
-import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
-
@RestController
-@RequestMapping("/api")
+@RequestMapping("/")
public class IndexController {
- @RequestMapping(method = RequestMethod.GET)
- public ResourceSupport index() {
- ResourceSupport index = new ResourceSupport();
- index.add(linkTo(MyRestController.class).withRel("notes"));
- index.add(linkTo(MyRestController.class).withRel("tags"));
- return index;
- }
+ @RequestMapping(method=RequestMethod.GET)
+ public ResourceSupport index() {
+ ResourceSupport index = new ResourceSupport();
+ index.add(linkTo(CRUDController.class).withRel("crud"));
+ return index;
+ }
}
\ No newline at end of file
diff --git a/spring-rest-docs/src/main/java/com/example/MyRestController.java b/spring-rest-docs/src/main/java/com/example/MyRestController.java
deleted file mode 100644
index 896b82abfb..0000000000
--- a/spring-rest-docs/src/main/java/com/example/MyRestController.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.example;
-
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
-
-@RestController
-@RequestMapping("/rest/api")
-public class MyRestController {
-
- @RequestMapping(method = RequestMethod.GET)
- public String index() {
- return "Hello";
- }
-
-}
diff --git a/spring-rest-docs/src/main/java/com/example/SpringRestDocsApplication.java b/spring-rest-docs/src/main/java/com/example/SpringRestDocsApplication.java
index da09f9accc..dd20ef324e 100644
--- a/spring-rest-docs/src/main/java/com/example/SpringRestDocsApplication.java
+++ b/spring-rest-docs/src/main/java/com/example/SpringRestDocsApplication.java
@@ -6,7 +6,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringRestDocsApplication {
- public static void main(String[] args) {
- SpringApplication.run(SpringRestDocsApplication.class, args);
- }
+ public static void main(String[] args) {
+ SpringApplication.run(SpringRestDocsApplication.class, args);
+ }
}
diff --git a/spring-rest-docs/src/test/java/com/example/ApiDocumentation.java b/spring-rest-docs/src/test/java/com/example/ApiDocumentation.java
index 5490e90ff5..96ecbe158a 100644
--- a/spring-rest-docs/src/test/java/com/example/ApiDocumentation.java
+++ b/spring-rest-docs/src/test/java/com/example/ApiDocumentation.java
@@ -1,66 +1,184 @@
package com.example;
+import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
+import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders;
+import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
+import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.put;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.restdocs.snippet.Attributes.key;
+import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
import org.junit.Before;
import org.junit.Rule;
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.restdocs.RestDocumentation;
+import org.springframework.restdocs.constraints.ConstraintDescriptions;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
+import org.springframework.restdocs.payload.FieldDescriptor;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
-import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
-import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
-import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
-import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
-import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
-import static org.springframework.restdocs.operation.preprocess.Preprocessors.*;
-import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
-import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import com.fasterxml.jackson.databind.ObjectMapper;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringRestDocsApplication.class)
@WebAppConfiguration
public class ApiDocumentation {
- @Rule
- public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets");
+ @Rule
+ public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets");
+
+ @Autowired
+ private WebApplicationContext context;
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ private RestDocumentationResultHandler document;
+
+ private MockMvc mockMvc;
+
+ @Before
+ public void setUp() {
+ this.document = document("{method-name}",preprocessRequest(prettyPrint()),preprocessResponse(prettyPrint()));
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).apply(documentationConfiguration(this.restDocumentation)).alwaysDo(this.document).build();
+ }
+
- @Autowired
- private WebApplicationContext context;
+ @Test
+ public void headersExample() throws Exception {
+ this.document.snippets(responseHeaders(headerWithName("Content-Type").description("The Content-Type of the payload, e.g. `application/hal+json`")));
+ this.mockMvc.perform(get("/")).andExpect(status().isOk());
+ }
+
+ @Test
+ public void indexExample() throws Exception {
+ this.document.snippets(links(linkWithRel("crud").description("The <>")),responseFields(fieldWithPath("_links").description("<> to other resources")));
+ this.mockMvc.perform(get("/")).andExpect(status().isOk());
+ }
+
+
+ @Test
+ public void crudGetExample() throws Exception {
+
+ Map tag = new HashMap();
+ tag.put("name", "GET");
- private RestDocumentationResultHandler document;
+ String tagLocation =this.mockMvc.perform(get("/crud").contentType(MediaTypes.HAL_JSON).content(this.objectMapper.writeValueAsString(tag))).andExpect(status().isOk()).andReturn().getResponse().getHeader("Location");
- private MockMvc mockMvc;
+ Map crud = new HashMap();
+ crud.put("title", "Sample Model");
+ crud.put("body", "http://www.baeldung.com/");
+ crud.put("tags", Arrays.asList(tagLocation));
+
+ this.mockMvc.perform(get("/crud").contentType(MediaTypes.HAL_JSON).content(this.objectMapper.writeValueAsString(crud))).andExpect(status().isOk());
+ }
+
+ @Test
+ public void crudCreateExample() throws Exception {
+ Map tag = new HashMap();
+ tag.put("name", "CREATE");
- @Before
- public void setUp() {
- this.document = document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()));
+ String tagLocation =this.mockMvc.perform(post("/crud").contentType(MediaTypes.HAL_JSON).content(this.objectMapper.writeValueAsString(tag))).andExpect(status().isCreated()).andReturn().getResponse().getHeader("Location");
- this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
- .apply(documentationConfiguration(this.restDocumentation))
- .alwaysDo(this.document).build();
- }
+ Map crud = new HashMap();
+ crud.put("title", "Sample Model");
+ crud.put("body", "http://www.baeldung.com/");
+ crud.put("tags", Arrays.asList(tagLocation));
- @Test
- public void indexExample() throws Exception {
- this.document.snippets(
- links(
- linkWithRel("notes").description("The <>"),
- linkWithRel("tags").description("The <>")
- ),
- responseFields(fieldWithPath("_links").description("<> to other resources")));
+ ConstrainedFields fields = new ConstrainedFields(CrudInput.class);
+ this.document.snippets(requestFields(fields.withPath("title").description("The title of the note"),fields.withPath("body").description("The body of the note"),fields.withPath("tags").description("An array of tag resource URIs")));
+ this.mockMvc.perform(post("/crud").contentType(MediaTypes.HAL_JSON).content(this.objectMapper.writeValueAsString(crud))).andExpect(status().isCreated());
+
+
+ }
+
+ @Test
+ public void crudDeleteExample() throws Exception {
+
+ Map tag = new HashMap();
+ tag.put("name", "DELETE");
+
+ String tagLocation =this.mockMvc.perform(delete("/crud/10").contentType(MediaTypes.HAL_JSON).content(this.objectMapper.writeValueAsString(tag))).andExpect(status().isOk()).andReturn().getResponse().getHeader("Location");
+ Map crud = new HashMap();
+ crud.put("title", "Sample Model");
+ crud.put("body", "http://www.baeldung.com/");
+ crud.put("tags", Arrays.asList(tagLocation));
+
+ this.mockMvc.perform(delete("/crud/10").contentType(MediaTypes.HAL_JSON).content(this.objectMapper.writeValueAsString(crud))).andExpect(status().isOk());
+ }
+
+ @Test
+ public void crudPatchExample() throws Exception {
+
+ Map tag = new HashMap();
+ tag.put("name", "PATCH");
+
+ String tagLocation =this.mockMvc.perform(patch("/crud/10").contentType(MediaTypes.HAL_JSON).content(this.objectMapper.writeValueAsString(tag))).andExpect(status().isNoContent()).andReturn().getResponse().getHeader("Location");
+ Map crud = new HashMap();
+ crud.put("title", "Sample Model");
+ crud.put("body", "http://www.baeldung.com/");
+ crud.put("tags", Arrays.asList(tagLocation));
+
+ this.mockMvc.perform(patch("/crud/10").contentType(MediaTypes.HAL_JSON).content(this.objectMapper.writeValueAsString(crud))).andExpect(status().isNoContent());
+ }
+
+
+ @Test
+ public void crudPutExample() throws Exception {
+ Map tag = new HashMap();
+ tag.put("name", "PUT");
+
+ String tagLocation =this.mockMvc.perform(put("/crud/10").contentType(MediaTypes.HAL_JSON).content(this.objectMapper.writeValueAsString(tag))).andExpect(status().isAccepted()).andReturn().getResponse().getHeader("Location");
+ Map crud = new HashMap();
+ crud.put("title", "Sample Model");
+ crud.put("body", "http://www.baeldung.com/");
+ crud.put("tags", Arrays.asList(tagLocation));
+
+ this.mockMvc.perform(put("/crud/10").contentType(MediaTypes.HAL_JSON).content(this.objectMapper.writeValueAsString(crud))).andExpect(status().isAccepted());
+ }
+
+
+ @Test
+ public void contextLoads() {
+ }
+
+ private static class ConstrainedFields {
- this.mockMvc.perform(get("/api")).andExpect(status().isOk());
- }
+ private final ConstraintDescriptions constraintDescriptions;
- @Test
- public void contextLoads() {
- }
-}
\ No newline at end of file
+ ConstrainedFields(Class> input) {
+ this.constraintDescriptions = new ConstraintDescriptions(input);
+ }
+
+ private FieldDescriptor withPath(String path) {
+ return fieldWithPath(path).attributes(key("constraints").value(StringUtils.collectionToDelimitedString(this.constraintDescriptions.descriptionsForProperty(path), ". ")));
+ }
+ }
+
+
+}
diff --git a/spring-rest-docs/src/test/java/com/example/GettingStartedDocumentation.java b/spring-rest-docs/src/test/java/com/example/GettingStartedDocumentation.java
new file mode 100644
index 0000000000..e9fb105438
--- /dev/null
+++ b/spring-rest-docs/src/test/java/com/example/GettingStartedDocumentation.java
@@ -0,0 +1,148 @@
+package com.example;
+
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch;
+import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
+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;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Rule;
+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.restdocs.RestDocumentation;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.web.context.WebApplicationContext;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.jayway.jsonpath.JsonPath;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringApplicationConfiguration(classes = SpringRestDocsApplication.class)
+@WebAppConfiguration
+public class GettingStartedDocumentation {
+
+ @Rule
+ public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets");
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ @Autowired
+ private WebApplicationContext context;
+
+ private MockMvc mockMvc;
+
+
+ @Before
+ public void setUp() {
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
+ .apply(documentationConfiguration(this.restDocumentation)).alwaysDo(document("{method-name}/{step}/",
+ preprocessRequest(prettyPrint()),preprocessResponse(prettyPrint()))).build();
+ }
+
+ @Test
+ public void index() throws Exception {
+ this.mockMvc.perform(get("/").accept(MediaTypes.HAL_JSON)).andExpect(status().isOk()).andExpect(jsonPath("_links.crud", is(notNullValue()))).andExpect(jsonPath("_links.crud", is(notNullValue())));
+ }
+
+
+ //@Test
+ public void creatingANote() throws JsonProcessingException, Exception {
+ String noteLocation = createNote();
+ MvcResult note = getNote(noteLocation);
+ String tagLocation = createTag();
+ getTag(tagLocation);
+ String taggedNoteLocation = createTaggedNote(tagLocation);
+ MvcResult taggedNote = getNote(taggedNoteLocation);
+ getTags(getLink(taggedNote, "note-tags"));
+ tagExistingNote(noteLocation, tagLocation);
+ getTags(getLink(note, "note-tags"));
+ }
+
+ 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("/crud").contentType(MediaTypes.HAL_JSON).content(objectMapper.writeValueAsString(note))).andExpect(status().isCreated()).andExpect(header().string("Location", notNullValue())).andReturn().getResponse().getHeader("Location");
+ return noteLocation;
+ }
+
+ MvcResult getNote(String noteLocation) throws Exception {
+ return this.mockMvc.perform(get(noteLocation)).andExpect(status().isOk()).andExpect(jsonPath("title", is(notNullValue()))).andExpect(jsonPath("body", is(notNullValue()))).andExpect(jsonPath("_links.crud", is(notNullValue()))).andReturn();
+ }
+
+
+ String createTag() throws Exception, JsonProcessingException {
+ Map tag = new HashMap();
+ tag.put("name", "getting-started");
+ String tagLocation = this.mockMvc.perform(post("/crud").contentType(MediaTypes.HAL_JSON).content(objectMapper.writeValueAsString(tag))).andExpect(status().isCreated()).andExpect(header().string("Location", notNullValue())).andReturn().getResponse().getHeader("Location");
+ return tagLocation;
+ }
+
+ void getTag(String tagLocation) throws Exception {
+ this.mockMvc.perform(get(tagLocation)).andExpect(status().isOk())
+ .andExpect(jsonPath("name", is(notNullValue())))
+ .andExpect(jsonPath("_links.tagged-notes", is(notNullValue())));
+ }
+
+ 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");
+ note.put("tags", Arrays.asList(tag));
+
+ String noteLocation = this.mockMvc.perform(post("/notes").contentType(MediaTypes.HAL_JSON).content(objectMapper.writeValueAsString(note)))
+ .andExpect(status().isCreated()).andExpect(header().string("Location", notNullValue())).andReturn().getResponse().getHeader("Location");
+ return noteLocation;
+ }
+
+ void getTags(String noteTagsLocation) throws Exception {
+ this.mockMvc.perform(get(noteTagsLocation))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("_embedded.tags", hasSize(1)));
+ }
+
+ void tagExistingNote(String noteLocation, String tagLocation) throws Exception {
+ Map update = new HashMap();
+ update.put("tags", Arrays.asList(tagLocation));
+ this.mockMvc.perform(patch(noteLocation).contentType(MediaTypes.HAL_JSON).content(objectMapper.writeValueAsString(update))).andExpect(status().isNoContent());
+ }
+
+ MvcResult getTaggedExistingNote(String noteLocation) throws Exception {
+ return this.mockMvc.perform(get(noteLocation)).andExpect(status().isOk()).andReturn();
+ }
+
+ void getTagsForExistingNote(String noteTagsLocation) throws Exception {
+ this.mockMvc.perform(get(noteTagsLocation))
+ .andExpect(status().isOk()).andExpect(jsonPath("_embedded.tags", hasSize(1)));
+ }
+
+ private String getLink(MvcResult result, String rel)
+ throws UnsupportedEncodingException {
+ return JsonPath.parse(result.getResponse().getContentAsString()).read("_links." + rel + ".href");
+ }
+
+}