Add reference documentation and create docs zip

Add reference documentation for the project and add a docs artifact
that contains both the reference documentation and the javadoc.

See gh-37
This commit is contained in:
Andy Wilkinson
2015-05-21 16:44:44 +01:00
parent f98bec317e
commit 8307fb5428
27 changed files with 1322 additions and 199 deletions

View File

@@ -1,87 +1,40 @@
project(':spring-restdocs') {
ext {
hamcrestVersion = '1.3'
jacksonVersion = '2.3.4'
jacocoVersion = '0.7.2.201409121644'
junitVersion = '4.11'
servletApiVersion = '3.1.0'
springHateoasVersion = '0.17.0.RELEASE'
springVersion = '4.1.4.RELEASE'
mockitoVersion = '1.10.19'
}
group = 'org.springframework.restdocs'
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'maven'
apply plugin: 'sonar-runner'
sonarRunner {
sonarProperties {
property 'sonar.jacoco.reportPath', "${buildDir.name}/jacoco.exec"
property 'sonar.java.coveragePlugin', 'jacoco'
property 'sonar.links.ci', 'https://build.spring.io/browse/SRD'
property 'sonar.links.homepage', 'https://github.com/spring-projects/spring-restdocs'
property 'sonar.links.issue', 'https://github.com/spring-projects/spring-restdocs'
property 'sonar.links.scm', 'https://github.com/spring-projects/spring-restdocs'
}
}
configurations {
jacoco
}
sourceCompatibility = 1.7
targetCompatibility = 1.7
buildscript {
repositories {
jcenter()
}
task sourcesJar(type: Jar) {
classifier = 'sources'
from project.sourceSets.main.allSource
}
task javadocJar(type: Jar) {
classifier = "javadoc"
from javadoc
}
artifacts {
archives sourcesJar
archives javadocJar
}
eclipseJdt.onlyIf { false }
cleanEclipseJdt.onlyIf { false }
dependencies {
compile "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
compile "junit:junit:$junitVersion"
compile "org.springframework:spring-test:$springVersion"
compile "org.springframework:spring-web:$springVersion"
compile "org.springframework:spring-webmvc:$springVersion"
compile "javax.servlet:javax.servlet-api:$servletApiVersion"
jacoco "org.jacoco:org.jacoco.agent:$jacocoVersion:runtime"
testCompile "org.springframework.hateoas:spring-hateoas:$springHateoasVersion"
testCompile "org.mockito:mockito-core:$mockitoVersion"
testCompile "org.hamcrest:hamcrest-core:$hamcrestVersion"
testCompile "org.hamcrest:hamcrest-library:$hamcrestVersion"
}
test {
jvmArgs "-javaagent:${configurations.jacoco.asPath}=destfile=${buildDir}/jacoco.exec,includes=org.springframework.restdocs.*"
testLogging {
exceptionFormat "full"
}
classpath 'io.spring.gradle:dependency-management-plugin:0.5.1.RELEASE'
}
}
apply plugin: 'samples'
ext {
springVersion = '4.1.5.RELEASE'
}
subprojects {
apply plugin: 'io.spring.dependency-management'
apply plugin: 'java'
apply plugin: 'eclipse'
dependencyManagement {
imports {
mavenBom "org.springframework:spring-framework-bom:$springVersion"
}
dependencies {
dependency 'com.fasterxml.jackson.core:jackson-databind:2.4.6'
dependency 'javax.servlet:javax.servlet-api:3.1.0'
dependency 'junit:junit:4.12'
dependency 'org.hamcrest:hamcrest-core:1.3'
dependency 'org.hamcrest:hamcrest-library:1.3'
dependency 'org.mockito:mockito-core:1.10.19'
dependency 'org.springframework.hateoas:spring-hateoas:0.17.0.RELEASE'
dependency 'org.jacoco:org.jacoco.agent:0.7.2.201409121644'
}
}
}
samples {
dependOn 'spring-restdocs:install'
@@ -94,6 +47,26 @@ samples {
}
}
wrapper {
gradleVersion = '2.3'
task docsZip(type: Zip, dependsOn: [':docs:asciidoctor', ':spring-restdocs:javadoc']) {
group = 'Distribution'
baseName = 'spring-restdocs'
classifier = 'docs'
description = 'Builds -${classifier} archive containing API and reference documentation'
destinationDir = file("${project.buildDir}/distributions")
from (project.tasks.findByPath(':docs:asciidoctor')) {
into 'reference'
}
from (project.tasks.findByPath(':spring-restdocs:javadoc')) {
into 'api'
}
}
configurations {
archives
}
artifacts {
archives docsZip
}

19
docs/build.gradle Normal file
View File

@@ -0,0 +1,19 @@
plugins {
id 'org.asciidoctor.convert' version '1.5.2'
}
dependencies {
compile 'org.springframework:spring-webmvc'
testCompile project(':spring-restdocs')
testCompile 'junit:junit'
testCompile 'org.springframework:spring-test'
}
tasks.findByPath("artifactoryPublish")?.enabled = false
asciidoctor {
sources {
include 'index.adoc'
}
}

View File

@@ -0,0 +1,67 @@
[[configuration]]
== Configuration
[[configuration-uris]]
=== Documented URIs
The default configuration for URIs documented by Spring REST docs is:
|===
|Setting |Default
|Scheme
|`http`
|Host
|`localhost`
|Port
|`8080`
|Context path
|Empty string
|===
This configuration is applied by `RestDocumentationConfigurer`. You can use its API to
change one or more of the defaults to suit your needs:
[source,java,indent=0]
----
include::{examples-dir}com/example/CustomUriConfiguration.java[tags=custom-uri-configuration]
----
[[configuration-snippet-encoding]]
=== Snippet encoding
The default encoding used by Asciidoc is `UTF-8`. Spring REST docs adopts the same
default for the snippets that it generated. If you require an encoding other than `UTF-8`,
use `RestDocumentationConfigurer` to configure it:
[source,java,indent=0]
----
include::{examples-dir}com/example/CustomEncoding.java[tags=custom-encoding]
----
[[configuration-output-directory]]
=== Snippet output directory
As described in <<getting-started-build-configuration>> the snippet output directory is
configured in your `pom.xml` or `build.gradle` file. This configuration applies to builds
on the command line, but it may not apply when running your tests in your IDE. In the
absence of the property, Spring REST Docs will write the generated snippets to standard
out.
If you'd prefer that your IDE writes the snippets to disk you can use a file in
`src/test/resources` named `documentation.properties` to specify the output directory that
should be used:
[source,properties]
----
org.springframework.restdocs.outputDir: target/generated-snippets
----

View File

@@ -0,0 +1,45 @@
[[customizing-responses]]
== Customizing responses
There may be situations where you do not want to document a response exactly as received.
Spring REST Docs provides a number of response post processors that can be used to modify
a response after it is received but before it's documented.
Response modification is configured using a `ResponseModifier`. An instance can be
obtained using the static `modifyResponseTo` method on `RestDocumentation`. Once the
response modifications have been provided, documentation can be configured as usual
via the `andDocument` method:
[source,java,indent=0]
----
include::{examples-dir}/com/example/ResponsePostProcessing.java[tags=general]
----
<1> Call `modifyResponseTo` to configure response modifications, passing in one or more
`ResponsePostProcessor` implementations.
<2> Proceed with documenting the call
[[customizing-responses-pretty-printing]]
=== Pretty printing
`prettyPrintContent` on `ResponsePostProcessors` formats the body of the response to
make it easier to read.
[[customizing-responses-masking-links]]
=== Masking links
If you're documenting a Hypermedia-based API, you may want to encourage clients to
navigate the API using links rather than through the use of hard coded URIs. One way to do
this is to limit the use of URIs in the documentation. `maskLinks` on
`ResponsePostProcessors` replaces the `href` of any links in the response with `...`. A
different replacement can also be specified if you wish.
=== Removing headers
`removeHeaders` on `ResponsePostProcessors` removes any occurrences of the named headers
from the response.
=== Replacing patterns
`replacePattern` on `ResponsePostProcessors` provides a general purpose mechanism for
replacing content in a response. Any occurrences of a regular expression are replaced.

View File

@@ -0,0 +1,263 @@
[[documenting-your-api]]
== Documenting your API
This section provides more details about using Spring REST Docs to document your API.
[[documenting-your-api-hypermedia]]
=== Hypermedia
Spring REST Docs provides support for documenting the links in a
https://en.wikipedia.org/wiki/HATEOAS[Hypermedia-based] API:
[source,java,indent=0]
----
include::{examples-dir}/com/example/Hypermedia.java[tag=links]
----
<1> `withLinks` is used to describe the expected links
<2> Expects a link whose rel is `alpha`. Uses the static `linkWithRel` method on
`org.springframework.restdocs.hypermedia.HypermediaDocumentation`.
<3> Expects a link whose rel is `bravo`
The result is a snippet named `links.adoc` that contains a table describing the resource's
links.
When documenting links, the test will fail if an undocumented link is found in the
response. Similarly, the test will also fail if a documented link is not found in the
response.
[[documenting-your-api-hypermedia-link-formats]]
==== Hypermedia link formats
Two link formats are understood by default:
* Atom links are expected to be in an array named `links`. Used by default when the
content type of the response is compatible with `application/json`.
* HAL links are expected to be in a map named `_links`. Used by default when the
content type of the response is compatible with `application/hal+json`.
If you are using Atom or HAL-format links but with a different content type you can
provide one of the built-in `LinkExtractor` implementations to `withLinks`. For example:
[source,java,indent=0]
----
include::{examples-dir}/com/example/Hypermedia.java[tag=explicit-extractor]
----
<1> Indicate that the links are in HAL format using the `halLinks` static method on
`org.springframework.restdocs.hypermedia.LinkExtractors`.
If your API represents its links in a format other than Atom or HAL you can provide your
own implementation of the `LinkExtractor` interface to extract the links from the
response.
[[documenting-your-api-request-response-payloads]]
=== Request and response payloads
In addition to the Hypermedia-specific support <<documenting-your-api-hypermedia,described
above>>, support for general documentation of request and response payloads is also
provided:
[source,java,indent=0]
----
include::{examples-dir}/com/example/Payload.java[tags=response]
----
<1> `withResponseFields` is used to describe the expected fields in the response payload.
To document a request `withRequestFields` can be used.
<2> Expects a field with the path `contact`. Uses the static `fieldWithPath` method on
`org.springframework.restdocs.payload.PayloadDocumentation`.
<3> Expects a field with the path `contact.email`
The result is a snippet that contains a table describing the fields. For requests this
snippet is named `request-fields.adoc`. For responses this snippet is named
`response-fields.adoc`.
When documenting fields, the test will fail if an undocumented field is found in the
payload. Similarly, the test will also fail if a documented field is not found in the
payload and the field has not be marked as optional. For payloads with a hierarchical
structure, when a point in the hierarchy is documented any fields beneath that point are
also deemed to have been documented. This means that you do not have to document the
entire hierarchy, but you may do so if you wish.
[[documenting-your-api-request-response-payloads-field-paths]]
==== Field paths
When documenting request and response payloads, fields are identified using a path. Paths
use `.` to descend into a child object and `[]` to identify an array. For example, with
this JSON payload:
[source,json,indent=0]
----
{
"a":{
"b":[
{
"c":"one"
},
{
"c":"two"
},
{
"d":"three"
}
]
}
}
----
The following paths are all present:
[cols="1,3"]
|===
|Path | Value
|`a`
|An object containing `b`
|`a.b`
|An array containing three objects
|`a.b[]`
|An array containing three objects
|`a.b[].c`
|An array containing the strings `one` and `two`
|`a.b[].d`
|The string `three`
|===
[[documenting-your-api-request-response-payloads-field-types]]
==== Field types
When a field is documented, Spring REST docs will attempt to determine its type by
examining the payload. Seven different types are supported:
[cols="1,3"]
|===
| Type | Description
| array
| The value of each occurrence of the field is an array
| boolean
| The value of each occurrence of the field is a boolean (`true` or `false`)
| object
| The value of each occurrence of the field is an object
| number
| The value of each occurrence of the field is a number
| null
| The value of each occurrence of the field is `null`
| string
| The value of each occurrence of the field is a string
| varies
| The field occurs multiple times in the payload with a variety of different types
|===
The type can also be set explicitly using the `type(FieldType)` method on
`FieldDescriptor`:
[source,java,indent=0]
----
include::{examples-dir}/com/example/Payload.java[tags=explicit-type]
----
<1> Set the field's type to `string`.
[[documenting-your-api-query-parameters]]
=== Query parameters
A request's query parameters can be documented using `withQueryParameters`
[source,java,indent=0]
----
include::{examples-dir}/com/example/QueryParameters.java[tags=query-parameters]
----
<1> `withQueryParameters` is used to describe the query parameters
<2> Documents a parameter named `page`. Uses the static `parameterWithName` method on
`org.springframework.restdocs.request.RequestDocumentation`.
<3> Documents a parameter named `per_page`
The result is a snippet named `query-parameters.adoc` that contains a table describing
the query parameters that are supported by the resource.
When documenting query parameters, the test will fail if an undocumented query parameter
is used in the request. Similarly, the test will also fail if a documented query parameter
is not found in the request.
[[documenting-your-api-default-snippets]]
=== Default snippets
A number of snippets are produced automatically when you document a call to
`MockMvc.perform`:
[cols="1,3"]
|===
|Snippet | Description
| `curl-request.adoc`
| Contains the http://curl.haxx.se[`curl`] command that is equivalent to the `MockMvc`
call that is being documented
| `http-request.adoc`
| Contains the HTTP request that is equivalent to the `MockMvc` call that is being
documented
| `http-response.adoc`
| Contains the HTTP response that was returned
|===
[[documentating-your-api-parameterized-output-directories]]
=== Using parameterized output directories
The output directory used by `document` can be parameterized. The following parameters
are supported:
[cols="1,3"]
|===
| Parameter | Description
| {methodName}
| The name of the test method, formatted using camelCase
| {method-name}
| The name of the test method, formatted using kebab-case
| {method_name}
| The name of the test method, formatted using snake_case
| {step}
| The count of calls to MockMvc.perform in the current test
|===
For example, `document("{method-name}")` in a test method named `creatingANote` will write
snippets into a directory named `creating-a-note`.
The `{step}` parameter is particularly useful in combination with Spring MVC Test's
`alwaysDo` functionality. It allows documentation to be configured once in a setup method:
[source,java,indent=0]
----
include::{examples-dir}/com/example/AlwaysDo.java[tags=always-do]
----
With this configuration in place, every call to `MockMvc.perform` will produce
the <<documenting-your-api-default-snippets,default snippets>> without any further
configuration. Take a look at the `GettingStartedDocumentation` classes in each of the
sample applications to see this functionality in action.

View File

@@ -0,0 +1,219 @@
[[getting-started]]
== Getting started
This section describes how to get started with Spring REST docs.
[[getting-started-sample-applications]]
=== Sample applications
If you want to jump straight in, there are two sample applications available.
{hateoas-sample}[One sample uses Spring HATEOAS] and {data-rest-sample}[the other uses
Spring Data REST]. Both samples use Spring REST Docs to produce a detailed API guide
and a getting started walkthrough. You can use either Gradle or Maven to build them.
In each sample the source for the documentation can be found in `src/main/asciidoc`.
`api-guide.adoc` produces an API guide for the service. `getting-started-guide.adoc`
produces a getting started guide that provides an introductory walkthrough.
The code that produces the generated snippets can be found in `src/test/java`.
`ApiDocumentation.java` produces the snippets for the API guide.
`GettingStartedDocumentation.java` produces the snippets for the getting started guide.
[[getting-started-build-configuration]]
=== Build configuration
The first step in using Spring REST Docs is to configure your project's build.
[[getting-started-build-configuration-gradle]]
==== Gradle build configuration
Both {samples}[sample applications] contain `build.gradle` files that you may wish to
use as a reference. The key parts of the configuration are described below.
[source,groovy,indent=0]
----
plugins { <1>
id "org.asciidoctor.convert" version "1.5.2"
}
dependencies { <2>
testCompile 'org.springframework.restdocs:spring-restdocs:0.1.0.BUILD-SNAPSHOT'
}
ext { <3>
snippetsDir = file('build/generated-snippets')
}
test { <4>
systemProperty 'org.springframework.restdocs.outputDir', snippetsDir
outputs.dir snippetsDir
}
asciidoctor { <5>
attributes 'snippets': snippetsDir
inputs.dir snippetsDir
dependsOn test
}
----
<1> Apply the Asciidoctor plugin
<2> Add a dependency on spring-restdocs in the `testCompile` configuration:
<3> Configure a property to define the output location for generated snippets:
<4> Configure the `test` task with the `org.springframework.restdocs.outputDir` system
property. This property controls the location into which Spring REST Docs will write the
snippets that it generates.
<5> Configure the `asciidoctor` task and define an attribute named `snippets`. You can
then use this attribute when including the generated snippets in your documentation.
[[getting-started-build-configuration-maven]]
==== Maven build configuration
Both {samples}[sample applications] contain `pom.xml` files that you may wish to
use as a reference. The key parts of the configuration are described below.
[source,xml,indent=0]
----
<dependency> <1>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs</artifactId>
<version>0.1.0.BUILD-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<properties> <2>
<snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
</properties>
<build>
<plugins>
<plugin> <3>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Documentation.java</include>
</includes>
<systemPropertyVariables>
<org.springframework.restdocs.outputDir>
${snippetsDirectory}
</org.springframework.restdocs.outputDir>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin> <4>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.2</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
<attributes>
<snippets>${snippetsDirectory}</snippets>
</attributes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
----
<1> Add a dependency on `spring-restdocs` in the `test` scope
<2> Configure a property to define the output location for generated snippets
<3> Configure the SureFire plugin with the `org.springframework.restdocs.outputDir` system
property. This property controls the location into which Spring REST docs will write the
snippets that it generates. The plugin is also configured to include files whose names end
with `Documentation.java`:
<4> Configure the Asciidoctor plugin and define an attribute named `snippets`. You can
then use this attribute when including the generated snippets in your documentation.
[[getting-started-documentation-snippets]]
=== Generating documentation snippets
Spring REST Docs uses {spring-mvc-test-docs}[Spring's MVC Test] to make requests to the
service that you are documenting. It then produces documentation snippets for the
result's request and response.
[[getting-started-documentation-snippets-setup]]
==== Setting up Spring MVC test
The first step in generating documentation snippets is to provide an `@Before` method
that creates a `MockMvc` instance:
[source,java,indent=0]
----
include::{examples-dir}com/example/ExampleApplicationTests.java[tags=mock-mvc-setup]
----
The `MockMvc` instance is configured using a `RestDocumentationConfigurer`. An instance
of this class can be obtained from the static `documentationConfiguration()` method on
`org.springframework.restdocs.RestDocumentation`. `RestDocumentationConfigurer` applies
sensible defaults and also provides an API for customizing the configuration. Refer to the
<<configuration, Configuration section>> for more information.
[[getting-started-documentation-snippets-setup]]
==== Invoking the RESTful service
Now that a `MockMvc` instance has been created, it can be used to invoke the RESTful
service and document the request and response.
[source,java,indent=0]
----
include::{examples-dir}com/example/InvokeService.java[tags=invoke-service]
----
<1> Invoke the root (`/`) of the service an indicate that an `application/json` response
is required
<2> Assert that the service is produced the expected response
<3> Document the call to the service, writing the snippets into a directory named `index`
that will be located beneath the configured output directory. The snippets are written by
a `RestDocumentationResultHandler`. An instance of this class can be obtained from the
static `document` method on `org.springframework.restdocs.RestDocumentation`.
By default, three snippets a written:
* `<output-directory>/index/curl-request.adoc`
* `<output-directory>/index/http-request.adoc`
* `<output-directory>/index/http-response.adoc`
Refer to <<documenting-your-api>> for more information about these and other snippets
that can be produced by Spring REST Docs.
[[getting-started-using-the-snippets]]
=== Using the snippets
The generated snippets can be included in your documentation using the
http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/#include-files[include macro].
The `snippets` attribute specified in the <<getting-started-build-configuration, build
configuration>> can be used to reference the snippets output directory, for example:
[source,adoc,indent=0]
....
[source,bash]
----
\include::{snippets}/index/curl-request.adoc[]
----
....

View File

@@ -0,0 +1,23 @@
= Spring REST Docs
Andy Wilkinson
:doctype: book
:toc: left
:toclevels: 3
:source-highlighter: highlightjs
:spring-mvc-test-docs: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#spring-mvc-test-framework
:samples: https://github.com/spring-projects/spring-restdocs/tree/master/samples
:hateoas-sample: https://github.com/spring-projects/spring-restdocs/tree/master/samples/rest-notes-spring-hateoas
:data-rest-sample: https://github.com/spring-projects/spring-restdocs/tree/master/samples/rest-notes-spring-data-rest
:examples-dir: ../../test/java/
:icons: font
[[abstract]]
Document RESTful services by combining hand-written documentation with auto-generated
snippets produced with Spring MVC Test.
include::introduction.adoc[]
include::getting-started.adoc[]
include::documenting-your-api.adoc[]
include::customizing-responses.adoc[]
include::configuration.adoc[]

View File

@@ -0,0 +1,23 @@
[[introduction]]
== Introduction
The aim of Spring REST Docs is to help you to produce documentation for your RESTful
services that is accurate and readable.
Writing high-quality documentation is difficult. One way to ease that difficulty is to use
tools that are well-suited to the job. To this end, Spring REST Docs uses
http://asciidoctor.org[Asciidoctor]. Asciidoctor processes plain text and produces
HTML styled and layed out to suit your needs.
Spring REST Docs makes use of snippets produced by tests written with
{spring-mvc-test-docs}[Spring MVC Test]. This test-driven approach helps to guarantee the
accuracy of your service's documentation. If a snippet is incorrect the test that
produces it will fail.
Documenting a RESTful service is largely about describing its resources. Two key parts
of each resource's description are the details of the HTTP requests that it consumes
and the HTTP responses that it produces. Spring REST Docs allows you to work with these
resources and the HTTP requests and responses, shielding your documentation
from the inner-details of your service's implementation. This separation helps you to
document your service's API rather than its implementation. It also frees you to evolve
the implementation without having to rework the documentation.

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.RestDocumentation.documentationConfiguration;
import org.junit.Before;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
public class AlwaysDo {
private MockMvc mockMvc;
private WebApplicationContext context;
// tag::always-do[]
@Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration())
.alwaysDo(document("{method-name}/{step}/"))
.build();
}
// end::always-do[]
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example;
import static org.springframework.restdocs.RestDocumentation.documentationConfiguration;
import org.junit.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
public class CustomEncoding {
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@Before
public void setUp() {
// tag::custom-encoding[]
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration().snippets()
.withEncoding("ISO-8859-1"))
.build();
// end::custom-encoding[]
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example;
import static org.springframework.restdocs.RestDocumentation.documentationConfiguration;
import org.junit.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
public class CustomUriConfiguration {
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@Before
public void setUp() {
// tag::custom-uri-configuration[]
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration().uris()
.withScheme("https")
.withHost("example.com")
.withPort(443)
.withContextPath("/api"))
.build();
// end::custom-uri-configuration[]
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example;
import org.junit.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.restdocs.RestDocumentation.documentationConfiguration;
public class ExampleApplicationTests {
// tag::mock-mvc-setup[]
@Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
@Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration())
.build();
}
// end::mock-mvc-setup[]
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
import static org.springframework.restdocs.hypermedia.LinkExtractors.halLinks;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
public class Hypermedia {
private MockMvc mockMvc;
public void links() throws Exception {
// tag::links[]
this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("index").withLinks( // <1>
linkWithRel("alpha").description("Link to the alpha resource"), // <2>
linkWithRel("bravo").description("Link to the bravo resource"))); // <3>
// end::links[]
}
public void explicitExtractor() throws Exception {
this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
//tag::explicit-extractor[]
.andDo(document("index").withLinks(halLinks(), // <1>
linkWithRel("alpha").description("Link to the alpha resource"),
linkWithRel("bravo").description("Link to the bravo resource")));
// end::explicit-extractor[]
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
public class InvokeService {
private MockMvc mockMvc;
public void invokeService() throws Exception {
// tag::invoke-service[]
this.mockMvc.perform(get("/").accept(MediaType.APPLICATION_JSON)) // <1>
.andExpect(status().isOk()) // <2>
.andDo(document("index")); // <3>
// end::invoke-service[]
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.springframework.http.MediaType;
import org.springframework.restdocs.payload.FieldType;
import org.springframework.test.web.servlet.MockMvc;
public class Payload {
private MockMvc mockMvc;
public void response() throws Exception {
// tag::response[]
this.mockMvc.perform(get("/user/5").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(document("index").withResponseFields( // <1>
fieldWithPath("contact").description("The user's contact details"), // <2>
fieldWithPath("contact.email").description("The user's email address"))); // <3>
// end::response[]
}
public void explicitType() throws Exception {
this.mockMvc.perform(get("/user/5").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
// tag::explicit-type[]
.andDo(document("index").withResponseFields(
fieldWithPath("contact.email")
.type(FieldType.STRING) // <1>
.optional()
.description("The user's email address")));
// end::explicit-type[]
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.springframework.test.web.servlet.MockMvc;
public class QueryParameters {
private MockMvc mockMvc;
public void queryParameters() throws Exception {
// tag::query-parameters[]
this.mockMvc.perform(get("/users?page=2&per_page=100"))
.andExpect(status().isOk())
.andDo(document("users").withQueryParameters( // <1>
parameterWithName("page").description("The page to retrieve"), // <2>
parameterWithName("per_page").description("Entries per page") // <3>
));
// end::query-parameters[]
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2014-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example;
import static org.springframework.restdocs.RestDocumentation.modifyResponseTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.springframework.test.web.servlet.MockMvc;
public class ResponsePostProcessing {
private MockMvc mockMvc;
public void general() throws Exception {
// tag::general[]
this.mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andDo(modifyResponseTo(/* ... */) // <1>
.andDocument("index")); // <2>
// end::general[]
}
}

View File

@@ -39,18 +39,18 @@ dependencies {
}
ext {
generatedDocumentation = file('build/generated-snippets')
snippetsDir = file('build/generated-snippets')
}
test {
systemProperty 'org.springframework.restdocs.outputDir', generatedDocumentation
outputs.dir generatedDocumentation
systemProperty 'org.springframework.restdocs.outputDir', snippetsDir
outputs.dir snippetsDir
}
asciidoctor {
sourceDir 'src/main/asciidoc' // Align with Maven's default location
attributes 'generated': generatedDocumentation
inputs.dir generatedDocumentation
attributes 'snippets': snippetsDir
inputs.dir snippetsDir
dependsOn test
}

View File

@@ -18,6 +18,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
<snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
</properties>
<dependencies>
@@ -68,7 +69,7 @@
<include>**/*Documentation.java</include>
</includes>
<systemPropertyVariables>
<org.springframework.restdocs.outputDir>${project.build.directory}/generated-snippets</org.springframework.restdocs.outputDir>
<org.springframework.restdocs.outputDir>${snippetsDirectory}</org.springframework.restdocs.outputDir>
</systemPropertyVariables>
</configuration>
</plugin>
@@ -87,7 +88,7 @@
<backend>html</backend>
<doctype>book</doctype>
<attributes>
<generated>${project.build.directory}/generated-snippets</generated>
<snippets>${snippetsDirectory}</snippets>
</attributes>
</configuration>
</execution>

View File

@@ -64,12 +64,12 @@ use of HTTP status codes.
Whenever an error response (status code >= 400) is returned, the body will contain a JSON object
that describes the problem. The error object has the following structure:
include::{generated}/error-example/response-fields.adoc[]
include::{snippets}/error-example/response-fields.adoc[]
For example, a request that attempts to apply a non-existent tag to a note will produce a
`400 Bad Request` response:
include::{generated}/error-example/http-response.adoc[]
include::{snippets}/error-example/http-response.adoc[]
[[overview-hypermedia]]
== Hypermedia
@@ -99,18 +99,18 @@ A `GET` request is used to access the index
==== Response structure
include::{generated}/index-example/response-fields.adoc[]
include::{snippets}/index-example/response-fields.adoc[]
==== Example response
include::{generated}/index-example/http-response.adoc[]
include::{snippets}/index-example/http-response.adoc[]
[[resources-index-links]]
==== Links
include::{generated}/index-example/links.adoc[]
include::{snippets}/index-example/links.adoc[]
@@ -128,15 +128,15 @@ A `GET` request will list all of the service's notes.
==== Response structure
include::{generated}/notes-list-example/response-fields.adoc[]
include::{snippets}/notes-list-example/response-fields.adoc[]
==== Example request
include::{generated}/notes-list-example/curl-request.adoc[]
include::{snippets}/notes-list-example/curl-request.adoc[]
==== Example response
include::{generated}/notes-list-example/http-response.adoc[]
include::{snippets}/notes-list-example/http-response.adoc[]
@@ -147,15 +147,15 @@ A `POST` request is used to create a note
==== Request structure
include::{generated}/notes-create-example/request-fields.adoc[]
include::{snippets}/notes-create-example/request-fields.adoc[]
==== Example request
include::{generated}/notes-create-example/curl-request.adoc[]
include::{snippets}/notes-create-example/curl-request.adoc[]
==== Example response
include::{generated}/notes-create-example/http-response.adoc[]
include::{snippets}/notes-create-example/http-response.adoc[]
@@ -173,15 +173,15 @@ A `GET` request will list all of the service's tags.
==== Response structure
include::{generated}/tags-list-example/response-fields.adoc[]
include::{snippets}/tags-list-example/response-fields.adoc[]
==== Example request
include::{generated}/tags-list-example/curl-request.adoc[]
include::{snippets}/tags-list-example/curl-request.adoc[]
==== Example response
include::{generated}/tags-list-example/http-response.adoc[]
include::{snippets}/tags-list-example/http-response.adoc[]
@@ -192,15 +192,15 @@ A `POST` request is used to create a note
==== Request structure
include::{generated}/tags-create-example/request-fields.adoc[]
include::{snippets}/tags-create-example/request-fields.adoc[]
==== Example request
include::{generated}/tags-create-example/curl-request.adoc[]
include::{snippets}/tags-create-example/curl-request.adoc[]
==== Example response
include::{generated}/tags-create-example/http-response.adoc[]
include::{snippets}/tags-create-example/http-response.adoc[]
@@ -214,7 +214,7 @@ The Note resource is used to retrieve, update, and delete individual notes
[[resources-note-links]]
=== Links
include::{generated}/note-get-example/links.adoc[]
include::{snippets}/note-get-example/links.adoc[]
@@ -225,15 +225,15 @@ A `GET` request will retrieve the details of a note
==== Response structure
include::{generated}/note-get-example/response-fields.adoc[]
include::{snippets}/note-get-example/response-fields.adoc[]
==== Example request
include::{generated}/note-get-example/curl-request.adoc[]
include::{snippets}/note-get-example/curl-request.adoc[]
==== Example response
include::{generated}/note-get-example/http-response.adoc[]
include::{snippets}/note-get-example/http-response.adoc[]
@@ -244,17 +244,17 @@ A `PATCH` request is used to update a note
==== Request structure
include::{generated}/note-update-example/request-fields.adoc[]
include::{snippets}/note-update-example/request-fields.adoc[]
To leave an attribute of a note unchanged, any of the above may be omitted from the request.
==== Example request
include::{generated}/note-update-example/curl-request.adoc[]
include::{snippets}/note-update-example/curl-request.adoc[]
==== Example response
include::{generated}/note-update-example/http-response.adoc[]
include::{snippets}/note-update-example/http-response.adoc[]
@@ -268,7 +268,7 @@ The Tag resource is used to retrieve, update, and delete individual tags
[[resources-tag-links]]
=== Links
include::{generated}/tag-get-example/links.adoc[]
include::{snippets}/tag-get-example/links.adoc[]
@@ -279,15 +279,15 @@ A `GET` request will retrieve the details of a tag
==== Response structure
include::{generated}/tag-get-example/response-fields.adoc[]
include::{snippets}/tag-get-example/response-fields.adoc[]
==== Example request
include::{generated}/tag-get-example/curl-request.adoc[]
include::{snippets}/tag-get-example/curl-request.adoc[]
==== Example response
include::{generated}/tag-get-example/http-response.adoc[]
include::{snippets}/tag-get-example/http-response.adoc[]
@@ -298,12 +298,12 @@ A `PATCH` request is used to update a tag
==== Request structure
include::{generated}/tag-update-example/request-fields.adoc[]
include::{snippets}/tag-update-example/request-fields.adoc[]
==== Example request
include::{generated}/tag-update-example/curl-request.adoc[]
include::{snippets}/tag-update-example/curl-request.adoc[]
==== Example response
include::{generated}/tag-update-example/http-response.adoc[]
include::{snippets}/tag-update-example/http-response.adoc[]

View File

@@ -42,12 +42,12 @@ $ java -jar build/libs/*.jar
You can check that the service is up and running by executing a simple request using
cURL:
include::{generated}/index/1/curl-request.adoc[]
include::{snippets}/index/1/curl-request.adoc[]
This request should yield the following response in the
http://stateless.co/hal_specification.html[Hypertext Application Language (HAL)] format:
include::{generated}/index/1/http-response.adoc[]
include::{snippets}/index/1/http-response.adoc[]
Note the `_links` in the JSON response. They are key to navigating the API.
@@ -59,26 +59,26 @@ Now that you've started the service and verified that it works, the next step is
it to create a new note. As you saw above, the URI for working with notes is included as
a link when you perform a `GET` request against the root of the service:
include::{generated}/index/1/http-response.adoc[]
include::{snippets}/index/1/http-response.adoc[]
To create a note, you need to execute a `POST` request to this URI including a JSON
payload containing the title and body of the note:
include::{generated}/creating-a-note/1/curl-request.adoc[]
include::{snippets}/creating-a-note/1/curl-request.adoc[]
The response from this request should have a status code of `201 Created` and contain a
`Location` header whose value is the URI of the newly created note:
include::{generated}/creating-a-note/1/http-response.adoc[]
include::{snippets}/creating-a-note/1/http-response.adoc[]
To work with the newly created note you use the URI in the `Location` header. For example,
you can access the note's details by performing a `GET` request:
include::{generated}/creating-a-note/2/curl-request.adoc[]
include::{snippets}/creating-a-note/2/curl-request.adoc[]
This request will produce a response with the note's details in its body:
include::{generated}/creating-a-note/2/http-response.adoc[]
include::{snippets}/creating-a-note/2/http-response.adoc[]
Note the `tags` link which we'll make use of later.
@@ -92,26 +92,26 @@ to tag a note, you must first create the tag.
Referring back to the response for the service's index, the URI for working with tags is
include as a link:
include::{generated}/index/1/http-response.adoc[]
include::{snippets}/index/1/http-response.adoc[]
To create a tag you need to execute a `POST` request to this URI, including a JSON
payload containing the name of the tag:
include::{generated}/creating-a-note/3/curl-request.adoc[]
include::{snippets}/creating-a-note/3/curl-request.adoc[]
The response from this request should have a status code of `201 Created` and contain a
`Location` header whose value is the URI of the newly created tag:
include::{generated}/creating-a-note/3/http-response.adoc[]
include::{snippets}/creating-a-note/3/http-response.adoc[]
To work with the newly created tag you use the URI in the `Location` header. For example
you can access the tag's details by performing a `GET` request:
include::{generated}/creating-a-note/4/curl-request.adoc[]
include::{snippets}/creating-a-note/4/curl-request.adoc[]
This request will produce a response with the tag's details in its body:
include::{generated}/creating-a-note/4/http-response.adoc[]
include::{snippets}/creating-a-note/4/http-response.adoc[]
@@ -132,25 +132,25 @@ with it.
Once again we execute a `POST` request. However, this time, in an array named tags, we
include the URI of the tag we just created:
include::{generated}/creating-a-note/5/curl-request.adoc[]
include::{snippets}/creating-a-note/5/curl-request.adoc[]
Once again, the response's `Location` header tells us the URI of the newly created note:
include::{generated}/creating-a-note/5/http-response.adoc[]
include::{snippets}/creating-a-note/5/http-response.adoc[]
As before, a `GET` request executed against this URI will retrieve the note's details:
include::{generated}/creating-a-note/6/curl-request.adoc[]
include::{generated}/creating-a-note/6/http-response.adoc[]
include::{snippets}/creating-a-note/6/curl-request.adoc[]
include::{snippets}/creating-a-note/6/http-response.adoc[]
To verify that the tag has been associated with the note, we can perform a `GET` request
against the URI from the `tags` link:
include::{generated}/creating-a-note/7/curl-request.adoc[]
include::{snippets}/creating-a-note/7/curl-request.adoc[]
The response embeds information about the tag that we've just associated with the note:
include::{generated}/creating-a-note/7/http-response.adoc[]
include::{snippets}/creating-a-note/7/http-response.adoc[]
@@ -160,18 +160,18 @@ An existing note can be tagged by executing a `PATCH` request against the note's
a body that contains the array of tags to be associated with the note. We'll used the
URI of the untagged note that we created earlier:
include::{generated}/creating-a-note/8/curl-request.adoc[]
include::{snippets}/creating-a-note/8/curl-request.adoc[]
This request should produce a `204 No Content` response:
include::{generated}/creating-a-note/8/http-response.adoc[]
include::{snippets}/creating-a-note/8/http-response.adoc[]
When we first created this note, we noted the tags link included in its details:
include::{generated}/creating-a-note/2/http-response.adoc[]
include::{snippets}/creating-a-note/2/http-response.adoc[]
We can use that link now and execute a `GET` request to see that the note now has a
single tag:
include::{generated}/creating-a-note/9/curl-request.adoc[]
include::{generated}/creating-a-note/9/http-response.adoc[]
include::{snippets}/creating-a-note/9/curl-request.adoc[]
include::{snippets}/creating-a-note/9/http-response.adoc[]

View File

@@ -41,18 +41,18 @@ dependencies {
}
ext {
generatedDocumentation = file('build/generated-snippets')
snippetsDir = file('build/generated-snippets')
}
test {
systemProperty 'org.springframework.restdocs.outputDir', generatedDocumentation
outputs.dir generatedDocumentation
systemProperty 'org.springframework.restdocs.outputDir', snippetsDir
outputs.dir snippetsDir
}
asciidoctor {
sourceDir 'src/main/asciidoc' // Align with Maven's default location
attributes 'generated': generatedDocumentation
inputs.dir generatedDocumentation
attributes 'snippets': snippetsDir
inputs.dir snippetsDir
dependsOn test
}

View File

@@ -18,6 +18,7 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
<snippetsDirectory>${project.build.directory}/generated-snippets</snippetsDirectory>
</properties>
<dependencies>
@@ -84,7 +85,7 @@
<include>**/*Documentation.java</include>
</includes>
<systemPropertyVariables>
<org.springframework.restdocs.outputDir>${project.build.directory}/generated-snippets</org.springframework.restdocs.outputDir>
<org.springframework.restdocs.outputDir>${snippetsDirectory}</org.springframework.restdocs.outputDir>
</systemPropertyVariables>
</configuration>
</plugin>
@@ -103,7 +104,7 @@
<backend>html</backend>
<doctype>book</doctype>
<attributes>
<generated>${project.build.directory}/generated-snippets</generated>
<snippets>${snippetsDirectory}</snippets>
</attributes>
</configuration>
</execution>

View File

@@ -64,12 +64,12 @@ use of HTTP status codes.
Whenever an error response (status code >= 400) is returned, the body will contain a JSON object
that describes the problem. The error object has the following structure:
include::{generated}/error-example/response-fields.adoc[]
include::{snippets}/error-example/response-fields.adoc[]
For example, a request that attempts to apply a non-existent tag to a note will produce a
`400 Bad Request` response:
include::{generated}/error-example/http-response.adoc[]
include::{snippets}/error-example/http-response.adoc[]
[[overview-hypermedia]]
== Hypermedia
@@ -99,18 +99,18 @@ A `GET` request is used to access the index
==== Response structure
include::{generated}/index-example/response-fields.adoc[]
include::{snippets}/index-example/response-fields.adoc[]
==== Example response
include::{generated}/index-example/http-response.adoc[]
include::{snippets}/index-example/http-response.adoc[]
[[resources-index-links]]
==== Links
include::{generated}/index-example/links.adoc[]
include::{snippets}/index-example/links.adoc[]
@@ -128,15 +128,15 @@ A `GET` request will list all of the service's notes.
==== Response structure
include::{generated}/notes-list-example/response-fields.adoc[]
include::{snippets}/notes-list-example/response-fields.adoc[]
==== Example request
include::{generated}/notes-list-example/curl-request.adoc[]
include::{snippets}/notes-list-example/curl-request.adoc[]
==== Example response
include::{generated}/notes-list-example/http-response.adoc[]
include::{snippets}/notes-list-example/http-response.adoc[]
@@ -147,15 +147,15 @@ A `POST` request is used to create a note
==== Request structure
include::{generated}/notes-create-example/request-fields.adoc[]
include::{snippets}/notes-create-example/request-fields.adoc[]
==== Example request
include::{generated}/notes-create-example/curl-request.adoc[]
include::{snippets}/notes-create-example/curl-request.adoc[]
==== Example response
include::{generated}/notes-create-example/http-response.adoc[]
include::{snippets}/notes-create-example/http-response.adoc[]
@@ -173,15 +173,15 @@ A `GET` request will list all of the service's tags.
==== Response structure
include::{generated}/tags-list-example/response-fields.adoc[]
include::{snippets}/tags-list-example/response-fields.adoc[]
==== Example request
include::{generated}/tags-list-example/curl-request.adoc[]
include::{snippets}/tags-list-example/curl-request.adoc[]
==== Example response
include::{generated}/tags-list-example/http-response.adoc[]
include::{snippets}/tags-list-example/http-response.adoc[]
@@ -192,15 +192,15 @@ A `POST` request is used to create a note
==== Request structure
include::{generated}/tags-create-example/request-fields.adoc[]
include::{snippets}/tags-create-example/request-fields.adoc[]
==== Example request
include::{generated}/tags-create-example/curl-request.adoc[]
include::{snippets}/tags-create-example/curl-request.adoc[]
==== Example response
include::{generated}/tags-create-example/http-response.adoc[]
include::{snippets}/tags-create-example/http-response.adoc[]
@@ -214,7 +214,7 @@ The Note resource is used to retrieve, update, and delete individual notes
[[resources-note-links]]
=== Links
include::{generated}/note-get-example/links.adoc[]
include::{snippets}/note-get-example/links.adoc[]
@@ -225,15 +225,15 @@ A `GET` request will retrieve the details of a note
==== Response structure
include::{generated}/note-get-example/response-fields.adoc[]
include::{snippets}/note-get-example/response-fields.adoc[]
==== Example request
include::{generated}/note-get-example/curl-request.adoc[]
include::{snippets}/note-get-example/curl-request.adoc[]
==== Example response
include::{generated}/note-get-example/http-response.adoc[]
include::{snippets}/note-get-example/http-response.adoc[]
@@ -244,17 +244,17 @@ A `PATCH` request is used to update a note
==== Request structure
include::{generated}/note-update-example/request-fields.adoc[]
include::{snippets}/note-update-example/request-fields.adoc[]
To leave an attribute of a note unchanged, any of the above may be omitted from the request.
==== Example request
include::{generated}/note-update-example/curl-request.adoc[]
include::{snippets}/note-update-example/curl-request.adoc[]
==== Example response
include::{generated}/note-update-example/http-response.adoc[]
include::{snippets}/note-update-example/http-response.adoc[]
[[resources-tag]]
@@ -267,7 +267,7 @@ The Tag resource is used to retrieve, update, and delete individual tags
[[resources-tag-links]]
=== Links
include::{generated}/tag-get-example/links.adoc[]
include::{snippets}/tag-get-example/links.adoc[]
@@ -278,15 +278,15 @@ A `GET` request will retrieve the details of a tag
==== Response structure
include::{generated}/tag-get-example/response-fields.adoc[]
include::{snippets}/tag-get-example/response-fields.adoc[]
==== Example request
include::{generated}/tag-get-example/curl-request.adoc[]
include::{snippets}/tag-get-example/curl-request.adoc[]
==== Example response
include::{generated}/tag-get-example/http-response.adoc[]
include::{snippets}/tag-get-example/http-response.adoc[]
@@ -297,12 +297,12 @@ A `PATCH` request is used to update a tag
==== Request structure
include::{generated}/tag-update-example/request-fields.adoc[]
include::{snippets}/tag-update-example/request-fields.adoc[]
==== Example request
include::{generated}/tag-update-example/curl-request.adoc[]
include::{snippets}/tag-update-example/curl-request.adoc[]
==== Example response
include::{generated}/tag-update-example/http-response.adoc[]
include::{snippets}/tag-update-example/http-response.adoc[]

View File

@@ -42,11 +42,11 @@ $ java -jar build/libs/*.jar
You can check that the service is up and running by executing a simple request using
cURL:
include::{generated}/index/1/curl-request.adoc[]
include::{snippets}/index/1/curl-request.adoc[]
This request should yield the following response:
include::{generated}/index/1/http-response.adoc[]
include::{snippets}/index/1/http-response.adoc[]
Note the `_links` in the JSON response. They are key to navigating the API.
@@ -58,26 +58,26 @@ Now that you've started the service and verified that it works, the next step is
it to create a new note. As you saw above, the URI for working with notes is included as
a link when you perform a `GET` request against the root of the service:
include::{generated}/index/1/http-response.adoc[]
include::{snippets}/index/1/http-response.adoc[]
To create a note you need to execute a `POST` request to this URI, including a JSON
payload containing the title and body of the note:
include::{generated}/creating-a-note/1/curl-request.adoc[]
include::{snippets}/creating-a-note/1/curl-request.adoc[]
The response from this request should have a status code of `201 Created` and contain a
`Location` header whose value is the URI of the newly created note:
include::{generated}/creating-a-note/1/http-response.adoc[]
include::{snippets}/creating-a-note/1/http-response.adoc[]
To work with the newly created note you use the URI in the `Location` header. For example
you can access the note's details by performing a `GET` request:
include::{generated}/creating-a-note/2/curl-request.adoc[]
include::{snippets}/creating-a-note/2/curl-request.adoc[]
This request will produce a response with the note's details in its body:
include::{generated}/creating-a-note/2/http-response.adoc[]
include::{snippets}/creating-a-note/2/http-response.adoc[]
Note the `note-tags` link which we'll make use of later.
@@ -91,26 +91,26 @@ to tag a note, you must first create the tag.
Referring back to the response for the service's index, the URI for working with tags is
include as a link:
include::{generated}/index/1/http-response.adoc[]
include::{snippets}/index/1/http-response.adoc[]
To create a tag you need to execute a `POST` request to this URI, including a JSON
payload containing the name of the tag:
include::{generated}/creating-a-note/3/curl-request.adoc[]
include::{snippets}/creating-a-note/3/curl-request.adoc[]
The response from this request should have a status code of `201 Created` and contain a
`Location` header whose value is the URI of the newly created tag:
include::{generated}/creating-a-note/3/http-response.adoc[]
include::{snippets}/creating-a-note/3/http-response.adoc[]
To work with the newly created tag you use the URI in the `Location` header. For example
you can access the tag's details by performing a `GET` request:
include::{generated}/creating-a-note/4/curl-request.adoc[]
include::{snippets}/creating-a-note/4/curl-request.adoc[]
This request will produce a response with the tag's details in its body:
include::{generated}/creating-a-note/4/http-response.adoc[]
include::{snippets}/creating-a-note/4/http-response.adoc[]
@@ -131,25 +131,25 @@ with it.
Once again we execute a `POST` request, but this time, in an array named tags, we include
the URI of the tag we just created:
include::{generated}/creating-a-note/5/curl-request.adoc[]
include::{snippets}/creating-a-note/5/curl-request.adoc[]
Once again, the response's `Location` header tells use the URI of the newly created note:
include::{generated}/creating-a-note/5/http-response.adoc[]
include::{snippets}/creating-a-note/5/http-response.adoc[]
As before, a `GET` request executed against this URI will retrieve the note's details:
include::{generated}/creating-a-note/6/curl-request.adoc[]
include::{generated}/creating-a-note/6/http-response.adoc[]
include::{snippets}/creating-a-note/6/curl-request.adoc[]
include::{snippets}/creating-a-note/6/http-response.adoc[]
To see the note's tags, execute a `GET` request against the URI of the note's
`note-tags` link:
include::{generated}/creating-a-note/7/curl-request.adoc[]
include::{snippets}/creating-a-note/7/curl-request.adoc[]
The response shows that, as expected, the note has a single tag:
include::{generated}/creating-a-note/7/http-response.adoc[]
include::{snippets}/creating-a-note/7/http-response.adoc[]
@@ -159,18 +159,18 @@ An existing note can be tagged by executing a `PATCH` request against the note's
a body that contains the array of tags to be associated with the note. We'll use the
URI of the untagged note that we created earlier:
include::{generated}/creating-a-note/8/curl-request.adoc[]
include::{snippets}/creating-a-note/8/curl-request.adoc[]
This request should produce a `204 No Content` response:
include::{generated}/creating-a-note/8/http-response.adoc[]
include::{snippets}/creating-a-note/8/http-response.adoc[]
When we first created this note, we noted the `note-tags` link included in its details:
include::{generated}/creating-a-note/2/http-response.adoc[]
include::{snippets}/creating-a-note/2/http-response.adoc[]
We can use that link now and execute a `GET` request to see that the note now has a
single tag:
include::{generated}/creating-a-note/9/curl-request.adoc[]
include::{generated}/creating-a-note/9/http-response.adoc[]
include::{snippets}/creating-a-note/9/curl-request.adoc[]
include::{snippets}/creating-a-note/9/http-response.adoc[]

View File

@@ -1,3 +1,4 @@
rootProject.name = 'spring-restdocs-build'
include 'docs'
include 'spring-restdocs'

View File

@@ -0,0 +1,89 @@
ext {
hamcrestVersion = '1.3'
jacocoVersion = '0.7.2.201409121644'
}
group = 'org.springframework.restdocs'
apply plugin: 'maven'
apply plugin: 'sonar-runner'
sourceCompatibility = 1.7
targetCompatibility = 1.7
eclipseJdt.onlyIf { false }
cleanEclipseJdt.onlyIf { false }
repositories {
jcenter()
}
sonarRunner {
sonarProperties {
property 'sonar.jacoco.reportPath', "${buildDir.name}/jacoco.exec"
property 'sonar.java.coveragePlugin', 'jacoco'
property 'sonar.links.ci', 'https://build.spring.io/browse/SRD'
property 'sonar.links.homepage', 'https://github.com/spring-projects/spring-restdocs'
property 'sonar.links.issue', 'https://github.com/spring-projects/spring-restdocs'
property 'sonar.links.scm', 'https://github.com/spring-projects/spring-restdocs'
}
}
configurations {
jacoco
}
ext {
javadocLinks = [
'http://docs.oracle.com/javase/8/docs/api/',
"http://docs.spring.io/spring-framework/docs/$springVersion/javadoc-api/"
] as String[]
}
dependencies {
compile 'com.fasterxml.jackson.core:jackson-databind'
compile 'junit:junit'
compile 'org.springframework:spring-core'
compile 'org.springframework:spring-test'
compile 'org.springframework:spring-web'
compile 'org.springframework:spring-webmvc'
compile 'javax.servlet:javax.servlet-api'
jacoco 'org.jacoco:org.jacoco.agent::runtime'
testCompile 'org.springframework.hateoas:spring-hateoas'
testCompile 'org.mockito:mockito-core'
testCompile 'org.hamcrest:hamcrest-core'
testCompile 'org.hamcrest:hamcrest-library'
}
task sourcesJar(type: Jar) {
classifier = 'sources'
from project.sourceSets.main.allSource
}
javadoc {
description = "Generates project-level javadoc for use in -javadoc jar"
options.memberLevel = org.gradle.external.javadoc.JavadocMemberLevel.PROTECTED
options.author = true
options.header = "Spring REST Docs $version"
options.docTitle = "${options.header} API"
options.links(project.ext.javadocLinks)
options.addStringOption('-quiet')
}
task javadocJar(type: Jar) {
classifier = "javadoc"
from javadoc
}
artifacts {
archives sourcesJar
archives javadocJar
}
test {
jvmArgs "-javaagent:${configurations.jacoco.asPath}=destfile=${buildDir}/jacoco.exec,includes=org.springframework.restdocs.*"
testLogging {
exceptionFormat "full"
}
}