Skip to content

Marthijs-Berfelo/restdocs-raml

 
 

Repository files navigation

Spring REST Docs RAML Integration

Build Status Coverage Status Download Join the chat at https://gitter.im/restdocs-raml/restdocs-raml

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.

Motivation

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.

Getting started

Project structure

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 one RAML file for the whole project.

Build configuration

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"
}
  1. add jcenter repository to buildscript to resolve the restdocs-raml-gradle-plugin
  2. add the dependency to restdocs-raml-gradle-plugin
  3. apply restdocs-raml-gradle-plugin
  4. add repositories used for dependency resolution. We need jcenter for restdocs-raml and jitpack for one of the dependencies we use internally
  5. add the actual restdocs-raml dependency to the test scope
  6. spring-boot specifies an old version of org.json:json. We use everit-org/json-schema to generate json schema files. This project depends on a newer version of org.json:json. As versions from BOM always override transitive versions coming in through maven dependencies, you need to add an explicit dependency to org.json:json:20170516
  7. 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.

Usage with Spring REST Docs

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"
    }
  }
}

⚠️ Use 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))

Migrate existing tests

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.

Compatibility with Spring Boot 2 (WebTestClient)

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.

Running the gradle plugin

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

Gradle plugin configuration

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

Generate HTML

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.

⚠️ the current version of raml2html is only working with RAML 1.0 - you have to fall back to raml2html version 3 - npm install -g [email protected]

Limitations

Rest Assured

Spring REST Docs also supports REST Assured to write tests that produce documentation. We currently have not tried REST Assured with our project.

Maven plugin

Currently only a gradle plugin exists to aggregate the RAML fragments.

About

Adds RAML support for Spring REST Docs

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Java 90.5%
  • Groovy 9.4%
  • Shell 0.1%