Update HATEOAS sample to use new API for generating extra snippets

See gh-249
This commit is contained in:
Andy Wilkinson
2016-05-25 12:02:53 +01:00
parent 251d93ddc8
commit 26ff2ec8a5

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2014-2015 the original author or authors. * Copyright 2014-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -34,7 +34,6 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWit
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.snippet.Attributes.key; import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -73,7 +72,7 @@ public class ApiDocumentation {
@Rule @Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("build/generated-snippets"); public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("build/generated-snippets");
private RestDocumentationResultHandler document; private RestDocumentationResultHandler documentationHandler;
@Autowired @Autowired
private NoteRepository noteRepository; private NoteRepository noteRepository;
@@ -91,77 +90,73 @@ public class ApiDocumentation {
@Before @Before
public void setUp() { public void setUp() {
this.document = document("{method-name}", this.documentationHandler = document("{method-name}",
preprocessRequest(prettyPrint()), preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())); preprocessResponse(prettyPrint()));
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation)) .apply(documentationConfiguration(this.restDocumentation))
.alwaysDo(this.document) .alwaysDo(this.documentationHandler)
.build(); .build();
} }
@Test @Test
public void headersExample() throws Exception { public void headersExample() throws Exception {
this.document.snippets(responseHeaders( this.mockMvc
headerWithName("Content-Type").description("The Content-Type of the payload, e.g. `application/hal+json`"))); .perform(get("/"))
.andExpect(status().isOk())
this.mockMvc.perform(get("/")) .andDo(this.documentationHandler.document(
.andExpect(status().isOk()); responseHeaders(
headerWithName("Content-Type").description("The Content-Type of the payload, e.g. `application/hal+json`"))));
} }
@Test @Test
public void errorExample() throws Exception { public void errorExample() throws Exception {
this.document.snippets(responseFields(
fieldWithPath("error").description("The HTTP error that occurred, e.g. `Bad Request`"),
fieldWithPath("message").description("A description of the cause of the error"),
fieldWithPath("path").description("The path to which the request was made"),
fieldWithPath("status").description("The HTTP status code, e.g. `400`"),
fieldWithPath("timestamp").description("The time, in milliseconds, at which the error occurred")));
this.mockMvc this.mockMvc
.perform(get("/error") .perform(get("/error")
.requestAttr(RequestDispatcher.ERROR_STATUS_CODE, 400) .requestAttr(RequestDispatcher.ERROR_STATUS_CODE, 400)
.requestAttr(RequestDispatcher.ERROR_REQUEST_URI, .requestAttr(RequestDispatcher.ERROR_REQUEST_URI, "/notes")
"/notes") .requestAttr(RequestDispatcher.ERROR_MESSAGE, "The tag 'http://localhost:8080/tags/123' does not exist"))
.requestAttr(RequestDispatcher.ERROR_MESSAGE, .andExpect(status().isBadRequest())
"The tag 'http://localhost:8080/tags/123' does not exist"))
.andDo(print()).andExpect(status().isBadRequest())
.andExpect(jsonPath("error", is("Bad Request"))) .andExpect(jsonPath("error", is("Bad Request")))
.andExpect(jsonPath("timestamp", is(notNullValue()))) .andExpect(jsonPath("timestamp", is(notNullValue())))
.andExpect(jsonPath("status", is(400))) .andExpect(jsonPath("status", is(400)))
.andExpect(jsonPath("path", is(notNullValue()))); .andExpect(jsonPath("path", is(notNullValue())))
.andDo(this.documentationHandler.document(
responseFields(
fieldWithPath("error").description("The HTTP error that occurred, e.g. `Bad Request`"),
fieldWithPath("message").description("A description of the cause of the error"),
fieldWithPath("path").description("The path to which the request was made"),
fieldWithPath("status").description("The HTTP status code, e.g. `400`"),
fieldWithPath("timestamp").description("The time, in milliseconds, at which the error occurred"))));
} }
@Test @Test
public void indexExample() throws Exception { public void indexExample() throws Exception {
this.document.snippets(
links(
linkWithRel("notes").description("The <<resources-notes,Notes resource>>"),
linkWithRel("tags").description("The <<resources-tags,Tags resource>>")),
responseFields(
fieldWithPath("_links").description("<<resources-index-links,Links>> to other resources")));
this.mockMvc.perform(get("/")) this.mockMvc.perform(get("/"))
.andExpect(status().isOk()); .andExpect(status().isOk())
.andDo(this.documentationHandler.document(
links(
linkWithRel("notes").description("The <<resources-notes,Notes resource>>"),
linkWithRel("tags").description("The <<resources-tags,Tags resource>>")),
responseFields(
fieldWithPath("_links").description("<<resources-index-links,Links>> to other resources"))));
} }
@Test @Test
public void notesListExample() throws Exception { public void notesListExample() throws Exception {
this.noteRepository.deleteAll(); this.noteRepository.deleteAll();
createNote("REST maturity model", createNote("REST maturity model", "http://martinfowler.com/articles/richardsonMaturityModel.html");
"http://martinfowler.com/articles/richardsonMaturityModel.html"); createNote("Hypertext Application Language (HAL)", "http://stateless.co/hal_specification.html");
createNote("Hypertext Application Language (HAL)",
"http://stateless.co/hal_specification.html");
createNote("Application-Level Profile Semantics (ALPS)", "http://alps.io/spec/"); createNote("Application-Level Profile Semantics (ALPS)", "http://alps.io/spec/");
this.document.snippets( this.mockMvc
.perform(get("/notes"))
.andExpect(status().isOk())
.andDo(this.documentationHandler.document(
responseFields( responseFields(
fieldWithPath("_embedded.notes").description("An array of <<resources-note, Note resources>>"))); fieldWithPath("_embedded.notes").description("An array of <<resources-note, Note resources>>"))));
this.mockMvc.perform(get("/notes"))
.andExpect(status().isOk());
} }
@Test @Test
@@ -170,11 +165,11 @@ public class ApiDocumentation {
tag.put("name", "REST"); tag.put("name", "REST");
String tagLocation = this.mockMvc String tagLocation = this.mockMvc
.perform( .perform(post("/tags")
post("/tags").contentType(MediaTypes.HAL_JSON).content( .contentType(MediaTypes.HAL_JSON)
this.objectMapper.writeValueAsString(tag))) .content(this.objectMapper.writeValueAsString(tag)))
.andExpect(status().isCreated()).andReturn().getResponse() .andExpect(status().isCreated())
.getHeader("Location"); .andReturn().getResponse().getHeader("Location");
Map<String, Object> note = new HashMap<String, Object>(); Map<String, Object> note = new HashMap<String, Object>();
note.put("title", "REST maturity model"); note.put("title", "REST maturity model");
@@ -183,16 +178,17 @@ public class ApiDocumentation {
ConstrainedFields fields = new ConstrainedFields(NoteInput.class); ConstrainedFields fields = new ConstrainedFields(NoteInput.class);
this.document.snippets( this.mockMvc
.perform(post("/notes")
.contentType(MediaTypes.HAL_JSON)
.content(this.objectMapper.writeValueAsString(note)))
.andExpect(
status().isCreated())
.andDo(this.documentationHandler.document(
requestFields( requestFields(
fields.withPath("title").description("The title of the note"), fields.withPath("title").description("The title of the note"),
fields.withPath("body").description("The body of the note"), fields.withPath("body").description("The body of the note"),
fields.withPath("tags").description("An array of tag resource URIs"))); fields.withPath("tags").description("An array of tag resource URIs"))));
this.mockMvc.perform(
post("/notes").contentType(MediaTypes.HAL_JSON).content(
this.objectMapper.writeValueAsString(note)))
.andExpect(status().isCreated());
} }
@Test @Test
@@ -201,11 +197,11 @@ public class ApiDocumentation {
tag.put("name", "REST"); tag.put("name", "REST");
String tagLocation = this.mockMvc String tagLocation = this.mockMvc
.perform( .perform(post("/tags")
post("/tags").contentType(MediaTypes.HAL_JSON).content( .contentType(MediaTypes.HAL_JSON)
this.objectMapper.writeValueAsString(tag))) .content(this.objectMapper.writeValueAsString(tag)))
.andExpect(status().isCreated()).andReturn().getResponse() .andExpect(status().isCreated())
.getHeader("Location"); .andReturn().getResponse().getHeader("Location");
Map<String, Object> note = new HashMap<String, Object>(); Map<String, Object> note = new HashMap<String, Object>();
note.put("title", "REST maturity model"); note.put("title", "REST maturity model");
@@ -213,27 +209,27 @@ public class ApiDocumentation {
note.put("tags", Arrays.asList(tagLocation)); note.put("tags", Arrays.asList(tagLocation));
String noteLocation = this.mockMvc String noteLocation = this.mockMvc
.perform( .perform(post("/notes")
post("/notes").contentType(MediaTypes.HAL_JSON).content( .contentType(MediaTypes.HAL_JSON)
this.objectMapper.writeValueAsString(note))) .content(this.objectMapper.writeValueAsString(note)))
.andExpect(status().isCreated()).andReturn().getResponse() .andExpect(status().isCreated())
.getHeader("Location"); .andReturn().getResponse().getHeader("Location");
this.document.snippets( this.mockMvc
links( .perform(get(noteLocation))
linkWithRel("self").description("This <<resources-note,note>>"),
linkWithRel("note-tags").description("This note's <<resources-note-tags,tags>>")),
responseFields(
fieldWithPath("title").description("The title of the note"),
fieldWithPath("body").description("The body of the note"),
fieldWithPath("_links").description("<<resources-note-links,Links>> to other resources")));
this.mockMvc.perform(get(noteLocation))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("title", is(note.get("title")))) .andExpect(jsonPath("title", is(note.get("title"))))
.andExpect(jsonPath("body", is(note.get("body")))) .andExpect(jsonPath("body", is(note.get("body"))))
.andExpect(jsonPath("_links.self.href", is(noteLocation))) .andExpect(jsonPath("_links.self.href", is(noteLocation)))
.andExpect(jsonPath("_links.note-tags", is(notNullValue()))); .andExpect(jsonPath("_links.note-tags", is(notNullValue())))
.andDo(this.documentationHandler.document(
links(
linkWithRel("self").description("This <<resources-note,note>>"),
linkWithRel("note-tags").description("This note's <<resources-note-tags,tags>>")),
responseFields(
fieldWithPath("title").description("The title of the note"),
fieldWithPath("body").description("The body of the note"),
fieldWithPath("_links").description("<<resources-note-links,Links>> to other resources"))));
} }
@@ -246,12 +242,12 @@ public class ApiDocumentation {
createTag("Hypermedia"); createTag("Hypermedia");
createTag("HTTP"); createTag("HTTP");
this.document.snippets( this.mockMvc
.perform(get("/tags"))
.andExpect(status().isOk())
.andDo(this.documentationHandler.document(
responseFields( responseFields(
fieldWithPath("_embedded.tags").description("An array of <<resources-tag,Tag resources>>"))); fieldWithPath("_embedded.tags").description("An array of <<resources-tag,Tag resources>>"))));
this.mockMvc.perform(get("/tags"))
.andExpect(status().isOk());
} }
@Test @Test
@@ -261,14 +257,14 @@ public class ApiDocumentation {
ConstrainedFields fields = new ConstrainedFields(TagInput.class); ConstrainedFields fields = new ConstrainedFields(TagInput.class);
this.document.snippets( this.mockMvc
.perform(post("/tags")
.contentType(MediaTypes.HAL_JSON)
.content(this.objectMapper.writeValueAsString(tag)))
.andExpect(status().isCreated())
.andDo(this.documentationHandler.document(
requestFields( requestFields(
fields.withPath("name").description("The name of the tag"))); fields.withPath("name").description("The name of the tag"))));
this.mockMvc.perform(
post("/tags").contentType(MediaTypes.HAL_JSON).content(
this.objectMapper.writeValueAsString(tag)))
.andExpect(status().isCreated());
} }
@Test @Test
@@ -278,50 +274,52 @@ public class ApiDocumentation {
note.put("body", "http://martinfowler.com/articles/richardsonMaturityModel.html"); note.put("body", "http://martinfowler.com/articles/richardsonMaturityModel.html");
String noteLocation = this.mockMvc String noteLocation = this.mockMvc
.perform( .perform(post("/notes")
post("/notes").contentType(MediaTypes.HAL_JSON).content( .contentType(MediaTypes.HAL_JSON)
this.objectMapper.writeValueAsString(note))) .content(this.objectMapper.writeValueAsString(note)))
.andExpect(status().isCreated()).andReturn().getResponse() .andExpect(status().isCreated())
.getHeader("Location"); .andReturn().getResponse().getHeader("Location");
this.mockMvc.perform(get(noteLocation)).andExpect(status().isOk()) this.mockMvc
.andExpect(jsonPath("title", is(note.get("title")))) .perform(get(noteLocation))
.andExpect(jsonPath("body", is(note.get("body")))) .andExpect(status().isOk())
.andExpect(jsonPath("_links.self.href", is(noteLocation))) .andExpect(jsonPath("title", is(note.get("title"))))
.andExpect(jsonPath("_links.note-tags", is(notNullValue()))); .andExpect(jsonPath("body", is(note.get("body"))))
.andExpect(jsonPath("_links.self.href", is(noteLocation)))
.andExpect(jsonPath("_links.note-tags", is(notNullValue())));
Map<String, String> tag = new HashMap<String, String>(); Map<String, String> tag = new HashMap<String, String>();
tag.put("name", "REST"); tag.put("name", "REST");
String tagLocation = this.mockMvc String tagLocation = this.mockMvc
.perform( .perform(post("/tags")
post("/tags").contentType(MediaTypes.HAL_JSON).content( .contentType(MediaTypes.HAL_JSON)
this.objectMapper.writeValueAsString(tag))) .content(this.objectMapper.writeValueAsString(tag)))
.andExpect(status().isCreated()).andReturn().getResponse() .andExpect(status().isCreated())
.getHeader("Location"); .andReturn().getResponse().getHeader("Location");
Map<String, Object> noteUpdate = new HashMap<String, Object>(); Map<String, Object> noteUpdate = new HashMap<String, Object>();
noteUpdate.put("tags", Arrays.asList(tagLocation)); noteUpdate.put("tags", Arrays.asList(tagLocation));
ConstrainedFields fields = new ConstrainedFields(NotePatchInput.class); ConstrainedFields fields = new ConstrainedFields(NotePatchInput.class);
this.document.snippets( this.mockMvc
.perform(patch(noteLocation)
.contentType(MediaTypes.HAL_JSON)
.content(this.objectMapper.writeValueAsString(noteUpdate)))
.andExpect(status().isNoContent())
.andDo(this.documentationHandler.document(
requestFields( requestFields(
fields.withPath("title") fields.withPath("title")
.description("The title of the note") .description("The title of the note")
.type(JsonFieldType.STRING) .type(JsonFieldType.STRING)
.optional(), .optional(),
fields.withPath("body") fields.withPath("body")
.description("The body of the note") .description("The body of the note")
.type(JsonFieldType.STRING) .type(JsonFieldType.STRING)
.optional(), .optional(),
fields.withPath("tags") fields.withPath("tags")
.description("An array of tag resource URIs"))); .description("An array of tag resource URIs"))));
this.mockMvc.perform(
patch(noteLocation).contentType(MediaTypes.HAL_JSON).content(
this.objectMapper.writeValueAsString(noteUpdate)))
.andExpect(status().isNoContent());
} }
@Test @Test
@@ -330,23 +328,23 @@ public class ApiDocumentation {
tag.put("name", "REST"); tag.put("name", "REST");
String tagLocation = this.mockMvc String tagLocation = this.mockMvc
.perform( .perform(post("/tags")
post("/tags").contentType(MediaTypes.HAL_JSON).content( .contentType(MediaTypes.HAL_JSON)
this.objectMapper.writeValueAsString(tag))) .content(this.objectMapper.writeValueAsString(tag)))
.andExpect(status().isCreated()).andReturn().getResponse() .andExpect(status().isCreated())
.getHeader("Location"); .andReturn().getResponse().getHeader("Location");
this.document.snippets( this.mockMvc
links( .perform(get(tagLocation))
linkWithRel("self").description("This <<resources-tag,tag>>"),
linkWithRel("tagged-notes").description("The <<resources-tagged-notes,notes>> that have this tag")),
responseFields(
fieldWithPath("name").description("The name of the tag"),
fieldWithPath("_links").description("<<resources-tag-links,Links>> to other resources")));
this.mockMvc.perform(get(tagLocation))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("name", is(tag.get("name")))); .andExpect(jsonPath("name", is(tag.get("name"))))
.andDo(this.documentationHandler.document(
links(
linkWithRel("self").description("This <<resources-tag,tag>>"),
linkWithRel("tagged-notes").description("The <<resources-tagged-notes,notes>> that have this tag")),
responseFields(
fieldWithPath("name").description("The name of the tag"),
fieldWithPath("_links").description("<<resources-tag-links,Links>> to other resources"))));
} }
@Test @Test
@@ -355,25 +353,25 @@ public class ApiDocumentation {
tag.put("name", "REST"); tag.put("name", "REST");
String tagLocation = this.mockMvc String tagLocation = this.mockMvc
.perform( .perform(post("/tags")
post("/tags").contentType(MediaTypes.HAL_JSON).content( .contentType(MediaTypes.HAL_JSON)
this.objectMapper.writeValueAsString(tag))) .content(this.objectMapper.writeValueAsString(tag)))
.andExpect(status().isCreated()).andReturn().getResponse() .andExpect(status().isCreated())
.getHeader("Location"); .andReturn().getResponse().getHeader("Location");
Map<String, Object> tagUpdate = new HashMap<String, Object>(); Map<String, Object> tagUpdate = new HashMap<String, Object>();
tagUpdate.put("name", "RESTful"); tagUpdate.put("name", "RESTful");
ConstrainedFields fields = new ConstrainedFields(TagPatchInput.class); ConstrainedFields fields = new ConstrainedFields(TagPatchInput.class);
this.document.snippets( this.mockMvc
.perform(patch(tagLocation)
.contentType(MediaTypes.HAL_JSON)
.content(this.objectMapper.writeValueAsString(tagUpdate)))
.andExpect(status().isNoContent())
.andDo(this.documentationHandler.document(
requestFields( requestFields(
fields.withPath("name").description("The name of the tag"))); fields.withPath("name").description("The name of the tag"))));
this.mockMvc.perform(
patch(tagLocation).contentType(MediaTypes.HAL_JSON).content(
this.objectMapper.writeValueAsString(tagUpdate)))
.andExpect(status().isNoContent());
} }
private void createNote(String title, String body) { private void createNote(String title, String body) {