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..195b9dc514 100644 --- a/spring-rest-docs/src/test/java/com/example/ApiDocumentation.java +++ b/spring-rest-docs/src/test/java/com/example/ApiDocumentation.java @@ -1,28 +1,39 @@ package com.example; +import com.fasterxml.jackson.databind.ObjectMapper; 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.web.context.WebApplicationContext; +import java.util.HashMap; +import java.util.Map; + +import static java.util.Collections.singletonList; +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.*; 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.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.snippet.Attributes.key; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.util.StringUtils.collectionToDelimitedString; @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = SpringRestDocsApplication.class) @@ -35,6 +46,9 @@ public class ApiDocumentation { @Autowired private WebApplicationContext context; + @Autowired + private ObjectMapper objectMapper; + private RestDocumentationResultHandler document; private MockMvc mockMvc; @@ -42,25 +56,179 @@ public class ApiDocumentation { @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(); + .alwaysDo(this.document) + .build(); + } + + + @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("notes").description("The <>"), - linkWithRel("tags").description("The <>") - ), - responseFields(fieldWithPath("_links").description("<> to other resources"))); - - this.mockMvc.perform(get("/api")).andExpect(status().isOk()); + 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"); + + String tagLocation = this.mockMvc.perform(get("/crud") + .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", singletonList(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"); + + String tagLocation = this.mockMvc.perform(post("/crud") + .contentType(MediaTypes.HAL_JSON) + .content(this.objectMapper.writeValueAsString(tag))) + .andExpect(status().isCreated()) + .andReturn() + .getResponse() + .getHeader("Location"); + + Map crud = new HashMap<>(); + crud.put("title", "Sample Model"); + crud.put("body", "http://www.baeldung.com/"); + crud.put("tags", singletonList(tagLocation)); + + 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", singletonList(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", singletonList(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", singletonList(tagLocation)); + + this.mockMvc.perform(put("/crud/10") + .contentType(MediaTypes.HAL_JSON) + .content(this.objectMapper.writeValueAsString(crud))) + .andExpect(status().isAccepted()); + } + + @Test public void contextLoads() { } -} \ No newline at end of file + + private static class ConstrainedFields { + + private final ConstraintDescriptions constraintDescriptions; + + ConstrainedFields(Class input) { + this.constraintDescriptions = new ConstraintDescriptions(input); + } + + private FieldDescriptor withPath(String path) { + return fieldWithPath(path) + .attributes(key("constraints") + .value(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..7f3c4db1f9 --- /dev/null +++ b/spring-rest-docs/src/test/java/com/example/GettingStartedDocumentation.java @@ -0,0 +1,158 @@ +package com.example; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jayway.jsonpath.JsonPath; +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 java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static org.hamcrest.Matchers.*; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@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()))); + } + +// 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"); +// } + +}