The aim of this extension to Spring REST Docs is to help you to write even less — both code and documentation. You still get the same nice documentation as with Spring REST Docs itself. The main benefit is that writing less and moving the documentation closer to the code increases the maintainability of the documentation.
In Spring REST Docs you have to add the documentation for your JSON with a DSL in your test. We moved this documentation to the POJO that represents your JSON object. You just add Javadoc to the fields and it will end up in the documentation.
Features:
-
Jackson visitor that gathers the whole JSON structure and includes Javadoc and constraint annotations on the fields. It works for both request and response bodies. In addition to the constraint documentation support that is already in Spring REST Docs, the constraint message is automatically included in the documentation. Constraint groups are also supported.
-
Path and query parameters can be documented automatically.
-
A helper to document authentication.
-
A snippet that includes all other snippets and thus helps you write even less.
FAQ
-
Does it work with Spring Web MVC tests?
Yes, take a look at the example.
-
Does it work with Spring WebFlux?
Yes, take a look at the example.
-
Is Kotlin supported?
Yes, Spring Auto REST Docs 1.0.13 and 2.0.0 introduced Kotlin support. Instead of the Javadoc Doclet one has to use the Dokka extension
spring-auto-restdocs-dokka-json
. The extension is only available as 2.0.x, but works with both version of Spring Auto REST Docs. Take a look at the example. -
Which Java versions are supported?
The baseline is Java 8. Our focus is on the LTS versions 8 and 11, but we also try to support the latest Java release. One has to use a different Javadoc Doclet starting at Java 9. See Getting Started.
-
Does it work with REST Assured tests?
Not at the moment. If you want this, your PR is welcome.
-
Is Jackson required for automatic field documentation?
Yes, this project only includes a Jackson visitor so far.
-
Is a multi-module project setup supported?
The JSON doclet and the REST Docs snippets produce files. Therefore, all files should be placed on the filesystem. JSON files will be additionally looked up from classpath, so modularization of codebase is fully supported. For advanced customization, multiple source directories for the JSON files can be configured (see usage).
-
Can Spring REST Docs and Spring Auto REST Docs features be combined?
Yes, this even works in the same test. One can generate Spring REST Docs snippets and Spring Auto REST Docs snippets out of the same test. This is demonstrated in the example project.
-
Can I regenerate the final HTML without running all tests?
Yes, if you do not need the snippets generated out of the test to be updated, Asciidoctor can be executed standalone. With Maven you can run
mvn asciidoctor:process-asciidoc
(whereprocess-asciidoc
is the goal specified in your Maven POM and might differ). With Gradle it can be done via./gradlew asciidoctor
ifasciidoctor
is the name of the Gradle task in your setup. You might have to removedependsOn test
depending on your setup to run it standalone. -
Is HATEOAS supported?
Yes, there is basic support for links and embedded resources.
-
What should I do if I get a
NoSuchBeanDefinitionException: No bean named 'webHandler' available
exception?This means that the servlet stack instead if the reactive one is active. Make sure that
@EnableWebFlux
is used.
Getting started
Requirements
Spring Auto REST Docs has the following minimum requirements:
-
Java 8 or Kotlin 1.3
-
Spring REST Docs 2.0.5.RELEASE (see documentation)
-
Jackson has to be used for creating and parsing JSON
For Java 7 and Spring REST Docs 1.2.x support, please use the 1.0.x version of Spring Auto REST Docs.
For Java 9+ support, use spring-auto-restdocs-json-doclet-jdk9
as doclet dependency.
Our support is focused on the latest Kotlin version, Java 8 and 11.
In addition, we try to support the latest Java version that is supported by Spring.
Usage
-
Setup project for Spring REST Docs
-
Additional configuration for this extension:
Maven<dependency> <groupId>capital.scalable</groupId> <artifactId>spring-auto-restdocs-core</artifactId> <version>2.0.11</version> <scope>test</scope> </dependency> <build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> <includes> <include>**/*Test.java</include> </includes> <systemPropertyVariables> <org.springframework.restdocs.outputDir> ${project.build.directory}/generated-snippets (1) </org.springframework.restdocs.outputDir> <org.springframework.restdocs.javadocJsonDir> ${project.build.directory}/generated-javadoc-json (2) </org.springframework.restdocs.javadocJsonDir> </systemPropertyVariables> </configuration> </plugin> <plugin> <artifactId>maven-javadoc-plugin</artifactId> <extensions>true</extensions> <executions> <execution> <id>generate-javadoc-json</id> <phase>compile</phase> <goals> <goal>javadoc-no-fork</goal> </goals> <configuration> <doclet>capital.scalable.restdocs.jsondoclet.ExtractDocumentationAsJsonDoclet</doclet> <docletArtifact> <groupId>capital.scalable</groupId> <artifactId>spring-auto-restdocs-json-doclet</artifactId> (3) <version>2.0.11</version> </docletArtifact> <destDir>generated-javadoc-json</destDir> (2) <reportOutputDirectory>${project.build.directory}</reportOutputDirectory> (2) <useStandardDocletOptions>false</useStandardDocletOptions> <show>package</show> </configuration> </execution> </executions> </plugin> ... </plugins> </build>
1 (Optional) Determines directory where snippets are saved. Defaults to generated-snippets
in build directory.2 (Optional) Determines where JSON files are saved. Defaults to generated-javadoc-json
in build directory. Multiple directories can be listed by separating them with,
. The directories are processed in order and only the first found JSON file is used.3 For Java 9/10/11 support, use spring-auto-restdocs-json-doclet-jdk9
as doclet dependency.Gradleconfigurations { jsondoclet } ext { javadocJsonDir = file("$buildDir/generated-javadoc-json") (2) } dependencies { testCompile group: 'capital.scalable', name: 'spring-auto-restdocs-core', version: '2.0.11' jsondoclet group: 'capital.scalable', name: 'spring-auto-restdocs-json-doclet', version: '2.0.11' (3) } task jsonDoclet(type: Javadoc, dependsOn: compileJava) { source = sourceSets.main.allJava classpath = sourceSets.main.compileClasspath destinationDir = javadocJsonDir (2) options.docletpath = configurations.jsondoclet.files.asType(List) options.doclet = 'capital.scalable.restdocs.jsondoclet.ExtractDocumentationAsJsonDoclet' options.memberLevel = JavadocMemberLevel.PACKAGE } test { systemProperty 'org.springframework.restdocs.outputDir', snippetsDir (1) systemProperty 'org.springframework.restdocs.javadocJsonDir', javadocJsonDir (2) dependsOn jsonDoclet } jar { dependsOn asciidoctor }
1 (Optional) Determines directory where snippets are saved. Defaults to generated-snippets
in build directory.2 (Optional) Determines where JSON files are saved. Defaults to generated-javadoc-json
in build directory. Multiple directories can be listed by separating them with,
. The directories are processed in order and only the first found JSON file is used.3 For Java 9/10/11 support, use spring-auto-restdocs-json-doclet-jdk9
as doclet dependency. -
Configure MockMvc or WebTestClient
MockMvc Test class@Autowired private WebApplicationContext context; @Autowired protected ObjectMapper objectMapper; protected MockMvc mockMvc; @Rule public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); @Before public void setUp() throws Exception { this.mockMvc = MockMvcBuilders .webAppContextSetup(context) .addFilters(springSecurityFilterChain) .alwaysDo(JacksonResultHandlers.prepareJackson(objectMapper)) .alwaysDo(MockMvcRestDocumentation.document("{class-name}/{method-name}", Preprocessors.preprocessRequest(), Preprocessors.preprocessResponse( ResponseModifyingPreprocessors.replaceBinaryContent(), ResponseModifyingPreprocessors.limitJsonArrayLength(objectMapper), Preprocessors.prettyPrint()))) .apply(MockMvcRestDocumentation.documentationConfiguration(restDocumentation) .uris() .withScheme("http") .withHost("localhost") .withPort(8080) .and().snippets() .withDefaults(CliDocumentation.curlRequest(), HttpDocumentation.httpRequest(), HttpDocumentation.httpResponse(), AutoDocumentation.requestFields(), AutoDocumentation.responseFields(), AutoDocumentation.pathParameters(), AutoDocumentation.requestParameters(), AutoDocumentation.description(), AutoDocumentation.methodAndPath(), AutoDocumentation.section())) .build(); }
WebTestClient Test class@Autowired private ApplicationContext context; protected WebTestClient webTestClient; @Rule public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); @Before public void setUp() throws Exception { this.webTestClient = WebTestClient .bindToApplicationContext(context) .apply(springSecurity()) .configureClient() .baseUrl("http://localhost:8080/") .filter(documentationConfiguration(restDocumentation) .snippets() .withDefaults(WebTestClientInitializer.prepareSnippets(context), CliDocumentation.curlRequest(), HttpDocumentation.httpRequest(), HttpDocumentation.httpResponse(), AutoDocumentation.requestFields(), AutoDocumentation.responseFields(), AutoDocumentation.pathParameters(), AutoDocumentation.requestParameters(), AutoDocumentation.description(), AutoDocumentation.methodAndPath(), AutoDocumentation.section())) .build(); }
Snapshot build
If you want to experiment with snapshot builds, add this repository:
<repositories>
<repository>
<id>sonatype-snapshots</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
repositories {
mavenCentral()
maven {
url "https://oss.sonatype.org/content/repositories/snapshots"
}
}
Sample applications
This projects includes a Java Web MVC example, a Java WebFlux example and a Kotlin Web MVC example application that demonstrate most features.
The generated documentation can be viewed for the Java Web MVC example, the Java WebFlux example and the Kotlin Web MCV example.
Snippets
Method and path snippet
Method and path snippet takes a value
(path) and method
from RequestMapping
annotation on the Controller’s method.
The shorthand annotations GetMapping
, PostMapping
, etc. are supported as well.
@GetMapping(value = "/items")
public ItemResponse getItems() { ... }
GET /items
Description snippet
Description snippet outputs the Controller’s method’s Javadoc.
Currently only basic br
, p
, ul
and li
tags are supported.
/**
* Returns list of all items.
* <p>
* Use query parameters to filter the list by:
* <ul>
* <li>orderNumber</li>
* <li>type</li>
* </ul>
*/
@GetMapping...
public ItemResponse getItems() { ... }
Returns list of all items. Use query parameters to filter the list by: - orderNumber - type
Authorization snippet
Authorization snippet does not help in adding authorization to your tests, but it makes it easy to document your authorization information.
Usually, a custom RequestPostProcessor
is used to add authorization and this is also the place where you add the documentation.
protected RequestPostProcessor userToken() {
return new RequestPostProcessor() {
@Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
// <Code to get access token>
request.addHeader("Authorization", "Bearer " + accessToken);
return documentAuthorization(request, "User access token required.");
}
};
}
@Test
public void addItem() throws Exception {
mockMvc.perform(post("/items").with(userToken())
...
}
User access token required.
Path parameters snippet
Path parameters snippet automatically lists parameters in the path including their type, whether they are optional, and their Javadoc with constraints.
/**
* @param id ID of the item.
*/
@GetMapping("{id}")
public ItemResponse getItem(@PathVariable String id) { ... }
Parameter | Type | Optional | Description |
---|---|---|---|
id |
String |
false |
ID of the item. |
Configuration
Configuration available during MockMvc setup:
-
failOnUndocumentedParams
- if build should fail on at least one undocumented parameter
Request parameters snippet
Request parameters snippet automatically lists query parameters including their type, whether they are optional, and their Javadoc with constraints. If a default value is set, it will also be included.
/**
* @param descMatch Lookup on description field.
* @param lang Lookup on language.
*/
@GetMapping("search")
public Page<ItemResponse> searchItem(
@RequestParam("desc") String descMatch,
@RequestParam(value= "language", required = false, defaultValue = "en") String lang
) { ... }
Parameter | Type | Optional | Description |
---|---|---|---|
desc |
String |
false |
Lookup on description field. |
language |
String |
true |
Lookup on language. Default value: "en". |
Configuration
Configuration available during MockMvc setup:
-
failOnUndocumentedParams
- if build should fail on at least one undocumented parameter
Model Attribute snippet
Model Attribute snippet automatically recognises POJOs annotated with @ModelAttribute
annotation. Depending on HTTP method, it generates either section for request parameters (GET
) or request fields (non GET
). This snippet works also for model attributes without annotation, as specified in "any other argument" section of Spring MVC reference documentation.
@GetMapping("search")
public SearchResult search(@ModelAttribute SearchCriteria criteria){ ... }
class SearchCriteria {
/** Lookup on description field. */
@NotBlank
String descMatch;
/** Lookup on language. */
String lang;
}
Configuration
Configuration available during MockMvc setup:
-
failOnUndocumentedFields
- if build should fail on at least one undocumented field
Request headers snippet
Request headers snippet automatically lists headers with their type, whether they are optional, and their Javadoc with constraints. If a default value is set, it will also be included.
/**
* @param lang Language we want the response in.
*/
@GetMapping("/all")
public ItemResponse getItem(
@RequestHeader(value = "Accept-Language", required = false, defaultValue = "en") String lang
) { ... }
Parameter | Type | Optional | Description |
---|---|---|---|
Accept-Language |
String |
true |
Language we want the response in. Default value: "en". |
Request fields snippet
Request fields snippet automatically lists all fields of the request class, determined from @RequestBody
parameter of Controller’s method.
List includes name, type, whether the field is optional, and its Javadoc with constraints.
@PostMapping
public void addItem(@RequestBody ItemUpdateRequest request) { ... }
static class ItemUpdateRequest {
/**
* Item ID.
*/
@NotBlank
private Integer id;
/**
* Item description.
*/
@Size(max = 20)
private String description;
}
Path | Type | Optional | Description |
---|---|---|---|
id |
Integer |
false |
Item ID. |
description |
String |
true |
Item description. Size must be between 0 and 20 inclusive. |
Configuration
Configuration available during MockMvc setup:
-
failOnUndocumentedFields
- if build should fail on at least one undocumented field -
requestBodyAsType
- specified class should be considered as request type instead of endpoint method’s parameter
Response fields snippet
Response fields snippet automatically lists all fields of the response class, determined from return value of Controller’s method. List includes name, type, whether the field is optional, and its Javadoc with constraints.
@GetMapping("{id}")
public ItemResponse getItem(@PathVariable String id) { ... }
static class ItemResponse {
/**
* Unique item ID.
*/
@NotBlank
private String id;
/**
* Item's order number.
*/
@Min(1)
private Integer orderNumber;
}
Path | Type | Optional | Description |
---|---|---|---|
id |
Integer |
false |
Unique item ID. |
orderNumber |
Integer |
true |
Item’s order number. Must be at least 1. |
Configuration
Configuration available during MockMvc setup:
-
failOnUndocumentedFields
- if build should fail on at least one undocumented field -
responseBodyAsType
- specified class should be considered as response type instead of endpoint method’s return type
Section snippet
Section snippets only work if AsciiDoc is used. Markdown is not supported, because Markdown has no includes. |
The section snippet combines most common snippets into one convenient file. It helps you being even more lazy, because a single line of AsciiDoc is sufficient to document one endpoint. Assuming of course that you already documented your code with Javadoc.
include::{snippets}/your-endpoint/auto-section.adoc[]
[[resources-{{link}}]]
=== {{title}}
include::auto-method-path.adoc[]
include::auto-description.adoc[]
{{#sections}}
==== {{header}}
{{#filenames}}
include::{{.}}.adoc[]
{{/filenames}}
{{/sections}}
Placeholder | Example | Description |
---|---|---|
{link} |
items-id |
Derived from the documentation string. For example: |
{title} |
Get Item By Id |
See Section title. |
{snippets} |
target/generated-snippets |
Base snippet folder configured via |
{sections} |
<iterable> |
Contains all sections (Snippet classes) which are configured to be included in the section snippet. Moustache templater iterates over this and expands into separate header and link lines. |
{header} |
Authorization |
Snippet header hard-coded inside Snippet class. |
{fileName} |
authorization |
Snippet name hard-coded inside Snippet class. |
Snippet configuration
By default, these snippets in the following order are linked inside section snippet:
Snippet name |
Example |
Description |
method-path |
|
Method and path snippet is used to determine the HTTP method and path (non-configurable). |
description |
Get item by ID. |
Description snippet reads the Javadoc on the method (non-configurable). |
authorization |
Resource is public. |
Authorization snippet can be used for custom authorization information, e.g. the OAuth role required for the endpoint |
path-parameters |
<table> |
Path parameters snippet automatically lists parameters in the path including their type, whether they are optional, and their Javadoc. |
request-parameters |
<table> |
Request parameters snippet automatically lists query parameters including their type, whether they are optional, and their Javadoc. |
request-fields |
<table> |
Request fields snippet automatically lists all fields of the request body including their type, whether they are optional, and their Javadoc. |
response-fields |
<table> |
Response fields snippet automatically lists all fields of the response body including their type, whether they are optional, and their Javadoc. |
curl-request |
|
The original cURL request snippet from Spring REST Docs is used here to provide a cURL example of the request. |
http-response |
|
The original HTTP response snippet from Spring REST Docs is used here to provide the HTTP response from the test. |
Customisation options
Sections and order
If you want to configure custom sections and their order, instead of using AutoDocumentation.section()
you can use
AutoDocumentation.sectionBuilder()
in the following manner:
AutoDocumentation.sectionBuilder()
.snippetNames( (1)
SnippetRegistry.PATH_PARAMETERS,
SnippetRegistry.REQUEST_PARAMETERS,
SnippetRegistry.REQUEST_FIELDS,
SnippetRegistry.RESPONSE_FIELDS)
.skipEmpty(true) (2)
.build()
1 | only selected snippets will be used in the specified order |
2 | whether snippets having no content should be skipped from section |
Section title
Section title uses text from @title
Javadoc tag on the handler method.
If missing, it’s derived from the method name, e.g. getItemById
→ Get Item By Id.
If you use @title
tag and want to generate regular Javadoc as well,
you need to include this tag among custom tags in plugin configuration.
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<tags>
<tag>
<name>title</name>
<placement>m</placement>
</tag>
</tags>
</configuration>
</execution>
</executions>
</plugin>
Custom snippet in section
If you want your custom snippet to be part of section snippet with automatically recognised header and include link (file name),
you have to implement SectionSupport interface.
Then, inside MockMvc configuration, add this snippet to the list of snippets using documentation configuration’s
builder withDefaults(…)
and include snippet name among SectionBuilder’s snippetNames(…)
.
Links snippet
Links snippet lists all possible links inside the response. In most cases, links cannot be determined from the response class, because they are highly dynamic depending on a resource state and often just stored in a Map. Therefore this snippet needs a custom class describing possible links, with field names acting as link _rel_s and field’s Javadoc and annotions used as usually to determine the comment, optionality, deprecation, etc. List includes name, whether the link is optional, and its Javadoc description.
@GetMapping
public ItemResponse getItem() {
ItemResponse response = new ItemResponse();
response.add(linkTo(methodOn(ItemResource.class).getItem()).withSelfRel());
return response;
}
static class ItemResponse extends org.springframework.hateoas.ResourceSupport {
...
}
The corresponding test method needs to be configured with documentation type:
@Test
public void getItem() throws Exception {
mockMvc.perform(get(...))
...
.andDo(links().documentationType(LinksDocumentation.class));
}
static class LinksDocumentation {
/**
* Link to this item.
*/
@NotNull
private String self;
/**
* Link to item's metadata.
*/
private String metadata;
}
Path | Optional | Description |
---|---|---|
self |
false |
Link to this item. |
metadata |
true |
Link to item’s metadata. |
Configuration
Configuration available during MockMvc setup:
-
failOnUndocumentedFields
- if build should fail on at least one undocumented field -
documentationType
- specified class should be considered for documentation
Embedded snippet
Embedded snippet lists all embedded resources of the response. Similarly to LinksSnippet, documentation is determined from custom documentation class. List includes name, type, whether the field is optional, and its Javadoc with constraints.
@GetMapping
public ItemResponse getItem() {
ItemResponse response = new ItemResponse();
response.embed(getAttributes());
response.embed(getMetadata());
return response;
}
static class ItemResponse extends EmbeddedSupport {
...
}
The corresponding test method needs to be configured with documentation type:
@Test
public void getItem() throws Exception {
mockMvc.perform(get(...))
...
.andDo(embedded().documentationType(EmbeddedDocumentation.class));
}
static class EmbeddedDocumentation {
/**
* Item's attributes.
*/
@NotNull
private Attributes attributes;
/**
* Item's metadata.
*/
private List<Metadata> metadata;
}
Path | Type | Optional | Description |
---|---|---|---|
attributes |
Object |
false |
Item’s attributes. |
metadata |
Array[Object] |
true |
Item’s metadata |
Configuration
Configuration available during MockMvc setup:
-
failOnUndocumentedFields
- if build should fail on at least one undocumented field -
documentationType
- specified class should be considered for documentation
Section snippet
Section snippets only work if AsciiDoc is used. Markdown is not supported, because Markdown has no includes. |
The section snippet combines most common snippets into one convenient file. It helps you being even more lazy, because a single line of AsciiDoc is sufficient to document one endpoint. Assuming of course that you already documented your code with Javadoc.
include::{snippets}/your-endpoint/auto-section.adoc[]
[[resources-{{link}}]]
=== {{title}}
include::auto-method-path.adoc[]
include::auto-description.adoc[]
{{#sections}}
==== {{header}}
{{#filenames}}
include::{{.}}.adoc[]
{{/filenames}}
{{/sections}}
Placeholder | Example | Description |
---|---|---|
{link} |
items-id |
Derived from the documentation string. For example: |
{title} |
Get Item By Id |
See Section title. |
{snippets} |
target/generated-snippets |
Base snippet folder configured via |
{sections} |
<iterable> |
Contains all sections (Snippet classes) which are configured to be included in the section snippet. Moustache templater iterates over this and expands into separate header and link lines. |
{header} |
Authorization |
Snippet header hard-coded inside Snippet class. |
{fileName} |
authorization |
Snippet name hard-coded inside Snippet class. |
Snippet configuration
By default, these snippets in the following order are linked inside section snippet:
Snippet name |
Example |
Description |
method-path |
|
Method and path snippet is used to determine the HTTP method and path (non-configurable). |
description |
Get item by ID. |
Description snippet reads the Javadoc on the method (non-configurable). |
authorization |
Resource is public. |
Authorization snippet can be used for custom authorization information, e.g. the OAuth role required for the endpoint |
path-parameters |
<table> |
Path parameters snippet automatically lists parameters in the path including their type, whether they are optional, and their Javadoc. |
request-parameters |
<table> |
Request parameters snippet automatically lists query parameters including their type, whether they are optional, and their Javadoc. |
request-fields |
<table> |
Request fields snippet automatically lists all fields of the request body including their type, whether they are optional, and their Javadoc. |
response-fields |
<table> |
Response fields snippet automatically lists all fields of the response body including their type, whether they are optional, and their Javadoc. |
curl-request |
|
The original cURL request snippet from Spring REST Docs is used here to provide a cURL example of the request. |
http-response |
|
The original HTTP response snippet from Spring REST Docs is used here to provide the HTTP response from the test. |
Customisation options
Sections and order
If you want to configure custom sections and their order, instead of using AutoDocumentation.section()
you can use
AutoDocumentation.sectionBuilder()
in the following manner:
AutoDocumentation.sectionBuilder()
.snippetNames( (1)
SnippetRegistry.PATH_PARAMETERS,
SnippetRegistry.REQUEST_PARAMETERS,
SnippetRegistry.REQUEST_FIELDS,
SnippetRegistry.RESPONSE_FIELDS)
.skipEmpty(true) (2)
.build()
1 | only selected snippets will be used in the specified order |
2 | whether snippets having no content should be skipped from section |
Section title
Section title uses text from @title
Javadoc tag on the handler method.
If missing, it’s derived from the method name, e.g. getItemById
→ Get Item By Id.
If you use @title
tag and want to generate regular Javadoc as well,
you need to include this tag among custom tags in plugin configuration.
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<tags>
<tag>
<name>title</name>
<placement>m</placement>
</tag>
</tags>
</configuration>
</execution>
</executions>
</plugin>
Custom snippet in section
If you want your custom snippet to be part of section snippet with automatically recognised header and include link (file name),
you have to implement SectionSupport interface.
Then, inside MockMvc configuration, add this snippet to the list of snippets using documentation configuration’s
builder withDefaults(…)
and include snippet name among SectionBuilder’s snippetNames(…)
.
Custom snippet
Custom snippets are created by subclassing the Snippet interface
(or more conveniently TemplatedSnippet).
Template for the snippet must be put into org/springframework/restdocs/templates/asciidoctor
(optionally also org/springframework/restdocs/templates/markdown
) resource package. The template name
must correspond with the snippet name passed to the class constructor.
You can also include your custom snippet in a section.
Snippet customization
Provide your own template in org/springframework/restdocs/templates/asciidoctor
(or markdown
) package of your resources.
You can override REST Docs templates
and Auto REST Docs templates.
Important is to omit default-
part from the template name so that the resolution algorithm will prefer this custom template.
See also original documentation for additional details.
Documenting constraints
Spring REST Docs has support for documenting constraints, but using them requires a bit of work and you have to decide on how to include them. Here we are going a step further and automatically use constraints and the constraint groups to derive whether a field is optional and to add the constraint descriptions to the field descriptions.
/**
* Username
*/
@NotNull
@Size(min = 2, max = 50)
private String username;
Parameter | Type | Optional | Description |
---|---|---|---|
username |
String |
false |
Username. Size must be between 2 and 50. |
Constraints that determine optionality
The following three constraints are used to determine whether a field is optional. Their descriptions are not included in the field descriptions.
-
NotNull
-
NotEmpty
-
NotBlank
Along with JSR-303 annotations, optionality is also determined based on:
-
required
parameter of@RequestParam
-
field type being
java.util.Optional
-
Koltin field being non-nullable
Custom constraints
Descriptions for custom constraints can be provided in org.springframework.restdocs.constraints.ConstraintDescriptions.properties
.
If this file can be loaded as a resource, descriptions are extracted.
Each description is a property where the key is the full class name of the annotation suffixed with .description
.
The value is the description and can contain placeholders for annotation methods,
e.g. ${value}
to get the content of value()
.
For more details, see original documentation of here.
Example for the constraint annotation myproject.constraints.OneOf
:
myproject.constraints.OneOf.description=Must be one of ${value}
Enums
Constraint message for enum is appended to description field as list of allowed values, e.g. Must be one of [A, B]
.
If you want to customize this message, use above mentioned ConstraintDescriptions.properties
approach.
The list of values is represented by the ${value}
placeholder.
Readable constraint values
Spring REST Docs just calls toString()
objects extracted from
the constraint for documentation.
Here we do a bit more on the following two types:
-
Array: The elements are extracted and
toString()
is called on each element. The resulting strings are wrapped in brackets and separated by commas, i.e.[element1.toString(), element2.toString(), …]
. -
Class: An instance with the default constructor is created and
toString()
is called on the instance.
Constraint groups
Constraint groups are also included in the documentation if a description is provided for them. The descriptions are provided in the same way as for custom constraints.
Examples
Descriptions for the examples
myproject.constraints.groups.German.description=DE: ${value}
myproject.constraints.groups.English.description=EN: ${value}
myproject.constraints.OneOf.description=Must be one of ${value}
Optionality description
@NotNull(groups = English.class)
private String field;
Parameter | Type | Optional | Description |
---|---|---|---|
field |
String |
true |
Field description example
@OneOf(value = {"big", "small"}, groups = English.class)
private String field;
Parameter | Type | Optional | Description |
---|---|---|---|
field |
String |
true |
EN: Must be one of [big, small] |
Other
Paging support
Paging support in an endpoint is automatically recognized if request handler method contains Pageable
parameter
and response is of type Page
. This is then mentioned in request and response snippets by linking
to common section explaining the details of supported paging parameters/fields.
This section has to be created manually and must be tagged as [[overview-pagination]]
.
See example project for paging section template.
Preprocessors
Preprocessors are available for use in order to improve quality of endpoint examples.
-
binary replacement: replaces content with
<binary>
for common mime types -
multipart binary replacement: replaces content with
<binary>
for multipart/form-data requests -
limit JSON array length: limits all JSON arrays to 3 items
For a list of standard preprocessors see Preprocessors.
Deprecation support
Endpoint or field will be marked as deprecated if at least one of these is present:
-
method / field is annotated with
@Deprecated
-
method’s / field’s Javadoc has
@deprecated
tag
Deprecation is shown in the title of an endpoint (also visible in TOC) and description.
Field’s deprecation is shown in description column of request/response table.
See how it looks in the example project.
See tag support
@see
Javadoc tag comment will be added to the main description section of an endpoint or a field.
Currently supports only HTML links. Does not support Java code references.
See how it looks in the example project.
Localization support
For custom translations of section titles, table headers and other messages:
-
Create a file
SnippetMessages.properties
incapital.scalable.restdocs.i18n
package. -
Add translations to the file using keys from DefaultSnippetMessages.
Recursive structures support
To prevent infinite loop in recursive structures, annotate the recursive field with the @RestdocsNotExpanded
annotation
from the spring-auto-restdocs-annotations
maven module.
Subtypes
There are two forms of subtypes supported, automatic via Jackson annotations and manual via MockMvc configuration. You can even combine them together if necessary.
Jackson subtypes
Standard JsonSubTypes
annotation and it’s configuration is supported.
@JsonTypeInfo(use = NAME, include = PROPERTY, property = "type", visible = true)
@JsonSubTypes({
@JsonSubTypes.Type(value = Sub1.class, name = "1"),
@JsonSubTypes.Type(value = Sub2.class, name = "2")
})
abstract class Parent {
private String type;
private String common;
}
class Sub1 extends Parent {
private Boolean sub1;
}
class Sub2 extends Parent {
private Integer sub2;
}
Custom subtypes
If for any reason Jackson annotations are not desired or possible, you can configure completely custom subtypes
via MockMvc using standard JacksonResultHandlers.prepareJackson
result handler with custom TypeMapping
.
.alwaysDo(prepareJackson(objectMapper, new TypeMapping()
.mapSubtypes(Parent.class, Sub1.class, Sub2.class)))
Discriminator text
For better understanding which field is included when in the final documentation, ConstraintDescriptions.properties can be used to provide a message, which will be then applied to all fields coming from that particular subtype.
mypackage.Sub1.description=(available when type=2)
Integrations
WireMock
WireMock stubs can be generated with Spring REST Docs, see Generating Stubs using REST Docs. This also works with Spring Auto REST Docs. However, the corresponding snippet does not register automatically and thus has to be registered manually.
The following code shows how the new WireMockSnippet()
can be registered.
public void setUp() {
this.mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.alwaysDo(prepareJackson(objectMapper))
.alwaysDo(document("{class-name}/{method-name}",
preprocessRequest(), commonResponsePreprocessor()))
.apply(documentationConfiguration(restDocumentation)
.uris()
.and().snippets()
.withDefaults(curlRequest(), httpRequest(), httpResponse(),
requestFields(), responseFields(), pathParameters(),
requestParameters(), description(), methodAndPath(),
section(), new WireMockSnippet()))
.build();
}
Contributing
Pull Requests
For code formatting please use intellij-codestyle.xml.
Building from source
Install Spring REST Docs test JAR
The Spring REST Docs test JAR is not published via Maven, but this project relies on it. If you want to build this project yourself, you need to install the test JAR. The test JAR is located in the lib folder and can be installed as follows:
mvn install:install-file -Dfile=lib/spring-restdocs-core-2.0.5.RELEASE-test.jar \
-DgroupId=org.springframework.restdocs -DartifactId=spring-restdocs-core \
-Dversion=2.0.5.RELEASE -Dpackaging=test-jar -Dclassifier=test
Install Dokka Core test JAR
Dokka publishes no test JAR, but the Spring Auto REST Docs Dokka extension uses their test utilities. If you want to build this project yourself, you need to install the test JAR. The test JAR is located in the lib folder and can be installed as follows:
mvn install:install-file -Dfile=lib/dokka-core-0.10.1-tests.jar \
-DgroupId=org.jetbrains.dokka -DartifactId=dokka-core -Dversion=0.10.1 \
-Dpackaging=jar -Dclassifier=test
Build Spring REST Docs test JAR
Building the Spring REST Docs test JAR is not required to build the project, but if you ever want to upgrade the version of Spring REST Docs in this project this step has to be done.
We use version 2.0.5.RELEASE of Spring REST Docs in this example.
You find the currently required version in pom.xml
:
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-core</artifactId>
<version>2.0.5.RELEASE</version>
<classifier>test</classifier>
<scope>test</scope>
</dependency>
Clone and build a specific version of Spring REST Docs:
git clone git@github.com:spring-projects/spring-restdocs.git
cd spring-restdocs
git fetch --tags
git checkout tags/v2.0.5.RELEASE
./gradlew build
Afterwards the test JAR is located at
spring-restdocs/spring-restdocs-core/build/libs/spring-restdocs-core-2.0.5.RELEASE-test.jar
and has to be installed with the Maven command shown in the section above.
Build Dokka Core test JAR
Building the Dokka core test JAR is not required to build the project, but if you ever want to upgrade the version of Dokka in this project this step has to be done.
We use version 0.10.1 of Dokka in this example.
Clone and checkout a specific version of Dokka:
git clone git@github.com:Kotlin/dokka.git
cd dokka
git checkout 0.10.1
Add the following snippet at the end of core/build.gradle
:
task testJar(type: Jar) {
classifier = 'tests'
from sourceSets.test.output
}
To speed the build up and avoid test issues, once can reduce the list of sub-projects in
settings.gradle
to just core
and integration
.
Make sure to use JDK 8 for the build. Create the test JAR by running the following command on the top-level:
./gradlew testJar
Afterwards the test JAR is located at
core/build/libs/core-0.10.1-SNAPSHOT-tests.jar
Build
mvn install