Allow documentation of extra attributes that apply to the whole snippet
This commit adds support for associating custom attributes with the generation of a particular snippet. The attributes are included in the model during snippet rendering allowing them to be referenced from a custom snippet template. Among other things, this makes it possible to provide a configurable title for snippets that produce a code block. Closes gh-77
This commit is contained in:
@@ -283,39 +283,52 @@ override the template for the `curl-request.adoc` snippet, create a template nam
|
||||
|
||||
[[documenting-your-api-customizing-including-extra-information]]
|
||||
==== Including extra information
|
||||
The descriptors for fields, links, and query parameters all have an `attribute` method
|
||||
that can be used to associate one or more key-value pairs with the descriptor. These
|
||||
attributes are made available during the template rendering process. Coupled with
|
||||
a custom snippet template, this makes it possible to include extra information in a
|
||||
generated snippet.
|
||||
|
||||
A concrete example of the above is the addition of a constraints column when documenting
|
||||
request fields. The first step is to provide a `constraints` attribute for each field that
|
||||
you are documenting:
|
||||
There are two ways to provide extra information for inclusion in a generated snippet:
|
||||
|
||||
. Use the `attributes` method on a field, link or query parameter descriptor to add one or
|
||||
more attributes to an individual descriptor
|
||||
. Pass in some attributes when calling `withCurlRequest`, `withHttpRequest`,
|
||||
`withHttpResponse`, etc on `RestDocumentationResultHandler`. Such attributes will be
|
||||
associated with the snippet as a whole.
|
||||
|
||||
Any additional attributes are made available during the template rendering process.
|
||||
Coupled with a custom snippet template, this makes it possible to include extra
|
||||
information in a generated snippet.
|
||||
|
||||
A concrete example of the above is the addition of a constraints column and a title when
|
||||
documenting request fields. The first step is to provide a `constraints` attribute for
|
||||
each field that you are documenting and to provide a `title` attribute:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::{examples-dir}/com/example/Payload.java[tags=constraints]
|
||||
----
|
||||
<1> Configure the `title` attribute for the request fields snippet
|
||||
<2> Set the `constraints` attribute for the `name` field
|
||||
<3> Set the `constraints` attribute for the `email` field
|
||||
|
||||
The second step is to provide a custom template named `request-fields.snippet` that
|
||||
includes the information about the fields' constraints in the generated snippet's table:
|
||||
includes the information about the fields' constraints in the generated snippet's table
|
||||
and adds a title:
|
||||
|
||||
[source,indent=0]
|
||||
----
|
||||
.{{title}} <1>
|
||||
|===
|
||||
|Path|Type|Description|Constraints <1>
|
||||
|Path|Type|Description|Constraints <2>
|
||||
|
||||
{{#fields}}
|
||||
|{{path}}
|
||||
|{{type}}
|
||||
|{{description}}
|
||||
|{{constraints}} <2>
|
||||
|{{constraints}} <3>
|
||||
|
||||
{{/fields}}
|
||||
|===
|
||||
----
|
||||
<1> Add a new column named "Constraints"
|
||||
<2> Include the descriptors' `constraints` attribute in each row of the table
|
||||
<1> Add a title to the table
|
||||
<2> Add a new column named "Constraints"
|
||||
<3> Include the descriptors' `constraints` attribute in each row of the table
|
||||
|
||||
|
||||
|
||||
@@ -17,12 +17,16 @@
|
||||
package com.example;
|
||||
|
||||
import static org.springframework.restdocs.RestDocumentation.document;
|
||||
import static org.springframework.restdocs.Attributes.attributes;
|
||||
import static org.springframework.restdocs.Attributes.key;
|
||||
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.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.springframework.restdocs.Attributes.key;
|
||||
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.restdocs.Attributes;
|
||||
import org.springframework.restdocs.payload.FieldType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
@@ -57,12 +61,16 @@ private MockMvc mockMvc;
|
||||
.andExpect(status().isOk())
|
||||
// tag::constraints[]
|
||||
.andDo(document("create-user").withRequestFields(
|
||||
attributes(
|
||||
key("title").value("Fields for user creation")), // <1>
|
||||
fieldWithPath("name")
|
||||
.description("The user's name")
|
||||
.attribute("constraints", "Must not be null. Must not be empty"),
|
||||
.attributes(
|
||||
key("constraints").value("Must not be null. Must not be empty")), // <2>
|
||||
fieldWithPath("email")
|
||||
.description("The user's email address")
|
||||
.attribute("constrains", "Must be a valid email address")));
|
||||
.attributes(
|
||||
key("constraints").value("Must be a valid email address")))); // <3>
|
||||
// end::constraints[]
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ package org.springframework.restdocs;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.restdocs.Attributes.Attribute;
|
||||
|
||||
/**
|
||||
* Base class for descriptors. Provides the ability to associate arbitrary attributes with
|
||||
* a descriptor.
|
||||
@@ -13,17 +15,19 @@ import java.util.Map;
|
||||
*/
|
||||
public abstract class AbstractDescriptor<T extends AbstractDescriptor<T>> {
|
||||
|
||||
private final Map<String, Object> attributes = new HashMap<>();
|
||||
private Map<String, Object> attributes = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Sets an attribute with the given {@code name} and {@code value}.
|
||||
* Sets the descriptor's attributes
|
||||
*
|
||||
* @param name The name of the attribute
|
||||
* @param value The value of the attribute
|
||||
* @param attributes the attributes
|
||||
* @return the descriptor
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T attribute(String name, Object value) {
|
||||
this.attributes.put(name, value);
|
||||
public T attributes(Attribute... attributes) {
|
||||
for (Attribute attribute : attributes) {
|
||||
this.attributes.put(attribute.getKey(), attribute.getValue());
|
||||
}
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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 org.springframework.restdocs;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A fluent API for building a map of attributes
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public abstract class Attributes {
|
||||
|
||||
private Attributes() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an attribute with the given {@code key}. A value for the attribute must
|
||||
* still be specified.
|
||||
*
|
||||
* @param key The key of the attribute
|
||||
* @return An {@code AttributeBuilder} to use to specify the value of the attribute
|
||||
* @see AttributeBuilder#value(Object)
|
||||
*/
|
||||
public static AttributeBuilder key(String key) {
|
||||
return new AttributeBuilder(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code Map} of the given {@code attributes}.
|
||||
*
|
||||
* @param attributes The attributes
|
||||
* @return A Map of the attributes
|
||||
*/
|
||||
public static Map<String, Object> attributes(Attribute... attributes) {
|
||||
Map<String, Object> attributeMap = new HashMap<>();
|
||||
for (Attribute attribute : attributes) {
|
||||
attributeMap.put(attribute.getKey(), attribute.getValue());
|
||||
}
|
||||
return attributeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple builder for an attribute (key-value pair)
|
||||
*/
|
||||
public static class AttributeBuilder {
|
||||
|
||||
private final String key;
|
||||
|
||||
private AttributeBuilder(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the value of the attribute
|
||||
*
|
||||
* @param value The attribute's value
|
||||
* @return A newly created {@code Attribute}
|
||||
*/
|
||||
public Attribute value(Object value) {
|
||||
return new Attribute(this.key, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An attribute (key-value pair).
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public static class Attribute {
|
||||
|
||||
private final String key;
|
||||
|
||||
private final Object value;
|
||||
|
||||
public Attribute(String key, Object value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import static org.springframework.restdocs.request.RequestDocumentation.document
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.restdocs.hypermedia.HypermediaDocumentation;
|
||||
import org.springframework.restdocs.hypermedia.LinkDescriptor;
|
||||
@@ -35,6 +36,7 @@ import org.springframework.restdocs.payload.FieldDescriptor;
|
||||
import org.springframework.restdocs.payload.PayloadDocumentation;
|
||||
import org.springframework.restdocs.request.ParameterDescriptor;
|
||||
import org.springframework.restdocs.request.RequestDocumentation;
|
||||
import org.springframework.restdocs.snippet.SnippetWritingResultHandler;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.ResultHandler;
|
||||
|
||||
@@ -49,15 +51,55 @@ public class RestDocumentationResultHandler implements ResultHandler {
|
||||
|
||||
private final String outputDir;
|
||||
|
||||
private List<ResultHandler> delegates = new ArrayList<>();
|
||||
private SnippetWritingResultHandler curlRequest;
|
||||
|
||||
private SnippetWritingResultHandler httpRequest;
|
||||
|
||||
private SnippetWritingResultHandler httpResponse;
|
||||
|
||||
private List<SnippetWritingResultHandler> delegates = new ArrayList<>();
|
||||
|
||||
RestDocumentationResultHandler(String outputDir) {
|
||||
this.outputDir = outputDir;
|
||||
this.curlRequest = documentCurlRequest(this.outputDir, null);
|
||||
this.httpRequest = documentHttpRequest(this.outputDir, null);
|
||||
this.httpResponse = documentHttpResponse(this.outputDir, null);
|
||||
}
|
||||
|
||||
this.delegates = new ArrayList<ResultHandler>();
|
||||
this.delegates.add(documentCurlRequest(this.outputDir));
|
||||
this.delegates.add(documentHttpRequest(this.outputDir));
|
||||
this.delegates.add(documentHttpResponse(this.outputDir));
|
||||
/**
|
||||
* Customizes the default curl request snippet generation to make the given attributes
|
||||
* available.
|
||||
*
|
||||
* @param attributes the attributes
|
||||
* @return {@code this}
|
||||
*/
|
||||
public RestDocumentationResultHandler withCurlRequest(Map<String, Object> attributes) {
|
||||
this.curlRequest = documentCurlRequest(this.outputDir, attributes);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Customizes the default HTTP request snippet generation to make the given attributes
|
||||
* available.
|
||||
*
|
||||
* @param attributes the attributes
|
||||
* @return {@code this}
|
||||
*/
|
||||
public RestDocumentationResultHandler withHttpRequest(Map<String, Object> attributes) {
|
||||
this.httpRequest = documentHttpRequest(this.outputDir, attributes);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Customizes the default HTTP response snippet generation to make the given
|
||||
* attributes available.
|
||||
*
|
||||
* @param attributes the attributes
|
||||
* @return {@code this}
|
||||
*/
|
||||
public RestDocumentationResultHandler withHttpResponse(Map<String, Object> attributes) {
|
||||
this.httpResponse = documentHttpResponse(this.outputDir, attributes);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,7 +117,7 @@ public class RestDocumentationResultHandler implements ResultHandler {
|
||||
* @see LinkExtractors#extractorForContentType(String)
|
||||
*/
|
||||
public RestDocumentationResultHandler withLinks(LinkDescriptor... descriptors) {
|
||||
return withLinks(null, descriptors);
|
||||
return withLinks(null, null, descriptors);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,7 +136,50 @@ public class RestDocumentationResultHandler implements ResultHandler {
|
||||
*/
|
||||
public RestDocumentationResultHandler withLinks(LinkExtractor linkExtractor,
|
||||
LinkDescriptor... descriptors) {
|
||||
this.delegates.add(documentLinks(this.outputDir, linkExtractor, descriptors));
|
||||
return this.withLinks(null, linkExtractor, descriptors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Document the links in the response using the given {@code descriptors}. The links
|
||||
* are extracted from the response based on its content type. The given
|
||||
* {@code attributes} are made available during the generation of the links snippet.
|
||||
* <p>
|
||||
* If a link is present in the response but is not described by one of the descriptors
|
||||
* a failure will occur when this handler is invoked. Similarly, if a link is
|
||||
* described but is not present in the response a failure will also occur when this
|
||||
* handler is invoked.
|
||||
*
|
||||
* @param attributes the attributes
|
||||
* @param descriptors the link descriptors
|
||||
* @return {@code this}
|
||||
* @see HypermediaDocumentation#linkWithRel(String)
|
||||
* @see LinkExtractors#extractorForContentType(String)
|
||||
*/
|
||||
public RestDocumentationResultHandler withLinks(Map<String, Object> attributes,
|
||||
LinkDescriptor... descriptors) {
|
||||
return withLinks(attributes, null, descriptors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Document the links in the response using the given {@code descriptors}. The links
|
||||
* are extracted from the response using the given {@code linkExtractor}. The given
|
||||
* {@code attributes} are made available during the generation of the links snippet.
|
||||
* <p>
|
||||
* If a link is present in the response but is not described by one of the descriptors
|
||||
* a failure will occur when this handler is invoked. Similarly, if a link is
|
||||
* described but is not present in the response a failure will also occur when this
|
||||
* handler is invoked.
|
||||
*
|
||||
* @param attributes the attributes
|
||||
* @param linkExtractor used to extract the links from the response
|
||||
* @param descriptors the link descriptors
|
||||
* @return {@code this}
|
||||
* @see HypermediaDocumentation#linkWithRel(String)
|
||||
*/
|
||||
public RestDocumentationResultHandler withLinks(Map<String, Object> attributes,
|
||||
LinkExtractor linkExtractor, LinkDescriptor... descriptors) {
|
||||
this.delegates.add(documentLinks(this.outputDir, attributes, linkExtractor,
|
||||
descriptors));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -114,7 +199,30 @@ public class RestDocumentationResultHandler implements ResultHandler {
|
||||
*/
|
||||
public RestDocumentationResultHandler withRequestFields(
|
||||
FieldDescriptor... descriptors) {
|
||||
this.delegates.add(documentRequestFields(this.outputDir, descriptors));
|
||||
return this.withRequestFields(null, descriptors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Document the fields in the request using the given {@code descriptors}. The given
|
||||
* {@code attributes} are made available during the generation of the request fields
|
||||
* snippet.
|
||||
* <p>
|
||||
* If a field is present in the request but is not documented by one of the
|
||||
* descriptors a failure will occur when this handler is invoked. Similarly, if a
|
||||
* field is documented, is not marked as optional, and is not present in the request a
|
||||
* failure will also occur. For payloads with a hierarchical structure, documenting a
|
||||
* field is sufficient for all of its descendants to also be treated as having been
|
||||
* documented.
|
||||
*
|
||||
* @param descriptors the link descriptors
|
||||
* @param attributes the attributes
|
||||
* @return {@code this}
|
||||
* @see PayloadDocumentation#fieldWithPath(String)
|
||||
*/
|
||||
public RestDocumentationResultHandler withRequestFields(
|
||||
Map<String, Object> attributes, FieldDescriptor... descriptors) {
|
||||
this.delegates
|
||||
.add(documentRequestFields(this.outputDir, attributes, descriptors));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -134,7 +242,30 @@ public class RestDocumentationResultHandler implements ResultHandler {
|
||||
*/
|
||||
public RestDocumentationResultHandler withResponseFields(
|
||||
FieldDescriptor... descriptors) {
|
||||
this.delegates.add(documentResponseFields(this.outputDir, descriptors));
|
||||
return this.withResponseFields(null, descriptors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Document the fields in the response using the given {@code descriptors}. The given
|
||||
* {@code attributes} are made available during the generation of the request fields
|
||||
* snippet.
|
||||
* <p>
|
||||
* If a field is present in the response but is not documented by one of the
|
||||
* descriptors a failure will occur when this handler is invoked. Similarly, if a
|
||||
* field is documented, is not marked as optional, and is not present in the response
|
||||
* a failure will also occur. For payloads with a hierarchical structure, documenting
|
||||
* a field is sufficient for all of its descendants to also be treated as having been
|
||||
* documented.
|
||||
*
|
||||
* @param descriptors the link descriptors
|
||||
* @param attributes the attributes
|
||||
* @return {@code this}
|
||||
* @see PayloadDocumentation#fieldWithPath(String)
|
||||
*/
|
||||
public RestDocumentationResultHandler withResponseFields(
|
||||
Map<String, Object> attributes, FieldDescriptor... descriptors) {
|
||||
this.delegates
|
||||
.add(documentResponseFields(this.outputDir, attributes, descriptors));
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -153,12 +284,36 @@ public class RestDocumentationResultHandler implements ResultHandler {
|
||||
*/
|
||||
public RestDocumentationResultHandler withQueryParameters(
|
||||
ParameterDescriptor... descriptors) {
|
||||
this.delegates.add(documentQueryParameters(this.outputDir, descriptors));
|
||||
return this.withQueryParameters(null, descriptors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Documents the parameters in the request's query string using the given
|
||||
* {@code descriptors}. The given {@code attributes} are made available during the
|
||||
* generation of the query parameters snippet.
|
||||
* <p>
|
||||
* If a parameter is present in the query string but is not described by one of the
|
||||
* descriptors a failure will occur when this handler is invoked. Similarly, if a
|
||||
* parameter is described but is not present in the request a failure will also occur
|
||||
* when this handler is invoked.
|
||||
*
|
||||
* @param descriptors the parameter descriptors
|
||||
* @param attributes the attributes
|
||||
* @return {@code this}
|
||||
* @see RequestDocumentation#parameterWithName(String)
|
||||
*/
|
||||
public RestDocumentationResultHandler withQueryParameters(
|
||||
Map<String, Object> attributes, ParameterDescriptor... descriptors) {
|
||||
this.delegates.add(documentQueryParameters(this.outputDir, attributes,
|
||||
descriptors));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(MvcResult result) throws Exception {
|
||||
this.curlRequest.handle(result);
|
||||
this.httpRequest.handle(result);
|
||||
this.httpResponse.handle(result);
|
||||
for (ResultHandler delegate : this.delegates) {
|
||||
delegate.handle(result);
|
||||
}
|
||||
|
||||
@@ -50,10 +50,13 @@ public abstract class CurlDocumentation {
|
||||
* Produces a documentation snippet containing the request formatted as a cURL command
|
||||
*
|
||||
* @param outputDir The directory to which snippet should be written
|
||||
* @param attributes Attributes made available during rendering of the curl request
|
||||
* snippet
|
||||
* @return the handler that will produce the snippet
|
||||
*/
|
||||
public static SnippetWritingResultHandler documentCurlRequest(String outputDir) {
|
||||
return new CurlRequestWritingResultHandler(outputDir);
|
||||
public static SnippetWritingResultHandler documentCurlRequest(String outputDir,
|
||||
Map<String, Object> attributes) {
|
||||
return new CurlRequestWritingResultHandler(outputDir, attributes);
|
||||
}
|
||||
|
||||
private static final class CurlRequestWritingResultHandler extends
|
||||
@@ -67,15 +70,16 @@ public abstract class CurlDocumentation {
|
||||
|
||||
private static final int STANDARD_PORT_HTTPS = 443;
|
||||
|
||||
private CurlRequestWritingResultHandler(String outputDir) {
|
||||
super(outputDir, "curl-request");
|
||||
private CurlRequestWritingResultHandler(String outputDir,
|
||||
Map<String, Object> attributes) {
|
||||
super(outputDir, "curl-request", attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(MvcResult result, PrintWriter writer) throws IOException {
|
||||
Map<String, Object> context = new HashMap<String, Object>();
|
||||
context.put("language", "bash");
|
||||
context.put("arguments", getCurlCommandArguments(result));
|
||||
context.putAll(getAttributes());
|
||||
|
||||
TemplateEngine templateEngine = (TemplateEngine) result.getRequest()
|
||||
.getAttribute(TemplateEngine.class.getName());
|
||||
|
||||
@@ -54,10 +54,13 @@ public abstract class HttpDocumentation {
|
||||
* request
|
||||
*
|
||||
* @param outputDir The directory to which snippet should be written
|
||||
* @param attributes Attributes made available during rendering of the HTTP requst
|
||||
* snippet
|
||||
* @return the handler that will produce the snippet
|
||||
*/
|
||||
public static SnippetWritingResultHandler documentHttpRequest(String outputDir) {
|
||||
return new HttpRequestWritingResultHandler(outputDir);
|
||||
public static SnippetWritingResultHandler documentHttpRequest(String outputDir,
|
||||
Map<String, Object> attributes) {
|
||||
return new HttpRequestWritingResultHandler(outputDir, attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,18 +68,22 @@ public abstract class HttpDocumentation {
|
||||
* response sent by the server
|
||||
*
|
||||
* @param outputDir The directory to which snippet should be written
|
||||
* @param attributes Attributes made available during rendering of the HTTP response
|
||||
* snippet
|
||||
* @return the handler that will produce the snippet
|
||||
*/
|
||||
public static SnippetWritingResultHandler documentHttpResponse(String outputDir) {
|
||||
return new HttpResponseWritingResultHandler(outputDir);
|
||||
public static SnippetWritingResultHandler documentHttpResponse(String outputDir,
|
||||
Map<String, Object> attributes) {
|
||||
return new HttpResponseWritingResultHandler(outputDir, attributes);
|
||||
|
||||
}
|
||||
|
||||
private static final class HttpRequestWritingResultHandler extends
|
||||
SnippetWritingResultHandler {
|
||||
|
||||
private HttpRequestWritingResultHandler(String outputDir) {
|
||||
super(outputDir, "http-request");
|
||||
private HttpRequestWritingResultHandler(String outputDir,
|
||||
Map<String, Object> attributes) {
|
||||
super(outputDir, "http-request", attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,11 +91,11 @@ public abstract class HttpDocumentation {
|
||||
DocumentableHttpServletRequest request = new DocumentableHttpServletRequest(
|
||||
result.getRequest());
|
||||
Map<String, Object> context = new HashMap<String, Object>();
|
||||
context.put("language", "http");
|
||||
context.put("method", result.getRequest().getMethod());
|
||||
context.put("path", request.getRequestUriWithQueryString());
|
||||
context.put("headers", getHeaders(request));
|
||||
context.put("requestBody", getRequestBody(request));
|
||||
context.putAll(getAttributes());
|
||||
|
||||
TemplateEngine templateEngine = (TemplateEngine) result.getRequest()
|
||||
.getAttribute(TemplateEngine.class.getName());
|
||||
@@ -212,8 +219,9 @@ public abstract class HttpDocumentation {
|
||||
private static final class HttpResponseWritingResultHandler extends
|
||||
SnippetWritingResultHandler {
|
||||
|
||||
private HttpResponseWritingResultHandler(String outputDir) {
|
||||
super(outputDir, "http-response");
|
||||
private HttpResponseWritingResultHandler(String outputDir,
|
||||
Map<String, Object> attributes) {
|
||||
super(outputDir, "http-response", attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -225,7 +233,6 @@ public abstract class HttpDocumentation {
|
||||
StringUtils.hasLength(result.getResponse().getContentAsString()) ? String
|
||||
.format("%n%s", result.getResponse().getContentAsString())
|
||||
: "");
|
||||
context.put("language", "http");
|
||||
context.put("statusCode", status.value());
|
||||
context.put("statusReason", status.getReasonPhrase());
|
||||
|
||||
@@ -238,6 +245,8 @@ public abstract class HttpDocumentation {
|
||||
}
|
||||
}
|
||||
|
||||
context.putAll(getAttributes());
|
||||
|
||||
TemplateEngine templateEngine = (TemplateEngine) result.getRequest()
|
||||
.getAttribute(TemplateEngine.class.getName());
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.restdocs.hypermedia;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.restdocs.RestDocumentationResultHandler;
|
||||
|
||||
@@ -48,6 +49,7 @@ public abstract class HypermediaDocumentation {
|
||||
* snippet for a response's links.
|
||||
*
|
||||
* @param outputDir The directory to which the snippet should be written
|
||||
* @param attributes Attributes made available during rendering of the links snippet
|
||||
* @param linkExtractor Used to extract the links from the response
|
||||
* @param descriptors The descriptions of the response's links
|
||||
* @return the handler
|
||||
@@ -55,8 +57,9 @@ public abstract class HypermediaDocumentation {
|
||||
* @see RestDocumentationResultHandler#withLinks(LinkExtractor, LinkDescriptor...)
|
||||
*/
|
||||
public static LinkSnippetResultHandler documentLinks(String outputDir,
|
||||
LinkExtractor linkExtractor, LinkDescriptor... descriptors) {
|
||||
return new LinkSnippetResultHandler(outputDir, linkExtractor,
|
||||
Map<String, Object> attributes, LinkExtractor linkExtractor,
|
||||
LinkDescriptor... descriptors) {
|
||||
return new LinkSnippetResultHandler(outputDir, attributes, linkExtractor,
|
||||
Arrays.asList(descriptors));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,13 +43,13 @@ public class LinkSnippetResultHandler extends SnippetWritingResultHandler {
|
||||
|
||||
private final Map<String, LinkDescriptor> descriptorsByRel = new LinkedHashMap<>();
|
||||
|
||||
private final Set<String> requiredRels = new HashSet<String>();
|
||||
private final Set<String> requiredRels = new HashSet<>();
|
||||
|
||||
private final LinkExtractor extractor;
|
||||
|
||||
LinkSnippetResultHandler(String outputDir, LinkExtractor linkExtractor,
|
||||
List<LinkDescriptor> descriptors) {
|
||||
super(outputDir, "links");
|
||||
LinkSnippetResultHandler(String outputDir, Map<String, Object> attributes,
|
||||
LinkExtractor linkExtractor, List<LinkDescriptor> descriptors) {
|
||||
super(outputDir, "links", attributes);
|
||||
this.extractor = linkExtractor;
|
||||
for (LinkDescriptor descriptor : descriptors) {
|
||||
Assert.hasText(descriptor.getRel());
|
||||
@@ -117,6 +117,7 @@ public class LinkSnippetResultHandler extends SnippetWritingResultHandler {
|
||||
.getAttribute(TemplateEngine.class.getName());
|
||||
Map<String, Object> context = new HashMap<>();
|
||||
context.put("links", createLinksModel());
|
||||
context.putAll(getAttributes());
|
||||
writer.print(templateEngine.compileTemplate("links").render(context));
|
||||
}
|
||||
|
||||
|
||||
@@ -55,8 +55,8 @@ public abstract class FieldSnippetResultHandler extends SnippetWritingResultHand
|
||||
private List<FieldDescriptor> fieldDescriptors;
|
||||
|
||||
FieldSnippetResultHandler(String outputDir, String type,
|
||||
List<FieldDescriptor> descriptors) {
|
||||
super(outputDir, type + "-fields");
|
||||
Map<String, Object> attributes, List<FieldDescriptor> descriptors) {
|
||||
super(outputDir, type + "-fields", attributes);
|
||||
this.templateName = type + "-fields";
|
||||
for (FieldDescriptor descriptor : descriptors) {
|
||||
Assert.notNull(descriptor.getPath());
|
||||
@@ -83,6 +83,7 @@ public abstract class FieldSnippetResultHandler extends SnippetWritingResultHand
|
||||
}
|
||||
fields.add(descriptor.toModel());
|
||||
}
|
||||
context.putAll(getAttributes());
|
||||
TemplateEngine templateEngine = (TemplateEngine) result.getRequest()
|
||||
.getAttribute(TemplateEngine.class.getName());
|
||||
writer.print(templateEngine.compileTemplate(this.templateName).render(context));
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.restdocs.payload;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.restdocs.RestDocumentationResultHandler;
|
||||
|
||||
@@ -107,14 +108,16 @@ public abstract class PayloadDocumentation {
|
||||
* documented.
|
||||
*
|
||||
* @param outputDir The directory to which the snippet should be written
|
||||
* @param attributes Attributes made available during rendering of the links snippet
|
||||
* @param descriptors The descriptions of the request's fields
|
||||
* @return the handler
|
||||
* @see RestDocumentationResultHandler#withRequestFields(FieldDescriptor...)
|
||||
* @see #fieldWithPath(String)
|
||||
*/
|
||||
public static FieldSnippetResultHandler documentRequestFields(String outputDir,
|
||||
FieldDescriptor... descriptors) {
|
||||
return new RequestFieldSnippetResultHandler(outputDir, Arrays.asList(descriptors));
|
||||
Map<String, Object> attributes, FieldDescriptor... descriptors) {
|
||||
return new RequestFieldSnippetResultHandler(outputDir, attributes,
|
||||
Arrays.asList(descriptors));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,13 +132,14 @@ public abstract class PayloadDocumentation {
|
||||
* documented.
|
||||
*
|
||||
* @param outputDir The directory to which the snippet should be written
|
||||
* @param attributes Attributes made available during rendering of the links snippet
|
||||
* @param descriptors The descriptions of the response's fields
|
||||
* @return the handler
|
||||
* @see RestDocumentationResultHandler#withResponseFields(FieldDescriptor...)
|
||||
*/
|
||||
public static FieldSnippetResultHandler documentResponseFields(String outputDir,
|
||||
FieldDescriptor... descriptors) {
|
||||
return new ResponseFieldSnippetResultHandler(outputDir,
|
||||
Map<String, Object> attributes, FieldDescriptor... descriptors) {
|
||||
return new ResponseFieldSnippetResultHandler(outputDir, attributes,
|
||||
Arrays.asList(descriptors));
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.restdocs.payload;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
|
||||
@@ -28,8 +29,9 @@ import org.springframework.test.web.servlet.MvcResult;
|
||||
*/
|
||||
public class RequestFieldSnippetResultHandler extends FieldSnippetResultHandler {
|
||||
|
||||
RequestFieldSnippetResultHandler(String outputDir, List<FieldDescriptor> descriptors) {
|
||||
super(outputDir, "request", descriptors);
|
||||
RequestFieldSnippetResultHandler(String outputDir, Map<String, Object> attributes,
|
||||
List<FieldDescriptor> descriptors) {
|
||||
super(outputDir, "request", attributes, descriptors);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
|
||||
@@ -29,8 +30,9 @@ import org.springframework.test.web.servlet.MvcResult;
|
||||
*/
|
||||
public class ResponseFieldSnippetResultHandler extends FieldSnippetResultHandler {
|
||||
|
||||
ResponseFieldSnippetResultHandler(String outputDir, List<FieldDescriptor> descriptors) {
|
||||
super(outputDir, "response", descriptors);
|
||||
ResponseFieldSnippetResultHandler(String outputDir, Map<String, Object> attributes,
|
||||
List<FieldDescriptor> descriptors) {
|
||||
super(outputDir, "response", attributes, descriptors);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -44,8 +44,8 @@ public class QueryParametersSnippetResultHandler extends SnippetWritingResultHan
|
||||
private final Map<String, ParameterDescriptor> descriptorsByName = new LinkedHashMap<>();
|
||||
|
||||
protected QueryParametersSnippetResultHandler(String outputDir,
|
||||
ParameterDescriptor... descriptors) {
|
||||
super(outputDir, "query-parameters");
|
||||
Map<String, Object> attributes, ParameterDescriptor... descriptors) {
|
||||
super(outputDir, "query-parameters", attributes);
|
||||
for (ParameterDescriptor descriptor : descriptors) {
|
||||
Assert.hasText(descriptor.getName());
|
||||
Assert.hasText(descriptor.getDescription());
|
||||
@@ -98,6 +98,7 @@ public class QueryParametersSnippetResultHandler extends SnippetWritingResultHan
|
||||
parameters.add(entry.getValue().toModel());
|
||||
}
|
||||
context.put("parameters", parameters);
|
||||
context.putAll(getAttributes());
|
||||
writer.print(templateEngine.compileTemplate("query-parameters").render(context));
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.restdocs.request;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.restdocs.RestDocumentationResultHandler;
|
||||
import org.springframework.restdocs.snippet.SnippetWritingResultHandler;
|
||||
|
||||
@@ -35,13 +37,15 @@ public abstract class RequestDocumentation {
|
||||
* documenting a request's query parameters
|
||||
*
|
||||
* @param outputDir The directory to which the snippet should be written
|
||||
* @param attributes Attributes made available during rendering of the query
|
||||
* parameters snippet
|
||||
* @param descriptors The descriptions of the parameters in the request's query string
|
||||
* @return the result handler
|
||||
* @see RestDocumentationResultHandler#withQueryParameters(ParameterDescriptor...)
|
||||
*/
|
||||
public static SnippetWritingResultHandler documentQueryParameters(String outputDir,
|
||||
ParameterDescriptor... descriptors) {
|
||||
return new QueryParametersSnippetResultHandler(outputDir, descriptors);
|
||||
Map<String, Object> attributes, ParameterDescriptor... descriptors) {
|
||||
return new QueryParametersSnippetResultHandler(outputDir, attributes, descriptors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,6 +23,8 @@ import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.restdocs.config.RestDocumentationContext;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
@@ -35,13 +37,19 @@ import org.springframework.test.web.servlet.ResultHandler;
|
||||
*/
|
||||
public abstract class SnippetWritingResultHandler implements ResultHandler {
|
||||
|
||||
private String outputDir;
|
||||
private final Map<String, Object> attributes = new HashMap<>();
|
||||
|
||||
private String fileName;
|
||||
private final String outputDir;
|
||||
|
||||
protected SnippetWritingResultHandler(String outputDir, String fileName) {
|
||||
private final String fileName;
|
||||
|
||||
protected SnippetWritingResultHandler(String outputDir, String fileName,
|
||||
Map<String, Object> attributes) {
|
||||
this.outputDir = outputDir;
|
||||
this.fileName = fileName;
|
||||
if (attributes != null) {
|
||||
this.attributes.putAll(attributes);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void handle(MvcResult result, PrintWriter writer)
|
||||
@@ -54,6 +62,10 @@ public abstract class SnippetWritingResultHandler implements ResultHandler {
|
||||
}
|
||||
}
|
||||
|
||||
protected Map<String, Object> getAttributes() {
|
||||
return this.attributes;
|
||||
}
|
||||
|
||||
private Writer createWriter() throws IOException {
|
||||
File outputFile = new OutputFileResolver().resolve(this.outputDir, this.fileName
|
||||
+ ".adoc");
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
|
||||
package org.springframework.restdocs.curl;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.restdocs.Attributes.attributes;
|
||||
import static org.springframework.restdocs.Attributes.key;
|
||||
import static org.springframework.restdocs.curl.CurlDocumentation.documentCurlRequest;
|
||||
import static org.springframework.restdocs.test.SnippetMatchers.codeBlock;
|
||||
import static org.springframework.restdocs.test.StubMvcResult.result;
|
||||
@@ -29,9 +34,13 @@ import java.io.IOException;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.restdocs.templates.TemplateEngine;
|
||||
import org.springframework.restdocs.templates.TemplateResourceResolver;
|
||||
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
|
||||
import org.springframework.restdocs.test.ExpectedSnippet;
|
||||
|
||||
/**
|
||||
@@ -54,14 +63,14 @@ public class CurlDocumentationTests {
|
||||
public void getRequest() throws IOException {
|
||||
this.snippet.expectCurlRequest("get-request").withContents(
|
||||
codeBlock("bash").content("$ curl 'http://localhost/foo' -i"));
|
||||
documentCurlRequest("get-request").handle(result(get("/foo")));
|
||||
documentCurlRequest("get-request", null).handle(result(get("/foo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonGetRequest() throws IOException {
|
||||
this.snippet.expectCurlRequest("non-get-request").withContents(
|
||||
codeBlock("bash").content("$ curl 'http://localhost/foo' -i -X POST"));
|
||||
documentCurlRequest("non-get-request").handle(result(post("/foo")));
|
||||
documentCurlRequest("non-get-request", null).handle(result(post("/foo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -69,7 +78,7 @@ public class CurlDocumentationTests {
|
||||
this.snippet.expectCurlRequest("request-with-content").withContents(
|
||||
codeBlock("bash")
|
||||
.content("$ curl 'http://localhost/foo' -i -d 'content'"));
|
||||
documentCurlRequest("request-with-content").handle(
|
||||
documentCurlRequest("request-with-content", null).handle(
|
||||
result(get("/foo").content("content")));
|
||||
}
|
||||
|
||||
@@ -79,7 +88,7 @@ public class CurlDocumentationTests {
|
||||
.withContents(
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo?param=value' -i"));
|
||||
documentCurlRequest("request-with-query-string").handle(
|
||||
documentCurlRequest("request-with-query-string", null).handle(
|
||||
result(get("/foo?param=value")));
|
||||
}
|
||||
|
||||
@@ -87,7 +96,7 @@ public class CurlDocumentationTests {
|
||||
public void requestWithOneParameter() throws IOException {
|
||||
this.snippet.expectCurlRequest("request-with-one-parameter").withContents(
|
||||
codeBlock("bash").content("$ curl 'http://localhost/foo?k1=v1' -i"));
|
||||
documentCurlRequest("request-with-one-parameter").handle(
|
||||
documentCurlRequest("request-with-one-parameter", null).handle(
|
||||
result(get("/foo").param("k1", "v1")));
|
||||
}
|
||||
|
||||
@@ -96,7 +105,7 @@ public class CurlDocumentationTests {
|
||||
this.snippet.expectCurlRequest("request-with-multiple-parameters").withContents(
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo?k1=v1&k1=v1-bis&k2=v2' -i"));
|
||||
documentCurlRequest("request-with-multiple-parameters").handle(
|
||||
documentCurlRequest("request-with-multiple-parameters", null).handle(
|
||||
result(get("/foo").param("k1", "v1").param("k2", "v2")
|
||||
.param("k1", "v1-bis")));
|
||||
}
|
||||
@@ -107,7 +116,7 @@ public class CurlDocumentationTests {
|
||||
.withContents(
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo?k1=foo+bar%26' -i"));
|
||||
documentCurlRequest("request-with-url-encoded-parameter").handle(
|
||||
documentCurlRequest("request-with-url-encoded-parameter", null).handle(
|
||||
result(get("/foo").param("k1", "foo bar&")));
|
||||
}
|
||||
|
||||
@@ -116,7 +125,7 @@ public class CurlDocumentationTests {
|
||||
this.snippet.expectCurlRequest("post-request-with-one-parameter").withContents(
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo' -i -X POST -d 'k1=v1'"));
|
||||
documentCurlRequest("post-request-with-one-parameter").handle(
|
||||
documentCurlRequest("post-request-with-one-parameter", null).handle(
|
||||
result(post("/foo").param("k1", "v1")));
|
||||
}
|
||||
|
||||
@@ -127,7 +136,7 @@ public class CurlDocumentationTests {
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo' -i -X POST"
|
||||
+ " -d 'k1=v1&k1=v1-bis&k2=v2'"));
|
||||
documentCurlRequest("post-request-with-multiple-parameters").handle(
|
||||
documentCurlRequest("post-request-with-multiple-parameters", null).handle(
|
||||
result(post("/foo").param("k1", "v1", "v1-bis").param("k2", "v2")));
|
||||
}
|
||||
|
||||
@@ -138,7 +147,7 @@ public class CurlDocumentationTests {
|
||||
.withContents(
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo' -i -X POST -d 'k1=a%26b'"));
|
||||
documentCurlRequest("post-request-with-url-encoded-parameter").handle(
|
||||
documentCurlRequest("post-request-with-url-encoded-parameter", null).handle(
|
||||
result(post("/foo").param("k1", "a&b")));
|
||||
}
|
||||
|
||||
@@ -147,7 +156,7 @@ public class CurlDocumentationTests {
|
||||
this.snippet.expectCurlRequest("put-request-with-one-parameter").withContents(
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo' -i -X PUT -d 'k1=v1'"));
|
||||
documentCurlRequest("put-request-with-one-parameter").handle(
|
||||
documentCurlRequest("put-request-with-one-parameter", null).handle(
|
||||
result(put("/foo").param("k1", "v1")));
|
||||
}
|
||||
|
||||
@@ -158,7 +167,7 @@ public class CurlDocumentationTests {
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo' -i -X PUT"
|
||||
+ " -d 'k1=v1&k1=v1-bis&k2=v2'"));
|
||||
documentCurlRequest("put-request-with-multiple-parameters").handle(
|
||||
documentCurlRequest("put-request-with-multiple-parameters", null).handle(
|
||||
result(put("/foo").param("k1", "v1", "v1-bis").param("k2", "v2")));
|
||||
}
|
||||
|
||||
@@ -168,7 +177,7 @@ public class CurlDocumentationTests {
|
||||
.withContents(
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo' -i -X PUT -d 'k1=a%26b'"));
|
||||
documentCurlRequest("put-request-with-url-encoded-parameter").handle(
|
||||
documentCurlRequest("put-request-with-url-encoded-parameter", null).handle(
|
||||
result(put("/foo").param("k1", "a&b")));
|
||||
}
|
||||
|
||||
@@ -178,7 +187,7 @@ public class CurlDocumentationTests {
|
||||
codeBlock("bash").content(
|
||||
"$ curl 'http://localhost/foo' -i"
|
||||
+ " -H 'Content-Type: application/json' -H 'a: alpha'"));
|
||||
documentCurlRequest("request-with-headers").handle(
|
||||
documentCurlRequest("request-with-headers", null).handle(
|
||||
result(get("/foo").contentType(MediaType.APPLICATION_JSON).header("a",
|
||||
"alpha")));
|
||||
}
|
||||
@@ -189,7 +198,7 @@ public class CurlDocumentationTests {
|
||||
codeBlock("bash").content("$ curl 'http://localhost:8080/foo' -i"));
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||
request.setServerPort(8080);
|
||||
documentCurlRequest("http-with-non-standard-port").handle(result(request));
|
||||
documentCurlRequest("http-with-non-standard-port", null).handle(result(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -199,7 +208,7 @@ public class CurlDocumentationTests {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||
request.setServerPort(443);
|
||||
request.setScheme("https");
|
||||
documentCurlRequest("https-with-standard-port").handle(result(request));
|
||||
documentCurlRequest("https-with-standard-port", null).handle(result(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -209,7 +218,7 @@ public class CurlDocumentationTests {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||
request.setServerPort(8443);
|
||||
request.setScheme("https");
|
||||
documentCurlRequest("https-with-non-standard-port").handle(result(request));
|
||||
documentCurlRequest("https-with-non-standard-port", null).handle(result(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -218,7 +227,7 @@ public class CurlDocumentationTests {
|
||||
codeBlock("bash").content("$ curl 'http://api.example.com/foo' -i"));
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||
request.setServerName("api.example.com");
|
||||
documentCurlRequest("request-with-custom-host").handle(result(request));
|
||||
documentCurlRequest("request-with-custom-host", null).handle(result(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -230,7 +239,7 @@ public class CurlDocumentationTests {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||
request.setServerName("api.example.com");
|
||||
request.setContextPath("/v3");
|
||||
documentCurlRequest("request-with-custom-context-with-slash").handle(
|
||||
documentCurlRequest("request-with-custom-context-with-slash", null).handle(
|
||||
result(request));
|
||||
}
|
||||
|
||||
@@ -243,7 +252,7 @@ public class CurlDocumentationTests {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||
request.setServerName("api.example.com");
|
||||
request.setContextPath("v3");
|
||||
documentCurlRequest("request-with-custom-context-without-slash").handle(
|
||||
documentCurlRequest("request-with-custom-context-without-slash", null).handle(
|
||||
result(request));
|
||||
}
|
||||
|
||||
@@ -256,7 +265,7 @@ public class CurlDocumentationTests {
|
||||
.withContents(codeBlock("bash").content(expectedContent));
|
||||
MockMultipartFile multipartFile = new MockMultipartFile("metadata",
|
||||
"{\"description\": \"foo\"}".getBytes());
|
||||
documentCurlRequest("multipart-post-no-original-filename").handle(
|
||||
documentCurlRequest("multipart-post-no-original-filename", null).handle(
|
||||
result(fileUpload("/upload").file(multipartFile)));
|
||||
}
|
||||
|
||||
@@ -270,7 +279,7 @@ public class CurlDocumentationTests {
|
||||
MockMultipartFile multipartFile = new MockMultipartFile("image",
|
||||
"documents/images/example.png", MediaType.IMAGE_PNG_VALUE,
|
||||
"bytes".getBytes());
|
||||
documentCurlRequest("multipart-post-with-content-type").handle(
|
||||
documentCurlRequest("multipart-post-with-content-type", null).handle(
|
||||
result(fileUpload("/upload").file(multipartFile)));
|
||||
}
|
||||
|
||||
@@ -283,7 +292,7 @@ public class CurlDocumentationTests {
|
||||
codeBlock("bash").content(expectedContent));
|
||||
MockMultipartFile multipartFile = new MockMultipartFile("image",
|
||||
"documents/images/example.png", null, "bytes".getBytes());
|
||||
documentCurlRequest("multipart-post").handle(
|
||||
documentCurlRequest("multipart-post", null).handle(
|
||||
result(fileUpload("/upload").file(multipartFile)));
|
||||
}
|
||||
|
||||
@@ -297,9 +306,26 @@ public class CurlDocumentationTests {
|
||||
codeBlock("bash").content(expectedContent));
|
||||
MockMultipartFile multipartFile = new MockMultipartFile("image",
|
||||
"documents/images/example.png", null, "bytes".getBytes());
|
||||
documentCurlRequest("multipart-post").handle(
|
||||
documentCurlRequest("multipart-post", null).handle(
|
||||
result(fileUpload("/upload").file(multipartFile)
|
||||
.param("a", "apple", "avocado").param("b", "banana")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customAttributes() throws IOException {
|
||||
this.snippet.expectCurlRequest("custom-attributes").withContents(
|
||||
containsString("curl request title"));
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("curl-request"))
|
||||
.thenReturn(
|
||||
new FileSystemResource(
|
||||
"src/test/resources/custom-snippet-templates/curl-request-with-title.snippet"));
|
||||
request.setAttribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(
|
||||
resolver));
|
||||
documentCurlRequest("custom-attributes",
|
||||
attributes(key("title").value("curl request title"))).handle(
|
||||
result(request));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,8 +16,13 @@
|
||||
|
||||
package org.springframework.restdocs.http;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.http.HttpStatus.BAD_REQUEST;
|
||||
import static org.springframework.http.HttpStatus.OK;
|
||||
import static org.springframework.restdocs.Attributes.attributes;
|
||||
import static org.springframework.restdocs.Attributes.key;
|
||||
import static org.springframework.restdocs.http.HttpDocumentation.documentHttpRequest;
|
||||
import static org.springframework.restdocs.http.HttpDocumentation.documentHttpResponse;
|
||||
import static org.springframework.restdocs.test.SnippetMatchers.httpRequest;
|
||||
@@ -35,11 +40,15 @@ import java.io.IOException;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.restdocs.templates.TemplateEngine;
|
||||
import org.springframework.restdocs.templates.TemplateResourceResolver;
|
||||
import org.springframework.restdocs.templates.mustache.MustacheTemplateEngine;
|
||||
import org.springframework.restdocs.test.ExpectedSnippet;
|
||||
|
||||
/**
|
||||
@@ -61,7 +70,7 @@ public class HttpDocumentationTests {
|
||||
httpRequest(GET, "/foo").header(HttpHeaders.HOST, "localhost").header(
|
||||
"Alpha", "a"));
|
||||
|
||||
documentHttpRequest("get-request").handle(
|
||||
documentHttpRequest("get-request", null).handle(
|
||||
result(get("/foo").header("Alpha", "a")));
|
||||
}
|
||||
|
||||
@@ -70,7 +79,7 @@ public class HttpDocumentationTests {
|
||||
this.snippet.expectHttpRequest("get-request-with-query-string").withContents(
|
||||
httpRequest(GET, "/foo?bar=baz").header(HttpHeaders.HOST, "localhost"));
|
||||
|
||||
documentHttpRequest("get-request-with-query-string").handle(
|
||||
documentHttpRequest("get-request-with-query-string", null).handle(
|
||||
result(get("/foo?bar=baz")));
|
||||
}
|
||||
|
||||
@@ -79,7 +88,7 @@ public class HttpDocumentationTests {
|
||||
this.snippet.expectHttpRequest("get-request-with-parameter").withContents(
|
||||
httpRequest(GET, "/foo?b%26r=baz").header(HttpHeaders.HOST, "localhost"));
|
||||
|
||||
documentHttpRequest("get-request-with-parameter").handle(
|
||||
documentHttpRequest("get-request-with-parameter", null).handle(
|
||||
result(get("/foo").param("b&r", "baz")));
|
||||
}
|
||||
|
||||
@@ -89,7 +98,7 @@ public class HttpDocumentationTests {
|
||||
httpRequest(POST, "/foo").header(HttpHeaders.HOST, "localhost").content(
|
||||
"Hello, world"));
|
||||
|
||||
documentHttpRequest("post-request-with-content").handle(
|
||||
documentHttpRequest("post-request-with-content", null).handle(
|
||||
result(post("/foo").content("Hello, world")));
|
||||
}
|
||||
|
||||
@@ -100,7 +109,7 @@ public class HttpDocumentationTests {
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.content("b%26r=baz&a=alpha"));
|
||||
|
||||
documentHttpRequest("post-request-with-parameter").handle(
|
||||
documentHttpRequest("post-request-with-parameter", null).handle(
|
||||
result(post("/foo").param("b&r", "baz").param("a", "alpha")));
|
||||
}
|
||||
|
||||
@@ -110,7 +119,7 @@ public class HttpDocumentationTests {
|
||||
httpRequest(PUT, "/foo").header(HttpHeaders.HOST, "localhost").content(
|
||||
"Hello, world"));
|
||||
|
||||
documentHttpRequest("put-request-with-content").handle(
|
||||
documentHttpRequest("put-request-with-content", null).handle(
|
||||
result(put("/foo").content("Hello, world")));
|
||||
}
|
||||
|
||||
@@ -121,14 +130,14 @@ public class HttpDocumentationTests {
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.content("b%26r=baz&a=alpha"));
|
||||
|
||||
documentHttpRequest("put-request-with-parameter").handle(
|
||||
documentHttpRequest("put-request-with-parameter", null).handle(
|
||||
result(put("/foo").param("b&r", "baz").param("a", "alpha")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicResponse() throws IOException {
|
||||
this.snippet.expectHttpResponse("basic-response").withContents(httpResponse(OK));
|
||||
documentHttpResponse("basic-response").handle(result());
|
||||
documentHttpResponse("basic-response", null).handle(result());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -138,7 +147,7 @@ public class HttpDocumentationTests {
|
||||
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.setStatus(BAD_REQUEST.value());
|
||||
documentHttpResponse("non-ok-response").handle(result(response));
|
||||
documentHttpResponse("non-ok-response", null).handle(result(response));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -151,7 +160,7 @@ public class HttpDocumentationTests {
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
response.setHeader("a", "alpha");
|
||||
documentHttpResponse("response-with-headers").handle(result(response));
|
||||
documentHttpResponse("response-with-headers", null).handle(result(response));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -160,7 +169,7 @@ public class HttpDocumentationTests {
|
||||
httpResponse(OK).content("content"));
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.getWriter().append("content");
|
||||
documentHttpResponse("response-with-content").handle(result(response));
|
||||
documentHttpResponse("response-with-content", null).handle(result(response));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -175,7 +184,7 @@ public class HttpDocumentationTests {
|
||||
.content(expectedContent));
|
||||
MockMultipartFile multipartFile = new MockMultipartFile("image",
|
||||
"documents/images/example.png", null, "<< data >>".getBytes());
|
||||
documentHttpRequest("multipart-post").handle(
|
||||
documentHttpRequest("multipart-post", null).handle(
|
||||
result(fileUpload("/upload").file(multipartFile)));
|
||||
}
|
||||
|
||||
@@ -198,7 +207,7 @@ public class HttpDocumentationTests {
|
||||
.content(expectedContent));
|
||||
MockMultipartFile multipartFile = new MockMultipartFile("image",
|
||||
"documents/images/example.png", null, "<< data >>".getBytes());
|
||||
documentHttpRequest("multipart-post").handle(
|
||||
documentHttpRequest("multipart-post", null).handle(
|
||||
result(fileUpload("/upload").file(multipartFile)
|
||||
.param("a", "apple", "avocado").param("b", "banana")));
|
||||
}
|
||||
@@ -217,7 +226,7 @@ public class HttpDocumentationTests {
|
||||
MockMultipartFile multipartFile = new MockMultipartFile("image",
|
||||
"documents/images/example.png", MediaType.IMAGE_PNG_VALUE,
|
||||
"<< data >>".getBytes());
|
||||
documentHttpRequest("multipart-post-with-content-type").handle(
|
||||
documentHttpRequest("multipart-post-with-content-type", null).handle(
|
||||
result(fileUpload("/upload").file(multipartFile)));
|
||||
}
|
||||
|
||||
@@ -229,7 +238,8 @@ public class HttpDocumentationTests {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||
request.setServerName("api.example.com");
|
||||
|
||||
documentHttpRequest("get-request-custom-server-name").handle(result(request));
|
||||
documentHttpRequest("get-request-custom-server-name", null).handle(
|
||||
result(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -237,10 +247,44 @@ public class HttpDocumentationTests {
|
||||
this.snippet.expectHttpRequest("get-request-custom-host").withContents(
|
||||
httpRequest(GET, "/foo").header(HttpHeaders.HOST, "api.example.com"));
|
||||
|
||||
documentHttpRequest("get-request-custom-host").handle(
|
||||
documentHttpRequest("get-request-custom-host", null).handle(
|
||||
result(get("/foo").header(HttpHeaders.HOST, "api.example.com")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWithCustomSnippetAttributes() throws IOException {
|
||||
this.snippet.expectHttpRequest("request-with-snippet-attributes").withContents(
|
||||
containsString("Title for the request"));
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("http-request"))
|
||||
.thenReturn(
|
||||
new FileSystemResource(
|
||||
"src/test/resources/custom-snippet-templates/http-request-with-title.snippet"));
|
||||
request.setAttribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(
|
||||
resolver));
|
||||
documentHttpRequest("request-with-snippet-attributes",
|
||||
attributes(key("title").value("Title for the request"))).handle(
|
||||
result(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseWithCustomSnippetAttributes() throws IOException {
|
||||
this.snippet.expectHttpResponse("response-with-snippet-attributes").withContents(
|
||||
containsString("Title for the response"));
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("http-response"))
|
||||
.thenReturn(
|
||||
new FileSystemResource(
|
||||
"src/test/resources/custom-snippet-templates/http-response-with-title.snippet"));
|
||||
request.setAttribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(
|
||||
resolver));
|
||||
documentHttpResponse("response-with-snippet-attributes",
|
||||
attributes(key("title").value("Title for the response"))).handle(
|
||||
result(request));
|
||||
}
|
||||
|
||||
private String createPart(String content) {
|
||||
return this.createPart(content, true);
|
||||
}
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
package org.springframework.restdocs.hypermedia;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.startsWith;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.restdocs.Attributes.attributes;
|
||||
import static org.springframework.restdocs.Attributes.key;
|
||||
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.documentLinks;
|
||||
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
|
||||
import static org.springframework.restdocs.test.StubMvcResult.result;
|
||||
@@ -57,7 +60,7 @@ public class HypermediaDocumentationTests {
|
||||
this.thrown.expect(SnippetGenerationException.class);
|
||||
this.thrown.expectMessage(equalTo("Links with the following relations were not"
|
||||
+ " documented: [foo]"));
|
||||
documentLinks("undocumented-link",
|
||||
documentLinks("undocumented-link", null,
|
||||
new StubLinkExtractor().withLinks(new Link("foo", "bar"))).handle(
|
||||
result());
|
||||
}
|
||||
@@ -67,7 +70,7 @@ public class HypermediaDocumentationTests {
|
||||
this.thrown.expect(SnippetGenerationException.class);
|
||||
this.thrown.expectMessage(equalTo("Links with the following relations were not"
|
||||
+ " found in the response: [foo]"));
|
||||
documentLinks("missing-link", new StubLinkExtractor(),
|
||||
documentLinks("missing-link", null, new StubLinkExtractor(),
|
||||
new LinkDescriptor("foo").description("bar")).handle(result());
|
||||
}
|
||||
|
||||
@@ -76,7 +79,7 @@ public class HypermediaDocumentationTests {
|
||||
this.snippet.expectLinks("documented-optional-link").withContents( //
|
||||
tableWithHeader("Relation", "Description") //
|
||||
.row("foo", "bar"));
|
||||
documentLinks("documented-optional-link",
|
||||
documentLinks("documented-optional-link", null,
|
||||
new StubLinkExtractor().withLinks(new Link("foo", "blah")),
|
||||
new LinkDescriptor("foo").description("bar").optional()).handle(result());
|
||||
}
|
||||
@@ -86,7 +89,7 @@ public class HypermediaDocumentationTests {
|
||||
this.snippet.expectLinks("missing-optional-link").withContents( //
|
||||
tableWithHeader("Relation", "Description") //
|
||||
.row("foo", "bar"));
|
||||
documentLinks("missing-optional-link", new StubLinkExtractor(),
|
||||
documentLinks("missing-optional-link", null, new StubLinkExtractor(),
|
||||
new LinkDescriptor("foo").description("bar").optional()).handle(result());
|
||||
}
|
||||
|
||||
@@ -96,7 +99,7 @@ public class HypermediaDocumentationTests {
|
||||
this.thrown.expectMessage(equalTo("Links with the following relations were not"
|
||||
+ " documented: [a]. Links with the following relations were not"
|
||||
+ " found in the response: [foo]"));
|
||||
documentLinks("undocumented-link-and-missing-link",
|
||||
documentLinks("undocumented-link-and-missing-link", null,
|
||||
new StubLinkExtractor().withLinks(new Link("a", "alpha")),
|
||||
new LinkDescriptor("foo").description("bar")).handle(result());
|
||||
}
|
||||
@@ -109,14 +112,15 @@ public class HypermediaDocumentationTests {
|
||||
.row("b", "two"));
|
||||
documentLinks(
|
||||
"documented-links",
|
||||
null,
|
||||
new StubLinkExtractor().withLinks(new Link("a", "alpha"), new Link("b",
|
||||
"bravo")), new LinkDescriptor("a").description("one"),
|
||||
new LinkDescriptor("b").description("two")).handle(result());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void linksWithCustomAttributes() throws IOException {
|
||||
this.snippet.expectLinks("documented-links").withContents( //
|
||||
public void linksWithCustomDescriptorAttributes() throws IOException {
|
||||
this.snippet.expectLinks("links-with-custom-descriptor-attributes").withContents( //
|
||||
tableWithHeader("Relation", "Description", "Foo") //
|
||||
.row("a", "one", "alpha") //
|
||||
.row("b", "two", "bravo"));
|
||||
@@ -129,12 +133,34 @@ public class HypermediaDocumentationTests {
|
||||
request.setAttribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(
|
||||
resolver));
|
||||
documentLinks(
|
||||
"documented-links",
|
||||
"links-with-custom-descriptor-attributes",
|
||||
null,
|
||||
new StubLinkExtractor().withLinks(new Link("a", "alpha"), new Link("b",
|
||||
"bravo")),
|
||||
new LinkDescriptor("a").description("one").attribute("foo", "alpha"),
|
||||
new LinkDescriptor("b").description("two").attribute("foo", "bravo"))
|
||||
.handle(result(request));
|
||||
new LinkDescriptor("a").description("one").attributes(
|
||||
key("foo").value("alpha")),
|
||||
new LinkDescriptor("b").description("two").attributes(
|
||||
key("foo").value("bravo"))).handle(result(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void linksWithCustomAttribute() throws IOException {
|
||||
this.snippet.expectLinks("links-with-custom-attribute").withContents(
|
||||
startsWith(".Title for the links"));
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("links"))
|
||||
.thenReturn(
|
||||
new FileSystemResource(
|
||||
"src/test/resources/custom-snippet-templates/links-with-title.snippet"));
|
||||
request.setAttribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(
|
||||
resolver));
|
||||
documentLinks(
|
||||
"links-with-custom-attribute",
|
||||
attributes(key("title").value("Title for the links")),
|
||||
new StubLinkExtractor().withLinks(new Link("a", "alpha"), new Link("b",
|
||||
"bravo")), new LinkDescriptor("a").description("one"),
|
||||
new LinkDescriptor("b").description("two")).handle(result(request));
|
||||
}
|
||||
|
||||
private static class StubLinkExtractor implements LinkExtractor {
|
||||
|
||||
@@ -20,6 +20,8 @@ import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.startsWith;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.restdocs.Attributes.attributes;
|
||||
import static org.springframework.restdocs.Attributes.key;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.documentRequestFields;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.documentResponseFields;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
@@ -62,7 +64,7 @@ public class PayloadDocumentationTests {
|
||||
.row("a.c", "String", "two") //
|
||||
.row("a", "Object", "three"));
|
||||
|
||||
documentRequestFields("map-request-with-fields",
|
||||
documentRequestFields("map-request-with-fields", null,
|
||||
fieldWithPath("a.b").description("one"),
|
||||
fieldWithPath("a.c").description("two"),
|
||||
fieldWithPath("a").description("three")).handle(
|
||||
@@ -77,7 +79,7 @@ public class PayloadDocumentationTests {
|
||||
.row("[]a.c", "String", "two") //
|
||||
.row("[]a", "Object", "three"));
|
||||
|
||||
documentRequestFields("array-request-with-fields",
|
||||
documentRequestFields("array-request-with-fields", null,
|
||||
fieldWithPath("[]a.b").description("one"),
|
||||
fieldWithPath("[]a.c").description("two"),
|
||||
fieldWithPath("[]a").description("three")).handle(
|
||||
@@ -100,7 +102,7 @@ public class PayloadDocumentationTests {
|
||||
response.getWriter().append(
|
||||
"{\"id\": 67,\"date\": \"2015-01-20\",\"assets\":"
|
||||
+ " [{\"id\":356,\"name\": \"sample\"}]}");
|
||||
documentResponseFields("map-response-with-fields",
|
||||
documentResponseFields("map-response-with-fields", null,
|
||||
fieldWithPath("id").description("one"),
|
||||
fieldWithPath("date").description("two"),
|
||||
fieldWithPath("assets").description("three"),
|
||||
@@ -121,7 +123,7 @@ public class PayloadDocumentationTests {
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.getWriter()
|
||||
.append("[{\"a\": {\"b\": 5}},{\"a\": {\"c\": \"charlie\"}}]");
|
||||
documentResponseFields("array-response-with-fields",
|
||||
documentResponseFields("array-response-with-fields", null,
|
||||
fieldWithPath("[]a.b").description("one"),
|
||||
fieldWithPath("[]a.c").description("two"),
|
||||
fieldWithPath("[]a").description("three")).handle(result(response));
|
||||
@@ -145,7 +147,7 @@ public class PayloadDocumentationTests {
|
||||
this.thrown
|
||||
.expectMessage(startsWith("The following parts of the payload were not"
|
||||
+ " documented:"));
|
||||
documentRequestFields("undocumented-request-fields").handle(
|
||||
documentRequestFields("undocumented-request-fields", null).handle(
|
||||
result(get("/foo").content("{\"a\": 5}")));
|
||||
}
|
||||
|
||||
@@ -155,7 +157,7 @@ public class PayloadDocumentationTests {
|
||||
this.thrown
|
||||
.expectMessage(equalTo("Fields with the following paths were not found"
|
||||
+ " in the payload: [a.b]"));
|
||||
documentRequestFields("missing-request-fields",
|
||||
documentRequestFields("missing-request-fields", null,
|
||||
fieldWithPath("a.b").description("one")).handle(
|
||||
result(get("/foo").content("{}")));
|
||||
}
|
||||
@@ -163,7 +165,7 @@ public class PayloadDocumentationTests {
|
||||
@Test
|
||||
public void missingOptionalRequestFieldWithNoTypeProvided() throws IOException {
|
||||
this.thrown.expect(FieldTypeRequiredException.class);
|
||||
documentRequestFields("missing-optional-request-field-with-no-type",
|
||||
documentRequestFields("missing-optional-request-field-with-no-type", null,
|
||||
fieldWithPath("a.b").description("one").optional()).handle(
|
||||
result(get("/foo").content("{ }")));
|
||||
}
|
||||
@@ -178,20 +180,20 @@ public class PayloadDocumentationTests {
|
||||
.expectMessage(endsWith("Fields with the following paths were not found"
|
||||
+ " in the payload: [a.b]"));
|
||||
documentRequestFields("undocumented-request-field-and-missing-request-field",
|
||||
fieldWithPath("a.b").description("one")).handle(
|
||||
null, fieldWithPath("a.b").description("one")).handle(
|
||||
result(get("/foo").content("{ \"a\": { \"c\": 5 }}")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestFieldsWithCustomAttributes() throws IOException {
|
||||
this.snippet.expectRequestFields("request-fields-with-custom-attributes")
|
||||
.withContents( //
|
||||
tableWithHeader("Path", "Type", "Description", "Foo") //
|
||||
.row("a.b", "Number", "one", "alpha") //
|
||||
.row("a.c", "String", "two", "bravo") //
|
||||
.row("a", "Object", "three", "charlie"));
|
||||
public void requestFieldsWithCustomDescriptorAttributes() throws IOException {
|
||||
this.snippet.expectRequestFields(
|
||||
"request-fields-with-custom-descriptor-attributes").withContents( //
|
||||
tableWithHeader("Path", "Type", "Description", "Foo") //
|
||||
.row("a.b", "Number", "one", "alpha") //
|
||||
.row("a.c", "String", "two", "bravo") //
|
||||
.row("a", "Object", "three", "charlie"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("fields"))
|
||||
when(resolver.resolveTemplateResource("request-fields"))
|
||||
.thenReturn(
|
||||
new FileSystemResource(
|
||||
"src/test/resources/custom-snippet-templates/request-fields-with-extra-column.snippet"));
|
||||
@@ -199,15 +201,19 @@ public class PayloadDocumentationTests {
|
||||
request.setAttribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(
|
||||
resolver));
|
||||
request.setContent("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}".getBytes());
|
||||
documentRequestFields("request-fields-with-custom-attributes",
|
||||
fieldWithPath("a.b").description("one").attribute("foo", "alpha"),
|
||||
fieldWithPath("a.c").description("two").attribute("foo", "bravo"),
|
||||
fieldWithPath("a").description("three").attribute("foo", "charlie"))
|
||||
.handle(result(request));
|
||||
documentRequestFields(
|
||||
"request-fields-with-custom-descriptor-attributes",
|
||||
null,
|
||||
fieldWithPath("a.b").description("one").attributes(
|
||||
key("foo").value("alpha")),
|
||||
fieldWithPath("a.c").description("two").attributes(
|
||||
key("foo").value("bravo")),
|
||||
fieldWithPath("a").description("three").attributes(
|
||||
key("foo").value("charlie"))).handle(result(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseFieldsWithCustomAttributes() throws IOException {
|
||||
public void responseFieldsWithCustomDescriptorAttributes() throws IOException {
|
||||
this.snippet.expectResponseFields("response-fields-with-custom-attributes")
|
||||
.withContents( //
|
||||
tableWithHeader("Path", "Type", "Description", "Foo") //
|
||||
@@ -215,7 +221,7 @@ public class PayloadDocumentationTests {
|
||||
.row("a.c", "String", "two", "bravo") //
|
||||
.row("a", "Object", "three", "charlie"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("fields"))
|
||||
when(resolver.resolveTemplateResource("response-fields"))
|
||||
.thenReturn(
|
||||
new FileSystemResource(
|
||||
"src/test/resources/custom-snippet-templates/response-fields-with-extra-column.snippet"));
|
||||
@@ -224,10 +230,53 @@ public class PayloadDocumentationTests {
|
||||
resolver));
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.getOutputStream().print("{\"a\": {\"b\": 5, \"c\": \"charlie\"}}");
|
||||
documentResponseFields("response-fields-with-custom-attributes",
|
||||
fieldWithPath("a.b").description("one").attribute("foo", "alpha"),
|
||||
fieldWithPath("a.c").description("two").attribute("foo", "bravo"),
|
||||
fieldWithPath("a").description("three").attribute("foo", "charlie"))
|
||||
.handle(result(request, response));
|
||||
documentResponseFields(
|
||||
"response-fields-with-custom-attributes",
|
||||
null,
|
||||
fieldWithPath("a.b").description("one").attributes(
|
||||
key("foo").value("alpha")),
|
||||
fieldWithPath("a.c").description("two").attributes(
|
||||
key("foo").value("bravo")),
|
||||
fieldWithPath("a").description("three").attributes(
|
||||
key("foo").value("charlie"))).handle(result(request, response));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestFieldsWithCustomAttributes() throws IOException {
|
||||
this.snippet.expectRequestFields("request-fields-with-custom-attributes")
|
||||
.withContents(startsWith(".Custom title"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("request-fields"))
|
||||
.thenReturn(
|
||||
new FileSystemResource(
|
||||
"src/test/resources/custom-snippet-templates/request-fields-with-title.snippet"));
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setAttribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(
|
||||
resolver));
|
||||
request.setContent("{\"a\": \"foo\"}".getBytes());
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
documentRequestFields("request-fields-with-custom-attributes",
|
||||
attributes(key("title").value("Custom title")),
|
||||
fieldWithPath("a").description("one")).handle(result(request, response));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void responseFieldsWithCustomAttributes() throws IOException {
|
||||
this.snippet.expectResponseFields("response-fields-with-custom-attributes")
|
||||
.withContents(startsWith(".Custom title"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("response-fields"))
|
||||
.thenReturn(
|
||||
new FileSystemResource(
|
||||
"src/test/resources/custom-snippet-templates/response-fields-with-title.snippet"));
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setAttribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(
|
||||
resolver));
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
response.getOutputStream().print("{\"a\": \"foo\"}");
|
||||
documentResponseFields("response-fields-with-custom-attributes",
|
||||
attributes(key("title").value("Custom title")),
|
||||
fieldWithPath("a").description("one")).handle(result(request, response));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
package org.springframework.restdocs.request;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.startsWith;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.restdocs.Attributes.attributes;
|
||||
import static org.springframework.restdocs.Attributes.key;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.documentQueryParameters;
|
||||
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
|
||||
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
|
||||
@@ -57,7 +60,7 @@ public class RequestDocumentationTests {
|
||||
this.thrown
|
||||
.expectMessage(equalTo("Query parameters with the following names were"
|
||||
+ " not documented: [a]"));
|
||||
documentQueryParameters("undocumented-parameter").handle(
|
||||
documentQueryParameters("undocumented-parameter", null).handle(
|
||||
result(get("/").param("a", "alpha")));
|
||||
}
|
||||
|
||||
@@ -67,7 +70,7 @@ public class RequestDocumentationTests {
|
||||
this.thrown
|
||||
.expectMessage(equalTo("Query parameters with the following names were"
|
||||
+ " not found in the request: [a]"));
|
||||
documentQueryParameters("missing-parameter",
|
||||
documentQueryParameters("missing-parameter", null,
|
||||
parameterWithName("a").description("one")).handle(result(get("/")));
|
||||
}
|
||||
|
||||
@@ -78,7 +81,7 @@ public class RequestDocumentationTests {
|
||||
.expectMessage(equalTo("Query parameters with the following names were"
|
||||
+ " not documented: [b]. Query parameters with the following"
|
||||
+ " names were not found in the request: [a]"));
|
||||
documentQueryParameters("undocumented-parameter-missing-parameter",
|
||||
documentQueryParameters("undocumented-parameter-missing-parameter", null,
|
||||
parameterWithName("a").description("one")).handle(
|
||||
result(get("/").param("b", "bravo")));
|
||||
}
|
||||
@@ -89,7 +92,7 @@ public class RequestDocumentationTests {
|
||||
.withContents(
|
||||
tableWithHeader("Parameter", "Description").row("a", "one").row(
|
||||
"b", "two"));
|
||||
documentQueryParameters("parameter-snippet-request-parameters",
|
||||
documentQueryParameters("parameter-snippet-request-parameters", null,
|
||||
parameterWithName("a").description("one"),
|
||||
parameterWithName("b").description("two")).handle(
|
||||
result(get("/").param("a", "bravo").param("b", "bravo")));
|
||||
@@ -101,15 +104,16 @@ public class RequestDocumentationTests {
|
||||
.withContents(
|
||||
tableWithHeader("Parameter", "Description").row("a", "one").row(
|
||||
"b", "two"));
|
||||
documentQueryParameters("parameter-snippet-request-uri-query-string",
|
||||
documentQueryParameters("parameter-snippet-request-uri-query-string", null,
|
||||
parameterWithName("a").description("one"),
|
||||
parameterWithName("b").description("two")).handle(
|
||||
result(get("/?a=alpha&b=bravo")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parametersWithCustomAttributes() throws IOException {
|
||||
this.snippet.expectQueryParameters("parameters-with-custom-attributes")
|
||||
public void parametersWithCustomDescriptorAttributes() throws IOException {
|
||||
this.snippet
|
||||
.expectQueryParameters("parameters-with-custom-descriptor-attributes")
|
||||
.withContents(
|
||||
tableWithHeader("Parameter", "Description", "Foo").row("a",
|
||||
"one", "alpha").row("b", "two", "bravo"));
|
||||
@@ -123,10 +127,36 @@ public class RequestDocumentationTests {
|
||||
resolver));
|
||||
request.addParameter("a", "bravo");
|
||||
request.addParameter("b", "bravo");
|
||||
documentQueryParameters("parameters-with-custom-attributes",
|
||||
parameterWithName("a").description("one").attribute("foo", "alpha"),
|
||||
parameterWithName("b").description("two").attribute("foo", "bravo"))
|
||||
.handle(result(request));
|
||||
documentQueryParameters(
|
||||
"parameters-with-custom-descriptor-attributes",
|
||||
null,
|
||||
parameterWithName("a").description("one").attributes(
|
||||
key("foo").value("alpha")),
|
||||
parameterWithName("b").description("two").attributes(
|
||||
key("foo").value("bravo"))).handle(result(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parametersWithCustomAttributes() throws IOException {
|
||||
this.snippet.expectQueryParameters("parameters-with-custom-attributes")
|
||||
.withContents(startsWith(".The title"));
|
||||
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
|
||||
when(resolver.resolveTemplateResource("query-parameters"))
|
||||
.thenReturn(
|
||||
new FileSystemResource(
|
||||
"src/test/resources/custom-snippet-templates/query-parameters-with-title.snippet"));
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setAttribute(TemplateEngine.class.getName(), new MustacheTemplateEngine(
|
||||
resolver));
|
||||
request.addParameter("a", "bravo");
|
||||
request.addParameter("b", "bravo");
|
||||
documentQueryParameters(
|
||||
"parameters-with-custom-attributes",
|
||||
attributes(key("title").value("The title")),
|
||||
parameterWithName("a").description("one").attributes(
|
||||
key("foo").value("alpha")),
|
||||
parameterWithName("b").description("two").attributes(
|
||||
key("foo").value("bravo"))).handle(result(request));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
[source,bash]
|
||||
.{{title}}
|
||||
----
|
||||
$ curl {{arguments}}
|
||||
----
|
||||
@@ -0,0 +1,9 @@
|
||||
[source,http]
|
||||
.{{title}}
|
||||
----
|
||||
{{method}} {{path}} HTTP/1.1
|
||||
{{#headers}}
|
||||
{{name}}: {{value}}
|
||||
{{/headers}}
|
||||
{{requestBody}}
|
||||
----
|
||||
@@ -0,0 +1,9 @@
|
||||
[source,http]
|
||||
.{{title}}
|
||||
----
|
||||
HTTP/1.1 {{statusCode}} {{statusReason}}
|
||||
{{#headers}}
|
||||
{{name}}: {{value}}
|
||||
{{/headers}}
|
||||
{{responseBody}}
|
||||
----
|
||||
@@ -0,0 +1,10 @@
|
||||
.{{title}}
|
||||
|===
|
||||
|Relation|Description
|
||||
|
||||
{{#links}}
|
||||
|{{rel}}
|
||||
|{{description}}
|
||||
|
||||
{{/links}}
|
||||
|===
|
||||
@@ -0,0 +1,10 @@
|
||||
.{{title}}
|
||||
|===
|
||||
|Parameter|Description
|
||||
|
||||
{{#parameters}}
|
||||
|{{name}}
|
||||
|{{description}}
|
||||
|
||||
{{/parameters}}
|
||||
|===
|
||||
@@ -0,0 +1,11 @@
|
||||
.{{title}}
|
||||
|===
|
||||
|Path|Type|Description
|
||||
|
||||
{{#fields}}
|
||||
|{{path}}
|
||||
|{{type}}
|
||||
|{{description}}
|
||||
|
||||
{{/fields}}
|
||||
|===
|
||||
@@ -0,0 +1,11 @@
|
||||
.{{title}}
|
||||
|===
|
||||
|Path|Type|Description
|
||||
|
||||
{{#fields}}
|
||||
|{{path}}
|
||||
|{{type}}
|
||||
|{{description}}
|
||||
|
||||
{{/fields}}
|
||||
|===
|
||||
Reference in New Issue
Block a user