Add support for documenting body of a request, response or request part
Closes gh-318 Closes gh-319
This commit is contained in:
@@ -105,12 +105,28 @@ descriptors to a snippet that's preconfigured to ignore certain links. For examp
|
|||||||
include::{examples-dir}/com/example/Hypermedia.java[tags=ignore-links]
|
include::{examples-dir}/com/example/Hypermedia.java[tags=ignore-links]
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[documenting-your-api-request-response-payloads]]
|
[[documenting-your-api-request-response-payloads]]
|
||||||
=== Request and response payloads
|
=== Request and response payloads
|
||||||
|
|
||||||
In addition to the hypermedia-specific support <<documenting-your-api-hypermedia,described
|
In addition to the hypermedia-specific support <<documenting-your-api-hypermedia,described
|
||||||
above>>, support for general documentation of request and response payloads is also
|
above>>, support for general documentation of request and response payloads is also
|
||||||
provided. Consider the following payload:
|
provided.
|
||||||
|
|
||||||
|
By default, Spring REST Docs will automatically generate snippets for the body of the
|
||||||
|
request and the body of the response. These snippets are named `request-body.adoc` and
|
||||||
|
`response-body.adoc` respectively.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[documenting-your-api-request-response-payloads-fields]]
|
||||||
|
==== Request and response fields
|
||||||
|
|
||||||
|
To provide more detailed documentation of a request or response payload, support for
|
||||||
|
documenting the payload's fields is provided.
|
||||||
|
|
||||||
|
Consider the following payload:
|
||||||
|
|
||||||
[source,json,indent=0]
|
[source,json,indent=0]
|
||||||
----
|
----
|
||||||
@@ -122,7 +138,7 @@ provided. Consider the following payload:
|
|||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
It can be documented like this:
|
Its fields can be documented like this:
|
||||||
|
|
||||||
[source,java,indent=0,role="primary"]
|
[source,java,indent=0,role="primary"]
|
||||||
.MockMvc
|
.MockMvc
|
||||||
@@ -195,11 +211,11 @@ TIP: By default, Spring REST Docs will assume that the payload you are documenti
|
|||||||
JSON. If you want to document an XML payload the content type of the request or response
|
JSON. If you want to document an XML payload the content type of the request or response
|
||||||
must be compatible with `application/xml`.
|
must be compatible with `application/xml`.
|
||||||
|
|
||||||
[[documenting-your-api-request-response-payloads-json]]
|
[[documenting-your-api-request-response-payloads-fields-json]]
|
||||||
==== JSON payloads
|
===== Fields in JSON payloads
|
||||||
|
|
||||||
[[documenting-your-api-request-response-payloads-json-field-paths]]
|
[[documenting-your-api-request-response-payloads-fields-json-field-paths]]
|
||||||
===== JSON field paths
|
====== JSON field paths
|
||||||
|
|
||||||
JSON field paths use either dot notation or bracket notation. Dot notation uses '.' to
|
JSON field paths use either dot notation or bracket notation. Dot notation uses '.' to
|
||||||
separate each key in the path; `a.b`, for example. Bracket notation wraps each key in
|
separate each key in the path; `a.b`, for example. Bracket notation wraps each key in
|
||||||
@@ -288,8 +304,8 @@ found in the following array:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[documenting-your-api-request-response-payloads-json-field-types]]
|
[[documenting-your-api-request-response-payloads-fields-json-field-types]]
|
||||||
===== JSON field types
|
====== JSON field types
|
||||||
|
|
||||||
When a field is documented, Spring REST Docs will attempt to determine its type by
|
When a field is documented, Spring REST Docs will attempt to determine its type by
|
||||||
examining the payload. Seven different types are supported:
|
examining the payload. Seven different types are supported:
|
||||||
@@ -340,18 +356,18 @@ include::{examples-dir}/com/example/restassured/Payload.java[tags=explicit-type]
|
|||||||
<1> Set the field's type to `string`.
|
<1> Set the field's type to `string`.
|
||||||
|
|
||||||
|
|
||||||
[[documenting-your-api-request-response-payloads-xml]]
|
[[documenting-your-api-request-response-payloads-fields-xml]]
|
||||||
==== XML payloads
|
===== XML payloads
|
||||||
|
|
||||||
[[documenting-your-api-request-response-payloads-xml-field-paths]]
|
[[documenting-your-api-request-response-payloads-fields-xml-field-paths]]
|
||||||
===== XML field paths
|
====== XML field paths
|
||||||
|
|
||||||
XML field paths are described using XPath. `/` is used to descend into a child node.
|
XML field paths are described using XPath. `/` is used to descend into a child node.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[documenting-your-api-request-response-payloads-xml-field-types]]
|
[[documenting-your-api-request-response-payloads-fields-xml-field-types]]
|
||||||
===== XML field types
|
====== XML field types
|
||||||
|
|
||||||
When documenting an XML payload, you must provide a type for the field using the
|
When documenting an XML payload, you must provide a type for the field using the
|
||||||
`type(Object)` method on `FieldDescriptor`. The result of the supplied type's `toString`
|
`type(Object)` method on `FieldDescriptor`. The result of the supplied type's `toString`
|
||||||
@@ -359,8 +375,8 @@ method will be used in the documentation.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[documenting-your-api-request-response-payloads-reusing-field-descriptors]]
|
[[documenting-your-api-request-response-payloads-fields-reusing-field-descriptors]]
|
||||||
==== Reusing field descriptors
|
===== Reusing field descriptors
|
||||||
|
|
||||||
In addition to the general support for <<documenting-your-api-reusing-snippets,reusing
|
In addition to the general support for <<documenting-your-api-reusing-snippets,reusing
|
||||||
snippets>>, the request and response snippets allow additional descriptors to be
|
snippets>>, the request and response snippets allow additional descriptors to be
|
||||||
@@ -445,7 +461,10 @@ If a payload is large or structurally complex, it can be useful to document
|
|||||||
individual sections of the payload. REST Docs allows you to do so by extracting a
|
individual sections of the payload. REST Docs allows you to do so by extracting a
|
||||||
subsection of the payload and then documenting it.
|
subsection of the payload and then documenting it.
|
||||||
|
|
||||||
Consider the following JSON response payload:
|
[[documenting-your-api-request-response-payloads-subsections-body]]
|
||||||
|
===== Documenting a subsection of a request or response body
|
||||||
|
|
||||||
|
Consider the following JSON response body:
|
||||||
|
|
||||||
[source,json,indent=0]
|
[source,json,indent=0]
|
||||||
----
|
----
|
||||||
@@ -463,13 +482,64 @@ Consider the following JSON response payload:
|
|||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
A snippet that documents the fields of the `temperature` object (`high` and `low`) can
|
A snippet that documents the `temperature` object can be produces as follows:
|
||||||
be produced as follows:
|
|
||||||
|
|
||||||
[source,java,indent=0,role="primary"]
|
[source,java,indent=0,role="primary"]
|
||||||
.MockMvc
|
.MockMvc
|
||||||
----
|
----
|
||||||
include::{examples-dir}/com/example/mockmvc/Payload.java[tags=beneath-path]
|
include::{examples-dir}/com/example/mockmvc/Payload.java[tags=body-subsection]
|
||||||
|
----
|
||||||
|
<1> Produce a snippet containing a subsection of the response body. Uses the static
|
||||||
|
`responseBody` and `beneathPath` methods on
|
||||||
|
`org.springframework.restdocs.payload.PayloadDocumentation`. To produce a snippet
|
||||||
|
for the request body, `requestBody` can be used in place of `responseBody`.
|
||||||
|
|
||||||
|
[source,java,indent=0,role="secondary"]
|
||||||
|
.REST Assured
|
||||||
|
----
|
||||||
|
include::{examples-dir}/com/example/restassured/Payload.java[tags=body-subsection]
|
||||||
|
----
|
||||||
|
<1> Produce a snippet containing a subsection of the response body. Uses the static
|
||||||
|
`responseBody` and `beneathPath` methods on
|
||||||
|
`org.springframework.restdocs.payload.PayloadDocumentation`. To produce a snippet
|
||||||
|
for the request body, `requestBody` can be used in place of `responseBody`.
|
||||||
|
|
||||||
|
The result is a snippet with the following contents:
|
||||||
|
|
||||||
|
[source,json,indent=0]
|
||||||
|
----
|
||||||
|
{
|
||||||
|
"temperature": {
|
||||||
|
"high": 21.2,
|
||||||
|
"low": 14.8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
To make the snippet's name distinct, an identifier for the subsection is included. By
|
||||||
|
default, this identifier is `beneath-${path}`. For example, the code above will result in
|
||||||
|
a snippet named `response-body-beneath-weather.temperature.adoc`. The identifier can
|
||||||
|
be customized using the `withSubsectionId(String)` method:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
include::{examples-dir}/com/example/Payload.java[tags=custom-subsection-id]
|
||||||
|
----
|
||||||
|
|
||||||
|
This example will result in a snippet named `request-body-temp.adoc`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[documenting-your-api-request-response-payloads-subsections-fields]]
|
||||||
|
===== Documenting the fields of a subsection of a request or response
|
||||||
|
|
||||||
|
As well as documenting a subsection of a request or response body, it's also possible to
|
||||||
|
document the fields in a particular subsection. A snippet that documents the fields of the `temperature` object (`high` and `low`) can be produced as follows:
|
||||||
|
|
||||||
|
[source,java,indent=0,role="primary"]
|
||||||
|
.MockMvc
|
||||||
|
----
|
||||||
|
include::{examples-dir}/com/example/mockmvc/Payload.java[tags=fields-subsection]
|
||||||
----
|
----
|
||||||
<1> Produce a snippet describing the fields in the subsection of the response payload
|
<1> Produce a snippet describing the fields in the subsection of the response payload
|
||||||
beneath the path `weather.temperature`. Uses the static `beneathPath` method on
|
beneath the path `weather.temperature`. Uses the static `beneathPath` method on
|
||||||
@@ -479,7 +549,7 @@ include::{examples-dir}/com/example/mockmvc/Payload.java[tags=beneath-path]
|
|||||||
[source,java,indent=0,role="secondary"]
|
[source,java,indent=0,role="secondary"]
|
||||||
.REST Assured
|
.REST Assured
|
||||||
----
|
----
|
||||||
include::{examples-dir}/com/example/restassured/Payload.java[tags=beneath-path]
|
include::{examples-dir}/com/example/restassured/Payload.java[tags=fields-subsection]
|
||||||
----
|
----
|
||||||
<1> Produce a snippet describing the fields in the subsection of the response payload
|
<1> Produce a snippet describing the fields in the subsection of the response payload
|
||||||
beneath the path `weather.temperature`. Uses the static `beneathPath` method on
|
beneath the path `weather.temperature`. Uses the static `beneathPath` method on
|
||||||
@@ -490,15 +560,7 @@ The result is a snippet that contains a table describing the `high` and `low` fi
|
|||||||
of `weather.temperature`. To make the snippet's name distinct, an identifier for the
|
of `weather.temperature`. To make the snippet's name distinct, an identifier for the
|
||||||
subsection is included. By default, this identifier is `beneath-${path}`. For
|
subsection is included. By default, this identifier is `beneath-${path}`. For
|
||||||
example, the code above will result in a snippet named
|
example, the code above will result in a snippet named
|
||||||
`response-fields-beneath-weather.temperature.adoc`. The identifier can be customized using
|
`response-fields-beneath-weather.temperature.adoc`.
|
||||||
the `withSubsectionId(String)` method:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
|
||||||
----
|
|
||||||
include::{examples-dir}/com/example/Payload.java[tags=custom-subsection-id]
|
|
||||||
----
|
|
||||||
|
|
||||||
This example will result in a snippet named `response-fields-temp.adoc`.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -679,12 +741,51 @@ above.
|
|||||||
=== Request part payloads
|
=== Request part payloads
|
||||||
|
|
||||||
The payload of a request part can be documented in much the same way as the
|
The payload of a request part can be documented in much the same way as the
|
||||||
<<documenting-your-api-request-response-payloads,payload of a request>>:
|
<<documenting-your-api-request-response-payloads,payload of a request>> with support
|
||||||
|
for documenting a request part's body and its fields.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[documenting-your-api-request-parts-payloads-fields]]
|
||||||
|
==== Documenting a request part's body
|
||||||
|
|
||||||
|
A snippet containing the body of a request part can be generated:
|
||||||
|
|
||||||
[source,java,indent=0,role="primary"]
|
[source,java,indent=0,role="primary"]
|
||||||
.MockMvc
|
.MockMvc
|
||||||
----
|
----
|
||||||
include::{examples-dir}/com/example/mockmvc/RequestPartPayload.java[tags=payload]
|
include::{examples-dir}/com/example/mockmvc/RequestPartPayload.java[tags=body]
|
||||||
|
----
|
||||||
|
<1> Configure Spring REST docs to produce a snippet containing the body of the
|
||||||
|
of the request part named `metadata`. Uses the static `requestPartBody` method on
|
||||||
|
`PayloadDocumentation`.
|
||||||
|
payload.
|
||||||
|
|
||||||
|
[source,java,indent=0,role="secondary"]
|
||||||
|
.REST Assured
|
||||||
|
----
|
||||||
|
include::{examples-dir}/com/example/restassured/RequestPartPayload.java[tags=body]
|
||||||
|
----
|
||||||
|
<1> Configure Spring REST docs to produce a snippet containing the body of the request
|
||||||
|
part named `metadata`. Uses the static `requestPartBody` method on
|
||||||
|
`PayloadDocumentation`.
|
||||||
|
|
||||||
|
The result is a snippet `request-part-${part-name}-body.adoc` that contains the part's
|
||||||
|
body. For example, documenting a part named `metadata` will produce a snippet named
|
||||||
|
`request-part-metadata-body.adoc`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[documenting-your-api-request-parts-payloads-fields]]
|
||||||
|
==== Documenting a request part's fields
|
||||||
|
|
||||||
|
A request part's fields can be documented in much the same way as the fields of a request
|
||||||
|
or response:
|
||||||
|
|
||||||
|
[source,java,indent=0,role="primary"]
|
||||||
|
.MockMvc
|
||||||
|
----
|
||||||
|
include::{examples-dir}/com/example/mockmvc/RequestPartPayload.java[tags=fields]
|
||||||
----
|
----
|
||||||
<1> Configure Spring REST docs to produce a snippet describing the fields in the payload
|
<1> Configure Spring REST docs to produce a snippet describing the fields in the payload
|
||||||
of the request part named `metadata`. Uses the static `requestPartFields` method on
|
of the request part named `metadata`. Uses the static `requestPartFields` method on
|
||||||
@@ -696,7 +797,7 @@ include::{examples-dir}/com/example/mockmvc/RequestPartPayload.java[tags=payload
|
|||||||
[source,java,indent=0,role="secondary"]
|
[source,java,indent=0,role="secondary"]
|
||||||
.REST Assured
|
.REST Assured
|
||||||
----
|
----
|
||||||
include::{examples-dir}/com/example/restassured/RequestPartPayload.java[tags=payload]
|
include::{examples-dir}/com/example/restassured/RequestPartPayload.java[tags=fields]
|
||||||
----
|
----
|
||||||
<1> Configure Spring REST docs to produce a snippet describing the fields in the payload
|
<1> Configure Spring REST docs to produce a snippet describing the fields in the payload
|
||||||
of the request part named `metadata`. Uses the static `requestPartFields` method on
|
of the request part named `metadata`. Uses the static `requestPartFields` method on
|
||||||
@@ -930,6 +1031,13 @@ documented
|
|||||||
|
|
||||||
| `http-response.adoc`
|
| `http-response.adoc`
|
||||||
| Contains the HTTP response that was returned
|
| Contains the HTTP response that was returned
|
||||||
|
|
||||||
|
| `request-body.adoc`
|
||||||
|
| Contains the body of the request that was sent
|
||||||
|
|
||||||
|
| `response-body.adoc`
|
||||||
|
| Contains the body of the response that was returned
|
||||||
|
|
||||||
|===
|
|===
|
||||||
|
|
||||||
You can configure which snippets are produced by default. Please refer to the
|
You can configure which snippets are produced by default. Please refer to the
|
||||||
@@ -1077,5 +1185,3 @@ and adds a title:
|
|||||||
<1> Add a title to the table
|
<1> Add a title to the table
|
||||||
<2> Add a new column named "Constraints"
|
<2> Add a new column named "Constraints"
|
||||||
<3> Include the descriptors' `constraints` attribute in each row of the table
|
<3> Include the descriptors' `constraints` attribute in each row of the table
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -406,12 +406,14 @@ static `document` method on
|
|||||||
<4> Invoke the root (`/`) of the service.
|
<4> Invoke the root (`/`) of the service.
|
||||||
<5> Assert that the service produce the expected response.
|
<5> Assert that the service produce the expected response.
|
||||||
|
|
||||||
By default, four snippets are written:
|
By default, six snippets are written:
|
||||||
|
|
||||||
* `<output-directory>/index/curl-request.adoc`
|
* `<output-directory>/index/curl-request.adoc`
|
||||||
* `<output-directory>/index/http-request.adoc`
|
* `<output-directory>/index/http-request.adoc`
|
||||||
* `<output-directory>/index/http-response.adoc`
|
* `<output-directory>/index/http-response.adoc`
|
||||||
* `<output-directory>/index/httpie-request.adoc`
|
* `<output-directory>/index/httpie-request.adoc`
|
||||||
|
* `<output-directory>/index/request-body.adoc`
|
||||||
|
* `<output-directory>/index/response-body.adoc`
|
||||||
|
|
||||||
Refer to <<documenting-your-api>> for more information about these and other snippets
|
Refer to <<documenting-your-api>> for more information about these and other snippets
|
||||||
that can be produced by Spring REST Docs.
|
that can be produced by Spring REST Docs.
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import org.springframework.restdocs.payload.FieldDescriptor;
|
|||||||
|
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.responseBody;
|
||||||
|
|
||||||
public class Payload {
|
public class Payload {
|
||||||
|
|
||||||
@@ -35,9 +35,7 @@ public class Payload {
|
|||||||
|
|
||||||
public void customSubsectionId() {
|
public void customSubsectionId() {
|
||||||
// tag::custom-subsection-id[]
|
// tag::custom-subsection-id[]
|
||||||
responseFields(beneathPath("weather.temperature").withSubsectionId("temp"),
|
responseBody(beneathPath("weather.temperature").withSubsectionId("temp"));
|
||||||
fieldWithPath("high").description("…"),
|
|
||||||
fieldWithPath("low").description("…"));
|
|
||||||
// end::custom-subsection-id[]
|
// end::custom-subsection-id[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,9 +26,10 @@ import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuild
|
|||||||
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
|
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
|
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
|
||||||
|
import static org.springframework.restdocs.payload.PayloadDocumentation.responseBody;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||||
|
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
|
||||||
import static org.springframework.restdocs.snippet.Attributes.attributes;
|
import static org.springframework.restdocs.snippet.Attributes.attributes;
|
||||||
import static org.springframework.restdocs.snippet.Attributes.key;
|
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
@@ -40,46 +41,50 @@ public class Payload {
|
|||||||
public void response() throws Exception {
|
public void response() throws Exception {
|
||||||
// tag::response[]
|
// tag::response[]
|
||||||
this.mockMvc.perform(get("/user/5").accept(MediaType.APPLICATION_JSON))
|
this.mockMvc.perform(get("/user/5").accept(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andDo(document("index", responseFields( // <1>
|
.andDo(document("index",
|
||||||
fieldWithPath("contact.email").description("The user's email address"), // <2>
|
responseFields( // <1>
|
||||||
fieldWithPath("contact.name").description("The user's name")))); // <3>
|
fieldWithPath("contact.email")
|
||||||
|
.description("The user's email address"), // <2>
|
||||||
|
fieldWithPath("contact.name").description("The user's name")))); // <3>
|
||||||
// end::response[]
|
// end::response[]
|
||||||
}
|
}
|
||||||
|
|
||||||
public void subsection() throws Exception {
|
public void subsection() throws Exception {
|
||||||
// tag::subsection[]
|
// tag::subsection[]
|
||||||
this.mockMvc.perform(get("/user/5").accept(MediaType.APPLICATION_JSON))
|
this.mockMvc.perform(get("/user/5").accept(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andDo(document("index", responseFields( // <1>
|
.andDo(document("index",
|
||||||
subsectionWithPath("contact").description("The user's contact details")))); // <1>
|
responseFields( // <1>
|
||||||
|
subsectionWithPath("contact")
|
||||||
|
.description("The user's contact details")))); // <1>
|
||||||
// end::subsection[]
|
// end::subsection[]
|
||||||
}
|
}
|
||||||
|
|
||||||
public void explicitType() throws Exception {
|
public void explicitType() throws Exception {
|
||||||
this.mockMvc.perform(get("/user/5").accept(MediaType.APPLICATION_JSON))
|
this.mockMvc.perform(get("/user/5").accept(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
// tag::explicit-type[]
|
// tag::explicit-type[]
|
||||||
.andDo(document("index", responseFields(
|
.andDo(document("index",
|
||||||
fieldWithPath("contact.email")
|
responseFields(
|
||||||
.type(JsonFieldType.STRING) // <1>
|
fieldWithPath("contact.email").type(JsonFieldType.STRING) // <1>
|
||||||
.description("The user's email address"))));
|
.description("The user's email address"))));
|
||||||
// end::explicit-type[]
|
// end::explicit-type[]
|
||||||
}
|
}
|
||||||
|
|
||||||
public void constraints() throws Exception {
|
public void constraints() throws Exception {
|
||||||
this.mockMvc.perform(post("/users/").accept(MediaType.APPLICATION_JSON))
|
this.mockMvc.perform(post("/users/").accept(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
// tag::constraints[]
|
// tag::constraints[]
|
||||||
.andDo(document("create-user", requestFields(
|
.andDo(document("create-user", requestFields(
|
||||||
attributes(key("title").value("Fields for user creation")), // <1>
|
attributes(key("title").value("Fields for user creation")), // <1>
|
||||||
fieldWithPath("name").description("The user's name")
|
fieldWithPath("name").description("The user's name")
|
||||||
.attributes(key("constraints")
|
.attributes(key("constraints")
|
||||||
.value("Must not be null. Must not be empty")), // <2>
|
.value("Must not be null. Must not be empty")), // <2>
|
||||||
fieldWithPath("email").description("The user's email address")
|
fieldWithPath("email").description("The user's email address")
|
||||||
.attributes(key("constraints")
|
.attributes(key("constraints")
|
||||||
.value("Must be a valid email address"))))); // <3>
|
.value("Must be a valid email address"))))); // <3>
|
||||||
// end::constraints[]
|
// end::constraints[]
|
||||||
}
|
}
|
||||||
|
|
||||||
public void descriptorReuse() throws Exception {
|
public void descriptorReuse() throws Exception {
|
||||||
@@ -89,27 +94,39 @@ public class Payload {
|
|||||||
|
|
||||||
// tag::single-book[]
|
// tag::single-book[]
|
||||||
this.mockMvc.perform(get("/books/1").accept(MediaType.APPLICATION_JSON))
|
this.mockMvc.perform(get("/books/1").accept(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk()).andDo(document("book", responseFields(book))); // <1>
|
||||||
.andDo(document("book", responseFields(book))); // <1>
|
|
||||||
// end::single-book[]
|
// end::single-book[]
|
||||||
|
|
||||||
// tag::book-array[]
|
// tag::book-array[]
|
||||||
this.mockMvc.perform(get("/books").accept(MediaType.APPLICATION_JSON))
|
this.mockMvc.perform(get("/books").accept(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andDo(document("book", responseFields(
|
.andDo(document("book",
|
||||||
fieldWithPath("[]").description("An array of books")) // <1>
|
responseFields(
|
||||||
.andWithPrefix("[].", book))); // <2>
|
fieldWithPath("[]").description("An array of books")) // <1>
|
||||||
|
.andWithPrefix("[].", book))); // <2>
|
||||||
// end::book-array[]
|
// end::book-array[]
|
||||||
}
|
}
|
||||||
|
|
||||||
public void subsectionBeneathPath() throws Exception {
|
public void fieldsSubsection() throws Exception {
|
||||||
// tag::beneath-path[]
|
// tag::fields-subsection[]
|
||||||
this.mockMvc.perform(get("/locations/1").accept(MediaType.APPLICATION_JSON))
|
this.mockMvc.perform(get("/locations/1").accept(MediaType.APPLICATION_JSON))
|
||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andDo(document("location", responseFields(beneathPath("weather.temperature"), // <1>
|
.andDo(document("location",
|
||||||
fieldWithPath("high").description("The forecast high in degrees celcius"), // <2>
|
responseFields(beneathPath("weather.temperature"), // <1>
|
||||||
fieldWithPath("low").description("The forecast low in degrees celcius"))));
|
fieldWithPath("high").description(
|
||||||
// end::beneath-path[]
|
"The forecast high in degrees celcius"), // <2>
|
||||||
|
fieldWithPath("low")
|
||||||
|
.description("The forecast low in degrees celcius"))));
|
||||||
|
// end::fields-subsection[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bodySubsection() throws Exception {
|
||||||
|
// tag::body-subsection[]
|
||||||
|
this.mockMvc.perform(get("/locations/1").accept(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk()).andDo(document("location",
|
||||||
|
responseBody(beneathPath("weather.temperature")))); // <1>
|
||||||
|
|
||||||
|
// end::body-subsection[]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.springframework.test.web.servlet.MockMvc;
|
|||||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||||
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.fileUpload;
|
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.fileUpload;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||||
|
import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartBody;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartFields;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartFields;
|
||||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
@@ -30,8 +31,8 @@ public class RequestPartPayload {
|
|||||||
|
|
||||||
private MockMvc mockMvc;
|
private MockMvc mockMvc;
|
||||||
|
|
||||||
public void response() throws Exception {
|
public void fields() throws Exception {
|
||||||
// tag::payload[]
|
// tag::fields[]
|
||||||
MockMultipartFile image = new MockMultipartFile("image", "image.png", "image/png",
|
MockMultipartFile image = new MockMultipartFile("image", "image.png", "image/png",
|
||||||
"<<png data>>".getBytes());
|
"<<png data>>".getBytes());
|
||||||
MockMultipartFile metadata = new MockMultipartFile("metadata", "",
|
MockMultipartFile metadata = new MockMultipartFile("metadata", "",
|
||||||
@@ -42,7 +43,21 @@ public class RequestPartPayload {
|
|||||||
.andExpect(status().isOk())
|
.andExpect(status().isOk())
|
||||||
.andDo(document("image-upload", requestPartFields("metadata", // <1>
|
.andDo(document("image-upload", requestPartFields("metadata", // <1>
|
||||||
fieldWithPath("version").description("The version of the image")))); // <2>
|
fieldWithPath("version").description("The version of the image")))); // <2>
|
||||||
// end::payload[]
|
// end::fields[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public void body() throws Exception {
|
||||||
|
// tag::body[]
|
||||||
|
MockMultipartFile image = new MockMultipartFile("image", "image.png", "image/png",
|
||||||
|
"<<png data>>".getBytes());
|
||||||
|
MockMultipartFile metadata = new MockMultipartFile("metadata", "",
|
||||||
|
"application/json", "{ \"version\": \"1.0\"}".getBytes());
|
||||||
|
|
||||||
|
this.mockMvc.perform(fileUpload("/images").file(image).file(metadata)
|
||||||
|
.accept(MediaType.APPLICATION_JSON))
|
||||||
|
.andExpect(status().isOk())
|
||||||
|
.andDo(document("image-upload", requestPartBody("metadata"))); // <1>
|
||||||
|
// end::body[]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import static org.hamcrest.CoreMatchers.is;
|
|||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
|
||||||
|
import static org.springframework.restdocs.payload.PayloadDocumentation.responseBody;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
|
||||||
import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document;
|
import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document;
|
||||||
@@ -107,15 +108,24 @@ public class Payload {
|
|||||||
// end::book-array[]
|
// end::book-array[]
|
||||||
}
|
}
|
||||||
|
|
||||||
public void subsectionBeneathPath() throws Exception {
|
public void fieldsSubsection() throws Exception {
|
||||||
// tag::beneath-path[]
|
// tag::fields-subsection[]
|
||||||
RestAssured.given(this.spec).accept("application/json")
|
RestAssured.given(this.spec).accept("application/json")
|
||||||
.filter(document("location", responseFields(beneathPath("weather.temperature"), // <1>
|
.filter(document("location", responseFields(beneathPath("weather.temperature"), // <1>
|
||||||
fieldWithPath("high").description("The forecast high in degrees celcius"), // <2>
|
fieldWithPath("high").description("The forecast high in degrees celcius"), // <2>
|
||||||
fieldWithPath("low").description("The forecast low in degrees celcius"))))
|
fieldWithPath("low").description("The forecast low in degrees celcius"))))
|
||||||
.when().get("/locations/1")
|
.when().get("/locations/1")
|
||||||
.then().assertThat().statusCode(is(200));
|
.then().assertThat().statusCode(is(200));
|
||||||
// end::beneath-path[]
|
// end::fields-subsection[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bodySubsection() throws Exception {
|
||||||
|
// tag::body-subsection[]
|
||||||
|
RestAssured.given(this.spec).accept("application/json")
|
||||||
|
.filter(document("location", responseBody(beneathPath("weather.temperature")))) // <1>
|
||||||
|
.when().get("/locations/1")
|
||||||
|
.then().assertThat().statusCode(is(200));
|
||||||
|
// end::body-subsection[]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import com.jayway.restassured.specification.RequestSpecification;
|
|||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||||
|
import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartBody;
|
||||||
import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartFields;
|
import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartFields;
|
||||||
import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document;
|
import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document;
|
||||||
|
|
||||||
@@ -32,8 +33,8 @@ public class RequestPartPayload {
|
|||||||
|
|
||||||
private RequestSpecification spec;
|
private RequestSpecification spec;
|
||||||
|
|
||||||
public void response() throws Exception {
|
public void fields() throws Exception {
|
||||||
// tag::payload[]
|
// tag::fields[]
|
||||||
Map<String, String> metadata = new HashMap<>();
|
Map<String, String> metadata = new HashMap<>();
|
||||||
metadata.put("version", "1.0");
|
metadata.put("version", "1.0");
|
||||||
RestAssured.given(this.spec).accept("application/json")
|
RestAssured.given(this.spec).accept("application/json")
|
||||||
@@ -42,7 +43,19 @@ public class RequestPartPayload {
|
|||||||
.when().multiPart("image", new File("image.png"), "image/png")
|
.when().multiPart("image", new File("image.png"), "image/png")
|
||||||
.multiPart("metadata", metadata).post("images")
|
.multiPart("metadata", metadata).post("images")
|
||||||
.then().assertThat().statusCode(is(200));
|
.then().assertThat().statusCode(is(200));
|
||||||
// end::payload[]
|
// end::fields[]
|
||||||
|
}
|
||||||
|
|
||||||
|
public void body() throws Exception {
|
||||||
|
// tag::body[]
|
||||||
|
Map<String, String> metadata = new HashMap<>();
|
||||||
|
metadata.put("version", "1.0");
|
||||||
|
RestAssured.given(this.spec).accept("application/json")
|
||||||
|
.filter(document("image-upload", requestPartBody("metadata"))) // <1>
|
||||||
|
.when().multiPart("image", new File("image.png"), "image/png")
|
||||||
|
.multiPart("metadata", metadata).post("images")
|
||||||
|
.then().assertThat().statusCode(is(200));
|
||||||
|
// end::body[]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import org.springframework.restdocs.RestDocumentationContext;
|
|||||||
import org.springframework.restdocs.cli.CliDocumentation;
|
import org.springframework.restdocs.cli.CliDocumentation;
|
||||||
import org.springframework.restdocs.generate.RestDocumentationGenerator;
|
import org.springframework.restdocs.generate.RestDocumentationGenerator;
|
||||||
import org.springframework.restdocs.http.HttpDocumentation;
|
import org.springframework.restdocs.http.HttpDocumentation;
|
||||||
|
import org.springframework.restdocs.payload.PayloadDocumentation;
|
||||||
import org.springframework.restdocs.snippet.Snippet;
|
import org.springframework.restdocs.snippet.Snippet;
|
||||||
import org.springframework.restdocs.templates.TemplateFormat;
|
import org.springframework.restdocs.templates.TemplateFormat;
|
||||||
import org.springframework.restdocs.templates.TemplateFormats;
|
import org.springframework.restdocs.templates.TemplateFormats;
|
||||||
@@ -42,7 +43,8 @@ public abstract class SnippetConfigurer<PARENT, TYPE>
|
|||||||
|
|
||||||
private List<Snippet> defaultSnippets = new ArrayList<>(Arrays.asList(
|
private List<Snippet> defaultSnippets = new ArrayList<>(Arrays.asList(
|
||||||
CliDocumentation.curlRequest(), CliDocumentation.httpieRequest(),
|
CliDocumentation.curlRequest(), CliDocumentation.httpieRequest(),
|
||||||
HttpDocumentation.httpRequest(), HttpDocumentation.httpResponse()));
|
HttpDocumentation.httpRequest(), HttpDocumentation.httpResponse(),
|
||||||
|
PayloadDocumentation.requestBody(), PayloadDocumentation.responseBody()));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default encoding for documentation snippets.
|
* The default encoding for documentation snippets.
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014-2016 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 org.springframework.restdocs.payload;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.restdocs.operation.Operation;
|
||||||
|
import org.springframework.restdocs.snippet.ModelCreationException;
|
||||||
|
import org.springframework.restdocs.snippet.TemplatedSnippet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract {@link TemplatedSnippet} subclass that provides a base for snippets that
|
||||||
|
* document a RESTful resource's request or response body.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public abstract class AbstractBodySnippet extends TemplatedSnippet {
|
||||||
|
|
||||||
|
private final PayloadSubsectionExtractor<?> subsectionExtractor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code AbstractBodySnippet} that will produce a snippet named
|
||||||
|
* {@code <type>-body} using a template named {@code <type>-body}. The snippet will
|
||||||
|
* contain the subsection of the body extracted by the given
|
||||||
|
* {@code subsectionExtractor}. The given {@code attributes} will be included in the
|
||||||
|
* model during template rendering
|
||||||
|
*
|
||||||
|
* @param type the type of the body
|
||||||
|
* @param subsectionExtractor the subsection extractor
|
||||||
|
* @param attributes the attributes
|
||||||
|
*/
|
||||||
|
protected AbstractBodySnippet(String type,
|
||||||
|
PayloadSubsectionExtractor<?> subsectionExtractor,
|
||||||
|
Map<String, Object> attributes) {
|
||||||
|
this(type, type, subsectionExtractor, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code AbstractBodySnippet} that will produce a snippet named
|
||||||
|
* {@code <name>-body} using a template named {@code <type>-body}. The snippet will
|
||||||
|
* contain the subsection of the body extracted by the given
|
||||||
|
* {@code subsectionExtractor}. The given {@code attributes} will be included in the
|
||||||
|
* model during template rendering
|
||||||
|
*
|
||||||
|
* @param name the name of the snippet
|
||||||
|
* @param type the type of the body
|
||||||
|
* @param subsectionExtractor the subsection extractor
|
||||||
|
* @param attributes the attributes
|
||||||
|
*/
|
||||||
|
protected AbstractBodySnippet(String name, String type,
|
||||||
|
PayloadSubsectionExtractor<?> subsectionExtractor,
|
||||||
|
Map<String, Object> attributes) {
|
||||||
|
super(name + "-body"
|
||||||
|
+ (subsectionExtractor != null
|
||||||
|
? "-" + subsectionExtractor.getSubsectionId() : ""),
|
||||||
|
type + "-body", attributes);
|
||||||
|
this.subsectionExtractor = subsectionExtractor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Map<String, Object> createModel(Operation operation) {
|
||||||
|
try {
|
||||||
|
MediaType contentType = getContentType(operation);
|
||||||
|
byte[] content = getContent(operation);
|
||||||
|
if (this.subsectionExtractor != null) {
|
||||||
|
content = this.subsectionExtractor.extractSubsection(content,
|
||||||
|
contentType);
|
||||||
|
}
|
||||||
|
Charset charset = extractCharset(contentType);
|
||||||
|
String body = charset != null ? new String(content, charset)
|
||||||
|
: new String(content);
|
||||||
|
Map<String, Object> model = new HashMap<>();
|
||||||
|
model.put("body", body);
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new ModelCreationException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Charset extractCharset(MediaType contentType) {
|
||||||
|
if (contentType == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return contentType.getCharset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the content of the request or response extracted from the given
|
||||||
|
* {@code operation}.
|
||||||
|
*
|
||||||
|
* @param operation The operation
|
||||||
|
* @return The content
|
||||||
|
* @throws IOException if the content cannot be extracted
|
||||||
|
*/
|
||||||
|
protected abstract byte[] getContent(Operation operation) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the content type of the request or response extracted from the given
|
||||||
|
* {@code operation}.
|
||||||
|
*
|
||||||
|
* @param operation The operation
|
||||||
|
* @return The content type
|
||||||
|
*/
|
||||||
|
protected abstract MediaType getContentType(Operation operation);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1443,6 +1443,164 @@ public abstract class PayloadDocumentation {
|
|||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code Snippet} that will document the body of the API operation's
|
||||||
|
* request payload.
|
||||||
|
*
|
||||||
|
* @return the snippet that will document the request body
|
||||||
|
*/
|
||||||
|
public static RequestBodySnippet requestBody() {
|
||||||
|
return new RequestBodySnippet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code Snippet} that will document the body of the API operation's
|
||||||
|
* request payload. The given attributes will be made available during snippet
|
||||||
|
* generation.
|
||||||
|
*
|
||||||
|
* @param attributes the attributes
|
||||||
|
* @return the snippet that will document the request body
|
||||||
|
*/
|
||||||
|
public static RequestBodySnippet requestBody(Map<String, Object> attributes) {
|
||||||
|
return new RequestBodySnippet(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code Snippet} that will document a subsection of the body of the API
|
||||||
|
* operation's request payload. The subsection will be extracted using the given
|
||||||
|
* {@code subsectionExtractor}.
|
||||||
|
*
|
||||||
|
* @param subsectionExtractor the subsection extractor
|
||||||
|
* @return the snippet that will document the request body subsection
|
||||||
|
*/
|
||||||
|
public static RequestBodySnippet requestBody(
|
||||||
|
PayloadSubsectionExtractor<?> subsectionExtractor) {
|
||||||
|
return new RequestBodySnippet(subsectionExtractor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code Snippet} that will document a subsection of the body of the API
|
||||||
|
* operation's request payload. The subsection will be extracted using the given
|
||||||
|
* {@code subsectionExtractor}. The given attributes will be made available during
|
||||||
|
* snippet generation.
|
||||||
|
*
|
||||||
|
* @param subsectionExtractor the subsection extractor
|
||||||
|
* @param attributes the attributes
|
||||||
|
* @return the snippet that will document the request body subsection
|
||||||
|
*/
|
||||||
|
public static RequestBodySnippet requestBody(
|
||||||
|
PayloadSubsectionExtractor<?> subsectionExtractor,
|
||||||
|
Map<String, Object> attributes) {
|
||||||
|
return new RequestBodySnippet(subsectionExtractor, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code Snippet} that will document the body of the API operation's
|
||||||
|
* response payload.
|
||||||
|
*
|
||||||
|
* @return the snippet that will document the response body
|
||||||
|
*/
|
||||||
|
public static ResponseBodySnippet responseBody() {
|
||||||
|
return new ResponseBodySnippet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code Snippet} that will document the body of the API operation's
|
||||||
|
* response payload. The given attributes will be made available during snippet
|
||||||
|
* generation.
|
||||||
|
*
|
||||||
|
* @param attributes the attributes
|
||||||
|
* @return the snippet that will document the response body
|
||||||
|
*/
|
||||||
|
public static ResponseBodySnippet responseBody(Map<String, Object> attributes) {
|
||||||
|
return new ResponseBodySnippet(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code Snippet} that will document a subsection of the body of the API
|
||||||
|
* operation's response payload. The subsection will be extracted using the given
|
||||||
|
* {@code subsectionExtractor}.
|
||||||
|
*
|
||||||
|
* @param subsectionExtractor the subsection extractor
|
||||||
|
* @return the snippet that will document the response body subsection
|
||||||
|
*/
|
||||||
|
public static ResponseBodySnippet responseBody(
|
||||||
|
PayloadSubsectionExtractor<?> subsectionExtractor) {
|
||||||
|
return new ResponseBodySnippet(subsectionExtractor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code Snippet} that will document a subsection of the body of the API
|
||||||
|
* operation's response payload. The subsection will be extracted using the given
|
||||||
|
* {@code subsectionExtractor}. The given attributes will be made available during
|
||||||
|
* snippet generation.
|
||||||
|
*
|
||||||
|
* @param subsectionExtractor the subsection extractor
|
||||||
|
* @param attributes the attributes
|
||||||
|
* @return the snippet that will document the response body subsection
|
||||||
|
*/
|
||||||
|
public static ResponseBodySnippet responseBody(
|
||||||
|
PayloadSubsectionExtractor<?> subsectionExtractor,
|
||||||
|
Map<String, Object> attributes) {
|
||||||
|
return new ResponseBodySnippet(subsectionExtractor, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code Snippet} that will document the body of specified part of the API
|
||||||
|
* operation's request payload.
|
||||||
|
*
|
||||||
|
* @param partName the name of the request part
|
||||||
|
* @return the snippet that will document the response body
|
||||||
|
*/
|
||||||
|
public static RequestPartBodySnippet requestPartBody(String partName) {
|
||||||
|
return new RequestPartBodySnippet(partName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code Snippet} that will document the body of specified part of the API
|
||||||
|
* operation's request payload. The given attributes will be made available during
|
||||||
|
* snippet generation.
|
||||||
|
*
|
||||||
|
* @param partName the name of the request part
|
||||||
|
* @param attributes the attributes
|
||||||
|
* @return the snippet that will document the response body
|
||||||
|
*/
|
||||||
|
public static RequestPartBodySnippet requestPartBody(String partName,
|
||||||
|
Map<String, Object> attributes) {
|
||||||
|
return new RequestPartBodySnippet(partName, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code Snippet} that will document a subsection of the body of specified
|
||||||
|
* part of the API operation's request payload. The subsection will be extracted using
|
||||||
|
* the given {@code subsectionExtractor}.
|
||||||
|
*
|
||||||
|
* @param partName the name of the request part
|
||||||
|
* @param subsectionExtractor the subsection extractor
|
||||||
|
* @return the snippet that will document the response body
|
||||||
|
*/
|
||||||
|
public static RequestPartBodySnippet requestPartBody(String partName,
|
||||||
|
PayloadSubsectionExtractor<?> subsectionExtractor) {
|
||||||
|
return new RequestPartBodySnippet(partName, subsectionExtractor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code Snippet} that will document a subsection of the body of specified
|
||||||
|
* part of the API operation's request payload. The subsection will be extracted using
|
||||||
|
* the given {@code subsectionExtractor}. The given attributes will be made available
|
||||||
|
* during snippet generation.
|
||||||
|
*
|
||||||
|
* @param partName the name of the request part
|
||||||
|
* @param subsectionExtractor the subsection extractor
|
||||||
|
* @param attributes the attributes
|
||||||
|
* @return the snippet that will document the response body
|
||||||
|
*/
|
||||||
|
public static RequestPartBodySnippet requestPartBody(String partName,
|
||||||
|
PayloadSubsectionExtractor<?> subsectionExtractor,
|
||||||
|
Map<String, Object> attributes) {
|
||||||
|
return new RequestPartBodySnippet(partName, subsectionExtractor, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a copy of the given {@code descriptors} with the given {@code pathPrefix}
|
* Creates a copy of the given {@code descriptors} with the given {@code pathPrefix}
|
||||||
* applied to their paths.
|
* applied to their paths.
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014-2016 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 org.springframework.restdocs.payload;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.restdocs.operation.Operation;
|
||||||
|
import org.springframework.restdocs.snippet.Snippet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Snippet} that documents the body of a request.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class RequestBodySnippet extends AbstractBodySnippet {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code RequestBodySnippet}.
|
||||||
|
*/
|
||||||
|
public RequestBodySnippet() {
|
||||||
|
this(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code RequestBodySnippet} that will document the subsection of the
|
||||||
|
* request body extracted by the given {@code subsectionExtractor}.
|
||||||
|
*
|
||||||
|
* @param subsectionExtractor the subsection extractor
|
||||||
|
*/
|
||||||
|
public RequestBodySnippet(PayloadSubsectionExtractor<?> subsectionExtractor) {
|
||||||
|
this(subsectionExtractor, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code RequestBodySnippet} with the given additional
|
||||||
|
* {@code attributes} that will be included in the model during template rendering.
|
||||||
|
*
|
||||||
|
* @param attributes The additional attributes
|
||||||
|
*/
|
||||||
|
public RequestBodySnippet(Map<String, Object> attributes) {
|
||||||
|
this(null, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code RequestBodySnippet} that will document the subsection of the
|
||||||
|
* request body extracted by the given {@code subsectionExtractor}. The given
|
||||||
|
* additional {@code attributes} that will be included in the model during template
|
||||||
|
* rendering.
|
||||||
|
*
|
||||||
|
* @param subsectionExtractor the subsection extractor
|
||||||
|
* @param attributes The additional attributes
|
||||||
|
*/
|
||||||
|
public RequestBodySnippet(PayloadSubsectionExtractor<?> subsectionExtractor,
|
||||||
|
Map<String, Object> attributes) {
|
||||||
|
super("request", subsectionExtractor, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] getContent(Operation operation) throws IOException {
|
||||||
|
return operation.getRequest().getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MediaType getContentType(Operation operation) {
|
||||||
|
return operation.getRequest().getHeaders().getContentType();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014-2016 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 org.springframework.restdocs.payload;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.restdocs.operation.Operation;
|
||||||
|
import org.springframework.restdocs.operation.OperationRequestPart;
|
||||||
|
import org.springframework.restdocs.snippet.Snippet;
|
||||||
|
import org.springframework.restdocs.snippet.SnippetException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Snippet} that documents the body of a request part.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class RequestPartBodySnippet extends AbstractBodySnippet {
|
||||||
|
|
||||||
|
private final String partName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code RequestPartBodySnippet} that will document the body of the
|
||||||
|
* request part with the given {@code partName}.
|
||||||
|
*
|
||||||
|
* @param partName the name of the request part
|
||||||
|
*/
|
||||||
|
public RequestPartBodySnippet(String partName) {
|
||||||
|
this(partName, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code RequestPartBodySnippet} that will document the subsection of
|
||||||
|
* the body of the request part with the given {@code partName} extracted by the given
|
||||||
|
* {@code subsectionExtractor}.
|
||||||
|
*
|
||||||
|
* @param partName the name of the request part
|
||||||
|
* @param subsectionExtractor the subsection extractor
|
||||||
|
*/
|
||||||
|
public RequestPartBodySnippet(String partName,
|
||||||
|
PayloadSubsectionExtractor<?> subsectionExtractor) {
|
||||||
|
this(partName, subsectionExtractor, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code RequestPartBodySnippet} that will document the body of the
|
||||||
|
* request part with the given {@code partName}. The given additional
|
||||||
|
* {@code attributes} will be included in the model during template rendering.
|
||||||
|
*
|
||||||
|
* @param partName the name of the request part
|
||||||
|
* @param attributes the additional attributes
|
||||||
|
*/
|
||||||
|
public RequestPartBodySnippet(String partName, Map<String, Object> attributes) {
|
||||||
|
this(partName, null, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code RequestPartBodySnippet} that will document the body of the
|
||||||
|
* request part with the given {@code partName}. The subsection of the body extracted
|
||||||
|
* by the given {@code subsectionExtractor} will be documented and the given
|
||||||
|
* additional {@code attributes} that will be included in the model during template
|
||||||
|
* rendering.
|
||||||
|
*
|
||||||
|
* @param partName the name of the request part
|
||||||
|
* @param subsectionExtractor the subsection extractor
|
||||||
|
* @param attributes The additional attributes
|
||||||
|
*/
|
||||||
|
public RequestPartBodySnippet(String partName,
|
||||||
|
PayloadSubsectionExtractor<?> subsectionExtractor,
|
||||||
|
Map<String, Object> attributes) {
|
||||||
|
super("request-part-" + partName, "request-part", subsectionExtractor,
|
||||||
|
attributes);
|
||||||
|
this.partName = partName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] getContent(Operation operation) throws IOException {
|
||||||
|
return findPart(operation).getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MediaType getContentType(Operation operation) {
|
||||||
|
return findPart(operation).getHeaders().getContentType();
|
||||||
|
}
|
||||||
|
|
||||||
|
private OperationRequestPart findPart(Operation operation) {
|
||||||
|
for (OperationRequestPart candidate : operation.getRequest().getParts()) {
|
||||||
|
if (candidate.getName().equals(this.partName)) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new SnippetException("A request part named '" + this.partName
|
||||||
|
+ "' was not found in the request");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014-2016 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 org.springframework.restdocs.payload;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.restdocs.operation.Operation;
|
||||||
|
import org.springframework.restdocs.snippet.Snippet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Snippet} that documents the body of a response.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class ResponseBodySnippet extends AbstractBodySnippet {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code ResponseBodySnippet}.
|
||||||
|
*/
|
||||||
|
public ResponseBodySnippet() {
|
||||||
|
this(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code ResponseBodySnippet} that will document the subsection of the
|
||||||
|
* response body extracted by the given {@code subsectionExtractor}.
|
||||||
|
*
|
||||||
|
* @param subsectionExtractor the subsection extractor
|
||||||
|
*/
|
||||||
|
public ResponseBodySnippet(PayloadSubsectionExtractor<?> subsectionExtractor) {
|
||||||
|
this(subsectionExtractor, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code ResponseBodySnippet} with the given additional
|
||||||
|
* {@code attributes} that will be included in the model during template rendering.
|
||||||
|
*
|
||||||
|
* @param attributes The additional attributes
|
||||||
|
*/
|
||||||
|
public ResponseBodySnippet(Map<String, Object> attributes) {
|
||||||
|
this(null, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@code ResponseBodySnippet} that will document the subsection of the
|
||||||
|
* response body extracted by the given {@code subsectionExtractor}. The given
|
||||||
|
* additional {@code attributes} that will be included in the model during template
|
||||||
|
* rendering.
|
||||||
|
*
|
||||||
|
* @param subsectionExtractor the subsection extractor
|
||||||
|
* @param attributes The additional attributes
|
||||||
|
*/
|
||||||
|
public ResponseBodySnippet(PayloadSubsectionExtractor<?> subsectionExtractor,
|
||||||
|
Map<String, Object> attributes) {
|
||||||
|
super("response", subsectionExtractor, attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] getContent(Operation operation) throws IOException {
|
||||||
|
return operation.getResponse().getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MediaType getContentType(Operation operation) {
|
||||||
|
return operation.getResponse().getHeaders().getContentType();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
[source,options="nowrap"]
|
||||||
|
----
|
||||||
|
{{body}}
|
||||||
|
----
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
[source,options="nowrap"]
|
||||||
|
----
|
||||||
|
{{body}}
|
||||||
|
----
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
[source,options="nowrap"]
|
||||||
|
----
|
||||||
|
{{body}}
|
||||||
|
----
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
```
|
||||||
|
{{body}}
|
||||||
|
```
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
```
|
||||||
|
{{body}}
|
||||||
|
```
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
```
|
||||||
|
{{body}}
|
||||||
|
```
|
||||||
@@ -68,7 +68,11 @@ public abstract class AbstractSnippetTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CodeBlockMatcher<?> codeBlock(String language) {
|
public CodeBlockMatcher<?> codeBlock(String language) {
|
||||||
return SnippetMatchers.codeBlock(this.templateFormat, language);
|
return this.codeBlock(language, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CodeBlockMatcher<?> codeBlock(String language, String options) {
|
||||||
|
return SnippetMatchers.codeBlock(this.templateFormat, language, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TableMatcher<?> tableWithHeader(String... headers) {
|
public TableMatcher<?> tableWithHeader(String... headers) {
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ import org.springframework.restdocs.cli.HttpieRequestSnippet;
|
|||||||
import org.springframework.restdocs.generate.RestDocumentationGenerator;
|
import org.springframework.restdocs.generate.RestDocumentationGenerator;
|
||||||
import org.springframework.restdocs.http.HttpRequestSnippet;
|
import org.springframework.restdocs.http.HttpRequestSnippet;
|
||||||
import org.springframework.restdocs.http.HttpResponseSnippet;
|
import org.springframework.restdocs.http.HttpResponseSnippet;
|
||||||
|
import org.springframework.restdocs.payload.RequestBodySnippet;
|
||||||
|
import org.springframework.restdocs.payload.ResponseBodySnippet;
|
||||||
import org.springframework.restdocs.snippet.Snippet;
|
import org.springframework.restdocs.snippet.Snippet;
|
||||||
import org.springframework.restdocs.snippet.StandardWriterResolver;
|
import org.springframework.restdocs.snippet.StandardWriterResolver;
|
||||||
import org.springframework.restdocs.snippet.WriterResolver;
|
import org.springframework.restdocs.snippet.WriterResolver;
|
||||||
@@ -76,7 +78,9 @@ public class RestDocumentationConfigurerTests {
|
|||||||
contains(instanceOf(CurlRequestSnippet.class),
|
contains(instanceOf(CurlRequestSnippet.class),
|
||||||
instanceOf(HttpieRequestSnippet.class),
|
instanceOf(HttpieRequestSnippet.class),
|
||||||
instanceOf(HttpRequestSnippet.class),
|
instanceOf(HttpRequestSnippet.class),
|
||||||
instanceOf(HttpResponseSnippet.class)));
|
instanceOf(HttpResponseSnippet.class),
|
||||||
|
instanceOf(RequestBodySnippet.class),
|
||||||
|
instanceOf(ResponseBodySnippet.class)));
|
||||||
assertThat(configuration, hasEntry(equalTo(SnippetConfiguration.class.getName()),
|
assertThat(configuration, hasEntry(equalTo(SnippetConfiguration.class.getName()),
|
||||||
instanceOf(SnippetConfiguration.class)));
|
instanceOf(SnippetConfiguration.class)));
|
||||||
SnippetConfiguration snippetConfiguration = (SnippetConfiguration) configuration
|
SnippetConfiguration snippetConfiguration = (SnippetConfiguration) configuration
|
||||||
@@ -138,7 +142,9 @@ public class RestDocumentationConfigurerTests {
|
|||||||
contains(instanceOf(CurlRequestSnippet.class),
|
contains(instanceOf(CurlRequestSnippet.class),
|
||||||
instanceOf(HttpieRequestSnippet.class),
|
instanceOf(HttpieRequestSnippet.class),
|
||||||
instanceOf(HttpRequestSnippet.class),
|
instanceOf(HttpRequestSnippet.class),
|
||||||
instanceOf(HttpResponseSnippet.class), equalTo(snippet)));
|
instanceOf(HttpResponseSnippet.class),
|
||||||
|
instanceOf(RequestBodySnippet.class),
|
||||||
|
instanceOf(ResponseBodySnippet.class), equalTo(snippet)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014-2016 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 org.springframework.restdocs.payload;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.restdocs.AbstractSnippetTests;
|
||||||
|
import org.springframework.restdocs.templates.TemplateEngine;
|
||||||
|
import org.springframework.restdocs.templates.TemplateFormat;
|
||||||
|
import org.springframework.restdocs.templates.TemplateResourceResolver;
|
||||||
|
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
|
||||||
|
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath;
|
||||||
|
import static org.springframework.restdocs.payload.PayloadDocumentation.requestPartBody;
|
||||||
|
import static org.springframework.restdocs.snippet.Attributes.attributes;
|
||||||
|
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link RequestPartBodySnippet}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class RequestBodyPartSnippetTests extends AbstractSnippetTests {
|
||||||
|
|
||||||
|
public RequestBodyPartSnippetTests(String name, TemplateFormat templateFormat) {
|
||||||
|
super(name, templateFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestPartWithBody() throws IOException {
|
||||||
|
this.snippets.expect("request-part-one-body")
|
||||||
|
.withContents(codeBlock(null, "nowrap").content("some content"));
|
||||||
|
|
||||||
|
requestPartBody("one").document(this.operationBuilder.request("http://localhost")
|
||||||
|
.part("one", "some content".getBytes()).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestPartWithNoBody() throws IOException {
|
||||||
|
this.snippets.expect("request-part-one-body")
|
||||||
|
.withContents(codeBlock(null, "nowrap").content(""));
|
||||||
|
requestPartBody("one").document(this.operationBuilder.request("http://localhost")
|
||||||
|
.part("one", new byte[0]).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void subsectionOfRequestPartBody() throws IOException {
|
||||||
|
this.snippets.expect("request-part-one-body-beneath-a.b")
|
||||||
|
.withContents(codeBlock(null, "nowrap").content("{\"c\":5}"));
|
||||||
|
|
||||||
|
requestPartBody("one", beneathPath("a.b"))
|
||||||
|
.document(this.operationBuilder.request("http://localhost")
|
||||||
|
.part("one", "{\"a\":{\"b\":{\"c\":5}}}".getBytes()).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void customSnippetAttributes() throws IOException {
|
||||||
|
this.snippets.expect("request-part-one-body")
|
||||||
|
.withContents(codeBlock("json", "nowrap").content("{\"a\":\"alpha\"}"));
|
||||||
|
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||||
|
given(resolver.resolveTemplateResource("request-part-body"))
|
||||||
|
.willReturn(snippetResource("request-part-body-with-language"));
|
||||||
|
requestPartBody("one",
|
||||||
|
attributes(
|
||||||
|
key("language").value("json")))
|
||||||
|
.document(
|
||||||
|
this.operationBuilder
|
||||||
|
.attribute(TemplateEngine.class.getName(),
|
||||||
|
new MustacheTemplateEngine(
|
||||||
|
resolver))
|
||||||
|
.request("http://localhost")
|
||||||
|
.part("one",
|
||||||
|
"{\"a\":\"alpha\"}".getBytes())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014-2016 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 org.springframework.restdocs.payload;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.restdocs.AbstractSnippetTests;
|
||||||
|
import org.springframework.restdocs.templates.TemplateEngine;
|
||||||
|
import org.springframework.restdocs.templates.TemplateFormat;
|
||||||
|
import org.springframework.restdocs.templates.TemplateResourceResolver;
|
||||||
|
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
|
||||||
|
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath;
|
||||||
|
import static org.springframework.restdocs.payload.PayloadDocumentation.requestBody;
|
||||||
|
import static org.springframework.restdocs.snippet.Attributes.attributes;
|
||||||
|
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link RequestBodySnippet}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class RequestBodySnippetTests extends AbstractSnippetTests {
|
||||||
|
|
||||||
|
public RequestBodySnippetTests(String name, TemplateFormat templateFormat) {
|
||||||
|
super(name, templateFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestWithBody() throws IOException {
|
||||||
|
this.snippets.expect("request-body")
|
||||||
|
.withContents(codeBlock(null, "nowrap").content("some content"));
|
||||||
|
|
||||||
|
requestBody().document(this.operationBuilder.request("http://localhost")
|
||||||
|
.content("some content").build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void requestWithNoBody() throws IOException {
|
||||||
|
this.snippets.expect("request-body")
|
||||||
|
.withContents(codeBlock(null, "nowrap").content(""));
|
||||||
|
requestBody().document(this.operationBuilder.request("http://localhost").build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void subsectionOfRequestBody() throws IOException {
|
||||||
|
this.snippets.expect("request-body-beneath-a.b")
|
||||||
|
.withContents(codeBlock(null, "nowrap").content("{\"c\":5}"));
|
||||||
|
|
||||||
|
requestBody(beneathPath("a.b"))
|
||||||
|
.document(this.operationBuilder.request("http://localhost")
|
||||||
|
.content("{\"a\":{\"b\":{\"c\":5}}}").build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void customSnippetAttributes() throws IOException {
|
||||||
|
this.snippets.expect("request-body")
|
||||||
|
.withContents(codeBlock("json", "nowrap").content("{\"a\":\"alpha\"}"));
|
||||||
|
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||||
|
given(resolver.resolveTemplateResource("request-body"))
|
||||||
|
.willReturn(snippetResource("request-body-with-language"));
|
||||||
|
requestBody(
|
||||||
|
attributes(
|
||||||
|
key("language").value("json")))
|
||||||
|
.document(
|
||||||
|
this.operationBuilder
|
||||||
|
.attribute(TemplateEngine.class.getName(),
|
||||||
|
new MustacheTemplateEngine(
|
||||||
|
resolver))
|
||||||
|
.request("http://localhost")
|
||||||
|
.content("{\"a\":\"alpha\"}").build());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2014-2016 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 org.springframework.restdocs.payload;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.restdocs.AbstractSnippetTests;
|
||||||
|
import org.springframework.restdocs.templates.TemplateEngine;
|
||||||
|
import org.springframework.restdocs.templates.TemplateFormat;
|
||||||
|
import org.springframework.restdocs.templates.TemplateResourceResolver;
|
||||||
|
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
|
||||||
|
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.springframework.restdocs.payload.PayloadDocumentation.beneathPath;
|
||||||
|
import static org.springframework.restdocs.payload.PayloadDocumentation.responseBody;
|
||||||
|
import static org.springframework.restdocs.snippet.Attributes.attributes;
|
||||||
|
import static org.springframework.restdocs.snippet.Attributes.key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ResponseBodySnippet}.
|
||||||
|
*
|
||||||
|
* @author Andy Wilkinson
|
||||||
|
*/
|
||||||
|
public class ResponseBodySnippetTests extends AbstractSnippetTests {
|
||||||
|
|
||||||
|
public ResponseBodySnippetTests(String name, TemplateFormat templateFormat) {
|
||||||
|
super(name, templateFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void responseWithBody() throws IOException {
|
||||||
|
this.snippets.expect("response-body")
|
||||||
|
.withContents(codeBlock(null, "nowrap").content("some content"));
|
||||||
|
|
||||||
|
PayloadDocumentation.responseBody().document(
|
||||||
|
this.operationBuilder.response().content("some content").build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void responseWithNoBody() throws IOException {
|
||||||
|
this.snippets.expect("response-body")
|
||||||
|
.withContents(codeBlock(null, "nowrap").content(""));
|
||||||
|
PayloadDocumentation.responseBody()
|
||||||
|
.document(this.operationBuilder.response().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void subsectionOfResponseBody() throws IOException {
|
||||||
|
this.snippets.expect("response-body-beneath-a.b")
|
||||||
|
.withContents(codeBlock(null, "nowrap").content("{\"c\":5}"));
|
||||||
|
|
||||||
|
responseBody(beneathPath("a.b")).document(this.operationBuilder.response()
|
||||||
|
.content("{\"a\":{\"b\":{\"c\":5}}}").build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void customSnippetAttributes() throws IOException {
|
||||||
|
this.snippets.expect("response-body")
|
||||||
|
.withContents(codeBlock("json", "nowrap").content("{\"a\":\"alpha\"}"));
|
||||||
|
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||||
|
given(resolver.resolveTemplateResource("response-body"))
|
||||||
|
.willReturn(snippetResource("response-body-with-language"));
|
||||||
|
responseBody(
|
||||||
|
attributes(
|
||||||
|
key("language").value("json")))
|
||||||
|
.document(
|
||||||
|
this.operationBuilder
|
||||||
|
.attribute(TemplateEngine.class.getName(),
|
||||||
|
new MustacheTemplateEngine(
|
||||||
|
resolver))
|
||||||
|
.response().content("{\"a\":\"alpha\"}")
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -95,6 +95,15 @@ public final class SnippetMatchers {
|
|||||||
return new MarkdownCodeBlockMatcher(language);
|
return new MarkdownCodeBlockMatcher(language);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "rawtypes" })
|
||||||
|
public static CodeBlockMatcher<?> codeBlock(TemplateFormat format, String language,
|
||||||
|
String options) {
|
||||||
|
if ("adoc".equals(format.getFileExtension())) {
|
||||||
|
return new AsciidoctorCodeBlockMatcher(language, options);
|
||||||
|
}
|
||||||
|
return new MarkdownCodeBlockMatcher(language);
|
||||||
|
}
|
||||||
|
|
||||||
private static abstract class AbstractSnippetContentMatcher
|
private static abstract class AbstractSnippetContentMatcher
|
||||||
extends BaseMatcher<String> {
|
extends BaseMatcher<String> {
|
||||||
|
|
||||||
@@ -186,7 +195,7 @@ public final class SnippetMatchers {
|
|||||||
|
|
||||||
protected AsciidoctorCodeBlockMatcher(String language, String options) {
|
protected AsciidoctorCodeBlockMatcher(String language, String options) {
|
||||||
super(TemplateFormats.asciidoctor());
|
super(TemplateFormats.asciidoctor());
|
||||||
this.addLine("[source," + language
|
this.addLine("[source" + (language == null ? "" : "," + language)
|
||||||
+ (options == null ? "" : ",options=\"" + options + "\"") + "]");
|
+ (options == null ? "" : ",options=\"" + options + "\"") + "]");
|
||||||
this.addLine("----");
|
this.addLine("----");
|
||||||
this.addLine("----");
|
this.addLine("----");
|
||||||
@@ -204,7 +213,7 @@ public final class SnippetMatchers {
|
|||||||
|
|
||||||
protected MarkdownCodeBlockMatcher(String language) {
|
protected MarkdownCodeBlockMatcher(String language) {
|
||||||
super(TemplateFormats.markdown());
|
super(TemplateFormats.markdown());
|
||||||
this.addLine("```" + language);
|
this.addLine("```" + (language == null ? "" : language));
|
||||||
this.addLine("```");
|
this.addLine("```");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
[source,{{language}},options="nowrap"]
|
||||||
|
----
|
||||||
|
{{body}}
|
||||||
|
----
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
[source,{{language}},options="nowrap"]
|
||||||
|
----
|
||||||
|
{{body}}
|
||||||
|
----
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
[source,{{language}},options="nowrap"]
|
||||||
|
----
|
||||||
|
{{body}}
|
||||||
|
----
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
```{{language}}
|
||||||
|
{{body}}
|
||||||
|
```
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
```{{language}}
|
||||||
|
{{body}}
|
||||||
|
```
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
```{{language}}
|
||||||
|
{{body}}
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user