This is an extension that adds RAML (RESTful API Modeling Language) as an output format to Spring REST Docs.
Check out our introductory blog post for a quick overview.
Spring REST Docs is a great tool to produce documentation for your RESTful services that is accurate and readable.
It offers support for AsciiDoc and Markdown. This is great for generating simple HTML-based documentation. But both are markup languages and thus it is hard to get any further than statically generated HTML.
RAML is a lot more flexible. With RAML you get a machine-readable description of your API. There is a rich ecosystem around it that contains tools to:
- generate a HTML representation of your API - raml2html
- interact with your API using an API console - api-console
Also, RAML is supported by many REST clients like Postman and Paw. Thus having a RAML representation of an API is a great plus when starting to work with it.
The project consists of two components:
- restdocs-raml - contains the actual Spring REST Docs extension. This is most importantly the RamlResourceDocumentation which is the entrypoint to use the extension in your tests. The RamlResourceSnippet is the snippet generating a RAML fragment for each documenated resource.
- restdocs-raml-gradle-plugin - adds a gradle plugin that aggregates the RAML fragment produced by
RamlResourceSnippet
into oneRAML
file for the whole project.
buildscript {
repositories {
//..
jcenter() //1
}
dependencies {
//..
classpath 'com.epages:restdocs-raml-gradle-plugin:0.2.10' //2
}
}
//..
apply plugin: 'com.epages.restdocs-raml' //3
repositories { //4
jcenter()
maven { url 'https://jitpack.io' }
}
//..
dependencies {
//..
testCompile 'com.epages:restdocs-raml:0.2.10' //5
testCompile 'org.json:json:20170516' //6
}
ramldoc { //7
apiTitle = 'Notes API'
apiBaseUri = 'http://localhost:8080/'
ramlVersion = "1.0"
}
- add jcenter repository to
buildscript
to resolve therestdocs-raml-gradle-plugin
- add the dependency to
restdocs-raml-gradle-plugin
- apply
restdocs-raml-gradle-plugin
- add repositories used for dependency resolution. We need
jcenter
forrestdocs-raml
andjitpack
for one of the dependencies we use internally - add the actual
restdocs-raml
dependency to the test scope spring-boot
specifies an old version oforg.json:json
. We use everit-org/json-schema to generate json schema files. This project depends on a newer version oforg.json:json
. As versions from BOM always override transitive versions coming in through maven dependencies, you need to add an explicit dependency toorg.json:json:20170516
- optional - add configuration options for restdocs-raml-gradle-plugin see Gradle plugin configuration
See the build.gradle for the setup used in the sample project.
The class RamlResourceDocumentation contains the entrypoint for using the RamlResourceSnippet.
The most basic form does not take any parameters:
mockMvc
.perform(get("/notes"))
.andExpect(status().isOk())
.andDo(document("notes-list", ramlResource()));
This test will produce the following files in the output directory:
- raml-resource.raml
- notes-list-response.json
The raml-resource.raml
is the raml fragment describing this resource.
/notes:
get:
description: notes-list
responses:
200:
body:
application/hal+json:
example: !include notes-list-response.json
Just like you are used to do with Spring REST Docs we can also describe request fields, response fields, path variables, parameters, and links.
Furthermore you can add a text description for your resource.
The extension also discovers JWT
tokens in the Authorization
header and will document the required scopes from it.
The following example uses RamlResourceSnippetParameters
to document response fields and links.
We paid close attention to keep the API as similar as possible to what you already know from Spring REST Docs.
fieldWithPath
and linkWithRel
are actually still the static methods you would use in your using Spring REST Docs test.
mockMvc
.perform(get("/notes/{id}", noteId))
.andExpect(status().isOk())
.andDo(document("notes-get",
ramlResource(RamlResourceSnippetParameters.builder()
.description("Get a note by id")
.pathParameters(parameterWithName("id").description("The note id"))
.responseFields(
fieldWithPath("title").description("The title of the note"),
fieldWithPath("body").description("The body of the note"),
fieldWithPath("_links").description("Links to other resources"))
.links(
linkWithRel("self").description("This self reference"),
linkWithRel("note-tags").description("The link to the tags associated with this note"))
.build()))
);
In this case there is one additional file generated - notes-get-schema-response.json
- which contains the json schema generated form the documented response fields and the links.
The raml-resource.raml
fragment would look like this.
/notes/{id}:
uriParameters:
id:
description: The note id
type: string
get:
description: Get a note by id
responses:
200:
body:
application/hal+json:
schema: !include notes-get-schema-response.json
example: !include notes-get-response.json
It now carries a reference to the json schema.
{
"type" : "object",
"properties" : {
"_links" : {
"description" : "Links to other resources",
"type" : "object",
"properties" : {
"note-tags" : {
"description" : "The link to the tags associated with this note",
"type" : "object",
"properties" : {
"href" : {
"type" : "string"
}
}
},
"self" : {
"description" : "This self reference",
"type" : "object",
"properties" : {
"href" : {
"type" : "string"
}
}
}
}
},
"body" : {
"description" : "The body of the note",
"type" : "string"
},
"title" : {
"description" : "The title of the note",
"type" : "string"
}
}
}
template URIs
to refer to path variables in your request
Note how we use the urlTemplate
to build the request with RestDocumentationRequestBuilders
. This makes the urlTemplate
available in the snippet and we can render it into the RAML
fragments.
mockMvc.perform(get("/notes/{id}", noteId))
For convenience when applying restdocs-raml
to an existing project that uses Spring REST Docs, we introduced RamlDocumentation.
In you tests you can just replace calls to MockMvcRestDocumentation.document
with the corresponding variant of RamlDocumentation.document
.
It will always execute the specified snippets and also add a RamlResourceSnippet
equipped with the input from your snippets.
Here is an example:
resultActions
.andDo(
RamlDocumentation.document(operationName,
requestFields(fieldDescriptors().getFieldDescriptors()),
responseFields(
fieldWithPath("comment").description("the comment"),
fieldWithPath("flag").description("the flag"),
fieldWithPath("count").description("the count"),
fieldWithPath("id").description("id"),
fieldWithPath("_links").ignored()
),
links(linkWithRel("self").description("some"))
)
);
This will do exactly the same as using MockMvcRestDocumentation.document
without restdocs-raml
.
Additionally it will add a RamlResourceSnippet
with the descriptors you provided in the RequestFieldsSnippet
, ResponseFieldsSnippet
, and LinksSnippet
.
restdocs-raml
is compatible with Spring REST Docs 2 and the new WebTestClient
since version 0.2.3
.
We adopted the Spring REST Docs sample project that shows the usage of WebTestClient
to use restdocs-raml
to verify the compatibility.
See https://github.com/mduesterhoeft/spring-restdocs/tree/master/samples/web-test-client.
restdocs-raml-gradle-plugin
is responsible for picking up the generated RAML
fragments and aggregate them into a set of top-level RAML
files that describe the complete API.
For this purpose we use the ramldoc
task:
./gradlew ramldoc
For our sample project this creates the following files in the output directory (build/ramldoc
).
.
├── api-public.raml
├── api.raml
├── notes-create-request.json
├── notes-create-schema-request.json
├── notes-get-response.json
├── notes-get-schema-response.json
├── notes-list-response.json
├── notes-list-schema-response.json
├── notes-public.raml
├── notes.raml
├── tags-create-request.json
├── tags-create-schema-request.json
├── tags-get-response.json
├── tags-get-schema-response.json
├── tags-list-response.json
├── tags-list-schema-response.json
├── tags-patch-request.json
├── tags-patch-schema-request.json
├── tags-public.raml
└── tags.raml
api.raml
is the top-level RAML
file.
api-public.raml
does not contain the resources that you have marked as private using RamlResourceSnippetParameters.privateResource
.
The file api-public.raml
if you set separatePublicApi
property to true (see Gradle plugin configuration)
api.raml
#%RAML 1.0
title: Notes API
baseUri: http://localhost:8080/
/tags: !include tags.raml
/notes: !include notes.raml
The top-level file just links to the top-level resources - here we have tags
and notes
.
notes.raml
post:
description: notes-create
body:
application/hal+json:
type: !include notes-create-schema-request.json
example: !include notes-create-request.json
/{id}:
patch:
description: tags-patch
body:
application/hal+json:
type: !include tags-patch-schema-request.json
example: !include tags-patch-request.json
get:
description: notes-get
responses:
200:
body:
application/hal+json:
type: !include notes-get-schema-response.json
example: !include notes-get-response.json
The restdocs-raml-gradle-plugin
takes the following configuration options - all are optional.
Name | Description | Default value |
---|---|---|
apiTitle | The title of the generated top-level RAML file |
empty |
apiBaseUri | The base uri added to the top-level RAML file |
empty |
ramlVersion | RAML version header - 0.8 or 1.0 |
1.0 |
separatePublicApi | Should the plugin generate an additional api-public.raml that does not contain the resources marked as private |
false |
outputDirectory | The output directory | build/ramldoc |
snippetsDirectory | The directory Spring REST Docs generated the snippets to | build/generated-snippets |
We can use raml2html to generate HTML out of our RAML
file.
Our sample project contains a setup to do this via docker. We use the gradle-docker-plugin and a docker image containing raml2html
.
So you can generate HTML for our sample project using
cd restdocs-raml-sample
./gradlew raml2html
You find the HTML file under build/ramldoc/api.raml.html
after running the gradle task.
npm install -g [email protected]
Spring REST Docs also supports REST Assured to write tests that produce documentation. We currently have not tried REST Assured with our project.
Currently only a gradle plugin exists to aggregate the RAML
fragments.