Provide a snippet for documenting request and response structure
With complex, heavily nested payloads some people have found the table of fields that's currently produced by the request and response fields snippets a little difficult to use when trying to determine the structure.
A suggestion has been to reflect the nesting in the documentation, ideally with collapsible sections. A relatively simple, albeit without collapsible sections, approach would be something like this:
{
"userName": String,
"name": {
"familyName": String,
"givenName": "String
},
"phoneNumbers": [ {
"value": String
} ],
"emails": [ {
"value": String,
"primary": Boolean
} ],
"active": Boolean,
"verified": Boolean,
"origin": String,
"externalId": String
}
We faced the same problem and did the following:
a) Override the response-fields.snippet like:
|===
|Path|Type|Description
{{#fields}}
|{{#tableCellContent}}`{{#displayName}}{{displayName}}{{/displayName}}{{^displayName}}{{path}}{{/displayName}}`{{/tableCellContent}}
|{{#tableCellContent}}`{{type}}`{{/tableCellContent}}
|{{#tableCellContent}}{{description}}{{/tableCellContent}}
{{/fields}}
|===
=> displayName is an optional attribute to override long and ugly paths
b) Use Multiple .andDo(document(....)) calls for each nested structure in a response body like:
MvcResult res = mockMvc.perform(
RestDocumentationRequestBuilders.post("/search/products")
.with(myHeader)
.contentType(myType)
.content(mapper.writeValueAsString(params)))
.andExpect(status().isOk())
.andDo(document("search/prodcuts",
CliDocumentation.curlRequest(),
HttpDocumentation.httpRequest(),
HttpDocumentation.httpResponse(),
relaxedResponseFields(
fieldWithPath([].metaData").attributes(key("displayName").value("metaData")).description("Product Meta Data"),
fieldWithPath([].content").attributes(key("displayName").value("content")).description("Product Content"))))
.andDo(document("search/prodcuts/metaData",
relaxedResponseFields(
fieldWithPath([].metaData.f1").attributes(key("displayName").value("f1")).description("F1 desc..."),
fieldWithPath([].metaData.f2").attributes(key("displayName").value("f2")).description("F2 desc"),
)))
.andDo(document("search/prodcuts/content",
relaxedResponseFields(
fieldWithPath([].content.f1").attributes(key("displayName").value("f1")).description("Content F1 desc..."),
fieldWithPath([].content.f2").attributes(key("displayName").value("f2")).description("Content F2 desc"),
)))
.andReturn();
=> This generates multiple response-fields.adoc files containing simple, clear and reusable tables for a single nested structure.
Note: If my last comment is at the wrong place/issuce feel free to move it :-)
@AnnaMaus86 Interesting approach. Thank you for sharing what you did.
@wilkinsona Note, that we developed this approach to mimic the Github API (like in https://developer.github.com/v3/git/tags/#create-a-tag-object)
I have found that I want to document the existence of the fields (with some brief description) in the table (or something similar), and then make more detailed notes about the content below that, with separate snippets for each subfield that I want to document. This means that I need a new snippet type for an individual response field, something like new ResponseFieldSnippet("foo.bar") to extract the foo.bar field from the response.
The only difficult part for me has been parsing the field specification ("foo.bar" in the example above) and extracting the right parts of the response. Especially difficult when one of the fields traversed is an array because you might want a specific array element and be unwilling to specify it by index (in case the order wasn't deterministic).
A further complication with arrays is that the logical file name for an extracted field snippet is the path used to extract it ("foo.bar" in the example generates "foo.bar.adoc"), but if the path has an array index (e.g. "foo.bar[0]") then you can't include it in Asciidoctor because of the way the include gets parsed. I had to adopt a convention that I stripped out the indexed parts from a path before I generated the file name.
Thanks, @dsyer. That sounds like a use case for another kind of snippet. Could you open another issue please? I'm interested to know what sort of thing you're documenting in the snippet for an individual field and what assertions you think REST Docs could provide for it.
See #312.
@wilkinsona you mentioned on https://github.com/spring-projects/spring-restdocs/issues/498
not a fan of introspecting a DTO to discover fields and types. It takes any DTO to JSON conversion out of the equation and, if the introspection doesn't use the exact same conversion that your app does at runtime, can lead to inaccurate documentation.
Is there any possibility to use some sort of JSON schema generator https://gist.github.com/mpellegrini/7571845 or https://github.com/FasterXML/jackson-module-jsonSchemain order to generate without having to write introspection code or worry about inconsistencies between introspection and how jackson would serialise.
@davidgoate I'd like to explore what might be possible with JSON schema, but have never had the time. See #43 for the original idea. Offering support for something like Jackson's JSON schema module (for apps using Jackson for serialization) could fit well with that.