I started to use Spring REST Docs to document a simple REST API. I have a payload that has some hierarchical structure, for example like this (company with employees).
{
"companyName": "FooBar",
"employee":
[
{
"name": "Lorem",
"age": "42"
},
{
"name": "Ipsum",
"age": "24"
}
]
}
I would like to separate the documentation of the company object (name and array of employees) and the employee object (employee name and age).
Using the org.springframework.restdocs.payload.PayloadDocumentation.responseFields
like explained here forces me to document all fields but in case I only want to document the employee fields - how can I achieve this?
I have no problem to document the company without the employee details because if a field is document the descendants are treated as been documented also. But I can not document the employee structure on its own and I have no dedicated payload for this structure without the company root object.
Inspired by this question, I've implemented an enhancement which makes the original answer (see below) obsolete.
If you use 1.0.0.BUILD-SNAPSHOT (available from https://repo.spring.io/libs-snapshot), you can now mark a field as ignored. Ignored fields count has having been documented without actually appearing in the documentation.
Given that you want to separate the documentation, having two document calls makes sense. In the first you can document the company name and the array of employees. In the second you document the employees array and mark the company name as ignored.
Your test would look something like this:
mockMvc.perform(get("/company/5").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("company",
responseFields(
fieldWithPath("companyName").description(
"The name of the company"),
fieldWithPath("employee").description(
"An array of the company's employees"))))
.andDo(document("employee",
responseFields(
fieldWithPath("companyName").ignored(),
fieldWithPath("employee[].name").description(
"The name of the employee"),
fieldWithPath("employee[].age").description(
"The age of the employee"))));
You'll end up with two directories of snippets, one named company
and one named employee
. You can then use the response-fields.adoc
snippet from each.
There's no explicit support for ignoring a field when you're documenting a request or a response, but I think you can probably achieve what you want by using a preprocessor to remove the fields that you don't want to document.
Given that you want to separate the documentation, having two document
calls makes sense. In the first you can document the company name and the array of employees. In the second you need to preprocess the request to remove the company and then document the employees array.
Your test would look something like this:
mockMvc.perform(get("/company/5").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("company",
responseFields(
fieldWithPath("companyName").description(
"The name of the company"),
fieldWithPath("employee").description(
"An array of the company's employees"))))
.andDo(document("employee",
preprocessResponse(removeCompany()),
responseFields(
fieldWithPath("employee[].name").description(
"The name of the employee"),
fieldWithPath("employee[].age").description(
"The age of the employee"))));
Note the use of preprocessResponse
in the second document
call. removeCompany
returns a preprocessor that uses a custom ContentModifier
to remove company name from the response:
private OperationPreprocessor removeCompany() {
return new ContentModifyingOperationPreprocessor(new ContentModifier() {
@Override
public byte[] modifyContent(byte[] originalContent, MediaType contentType) {
ObjectMapper objectMapper = new ObjectMapper();
try {
Map<?, ?> map = objectMapper.readValue(originalContent, Map.class);
map.remove("companyName");
return objectMapper.writeValueAsBytes(map);
}
catch (IOException ex) {
return originalContent;
}
}
});
}
You'll end up with two directories of snippets, one named company
and one named employee
. You can then use the response-fields.adoc
snippet from each.
While the above will work, it's harder than it needs to be. I've opened an issue so that the preprocessing to modify the response's content will no longer be necessary.