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(
requestFields(
fields.withPath("title")
.description("The title of the note")
.type(JsonFieldType.STRING)
.optional(),
fields.withPath("body")
.description("The body of the note")
.type(JsonFieldType.STRING)
.optional(),
fields.withPath("tags")
.description("An array of tag resource URIs")));
this.mockMvc.perform( this.mockMvc
patch(noteLocation).contentType(MediaTypes.HAL_JSON).content( .perform(patch(noteLocation)
this.objectMapper.writeValueAsString(noteUpdate))) .contentType(MediaTypes.HAL_JSON)
.andExpect(status().isNoContent()); .content(this.objectMapper.writeValueAsString(noteUpdate)))
.andExpect(status().isNoContent())
.andDo(this.documentationHandler.document(
requestFields(
fields.withPath("title")
.description("The title of the note")
.type(JsonFieldType.STRING)
.optional(),
fields.withPath("body")
.description("The body of the note")
.type(JsonFieldType.STRING)
.optional(),
fields.withPath("tags")
.description("An array of tag resource URIs"))));
} }
@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(
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")));
this.mockMvc.perform(get(tagLocation)) 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) {