Provide support for documenting path parameters

This commit adds support for documenting a request's path parameters.
For example:

mockMvc.perform(
		get("/locations/{latitude}/{longitude}", 51.5072, 0.1275))
			.andExpect(status().isOk())
			.andDo(document("locations").withPathParameters(
					parameterWithName("latitude")
							.description("The location's latitude"),
					parameterWithName("longitude")
							.description("The location's longitude")
			));

Closes gh-86
This commit is contained in:
Andy Wilkinson
2015-07-29 14:26:57 +01:00
parent e5c992c249
commit 8e2fc7f45d
30 changed files with 943 additions and 135 deletions

View File

@@ -18,4 +18,5 @@ asciidoctor {
}
attributes 'revnumber': project.version,
'branch-or-tag': project.version.endsWith('SNAPSHOT') ? 'master': "v${project.version}"
inputs.files(sourceSets.test.java)
}

View File

@@ -196,6 +196,32 @@ is not found in the request.
[[documenting-your-api-path-parameters]]
=== Path parameters
A request's path parameters can be documented using `withPathParameters`
[source,java,indent=0]
----
include::{examples-dir}/com/example/PathParameters.java[tags=path-parameters]
----
<1> Build the request. Uses the static `get` method on `RestDocumentationRequestBuilders`.
<2> Use `withPathParameters` to describe the path parameters.
<3> Document a parameter named `longitude`. Uses the static `parameterWithName` method on
`org.springframework.restdocs.request.RequestDocumentation`.
<4> Document a parameter named `latitude`.
The result is a snippet named `path-parameters.adoc` that contains a table describing
the path parameters that are supported by the resource.
When documenting path parameters, the test will fail if an undocumented path parameter
is used in the request. Similarly, the test will also fail if a documented path parameter
is not found in the request.
TIP: To make the path parameters available for documentation, the request must be
built using one of the methods on `RestDocumentationRequestBuilders` rather than
`MockMvcRequestBuilders`.
[[documenting-your-api-default-snippets]]
=== Default snippets
@@ -286,8 +312,8 @@ override the template for the `curl-request.adoc` snippet, create a template nam
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
. Use the `attributes` method on a field, link or 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.

View File

@@ -17,7 +17,7 @@
package com.example;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
import static org.springframework.restdocs.hypermedia.LinkExtractors.halLinks;

View File

@@ -17,7 +17,7 @@
package com.example;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.springframework.http.MediaType;

View File

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

View File

@@ -20,8 +20,8 @@ import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.snippet.Attributes.attributes;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.springframework.http.MediaType;

View File

@@ -18,7 +18,7 @@ package com.example;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.springframework.test.web.servlet.MockMvc;

View File

@@ -17,7 +17,7 @@
package com.example;
import static org.springframework.restdocs.RestDocumentation.modifyResponseTo;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.springframework.test.web.servlet.MockMvc;

View File

@@ -22,9 +22,9 @@ import static org.springframework.restdocs.RestDocumentation.documentationConfig
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
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.patch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.patch;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -279,7 +279,7 @@ public class ApiDocumentation {
fieldWithPath("title").description("The title of the note").type(FieldType.STRING).optional(),
fieldWithPath("body").description("The body of the note").type(FieldType.STRING).optional(),
fieldWithPath("tags").description("An array of tag resource URIs").optional()));
}
@Test

View File

@@ -21,9 +21,9 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.RestDocumentation.documentationConfiguration;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.patch;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -155,7 +155,7 @@ public class GettingStartedDocumentation {
return noteLocation;
}
void getTags(String noteTagsLocation) throws Exception {
void getTags(String noteTagsLocation) throws Exception {
this.mockMvc.perform(get(noteTagsLocation))
.andExpect(status().isOk())
.andExpect(jsonPath("_embedded.tags", hasSize(1)));
@@ -177,7 +177,7 @@ public class GettingStartedDocumentation {
.andReturn();
}
void getTagsForExistingNote(String noteTagsLocation) throws Exception {
void getTagsForExistingNote(String noteTagsLocation) throws Exception {
this.mockMvc.perform(get(noteTagsLocation))
.andExpect(status().isOk())
.andExpect(jsonPath("_embedded.tags", hasSize(1)));

View File

@@ -22,9 +22,9 @@ import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.RestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
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.patch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.patch;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

View File

@@ -21,9 +21,9 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.RestDocumentation.documentationConfiguration;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.patch;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -155,7 +155,7 @@ public class GettingStartedDocumentation {
return noteLocation;
}
void getTags(String noteTagsLocation) throws Exception {
void getTags(String noteTagsLocation) throws Exception {
this.mockMvc.perform(get(noteTagsLocation))
.andExpect(status().isOk())
.andExpect(jsonPath("_embedded.tags", hasSize(1)));
@@ -177,7 +177,7 @@ public class GettingStartedDocumentation {
.andReturn();
}
void getTagsForExistingNote(String noteTagsLocation) throws Exception {
void getTagsForExistingNote(String noteTagsLocation) throws Exception {
this.mockMvc.perform(get(noteTagsLocation))
.andExpect(status().isOk())
.andExpect(jsonPath("_embedded.tags", hasSize(1)));

View File

@@ -0,0 +1,265 @@
/*
* 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.net.URI;
import org.springframework.http.HttpMethod;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
/**
* A drop-in replacement for {@link MockMvcRequestBuilders} that captures a request's URL
* template and makes it available for documentation. Required when
* {@link RestDocumentationResultHandler#withPathParameters(org.springframework.restdocs .request.ParameterDescriptor...)
* documenting path parameters} and recommended for general usage.
*
* @author Andy Wilkinson
* @see MockMvcRequestBuilders
* @see RestDocumentationResultHandler#withPathParameters(org.springframework.restdocs.request.ParameterDescriptor...)
* @see RestDocumentationResultHandler#withPathParameters(java.util.Map,
* org.springframework.restdocs.request.ParameterDescriptor...)
*/
public abstract class RestDocumentationRequestBuilders {
private static final String ATTRIBUTE_NAME_URL_TEMPLATE = "org.springframework.restdocs.urlTemplate";
private RestDocumentationRequestBuilders() {
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a GET request. The url template
* will be captured and made available for documentation.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the GET request
*/
public static MockHttpServletRequestBuilder get(String urlTemplate,
Object... urlVariables) {
return MockMvcRequestBuilders.get(urlTemplate, urlVariables).requestAttr(
ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a GET request.
*
* @param uri the URL
* @return the builder for the GET request
*/
public static MockHttpServletRequestBuilder get(URI uri) {
return MockMvcRequestBuilders.get(uri);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a POST request. The url template
* will be captured and made available for documentation.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the POST request
*/
public static MockHttpServletRequestBuilder post(String urlTemplate,
Object... urlVariables) {
return MockMvcRequestBuilders.post(urlTemplate, urlVariables).requestAttr(
ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a POST request.
*
* @param uri the URL
* @return the builder for the POST request
*/
public static MockHttpServletRequestBuilder post(URI uri) {
return MockMvcRequestBuilders.post(uri);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a PUT request. The url template
* will be captured and made available for documentation.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the PUT request
*/
public static MockHttpServletRequestBuilder put(String urlTemplate,
Object... urlVariables) {
return MockMvcRequestBuilders.put(urlTemplate, urlVariables).requestAttr(
ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a PUT request.
*
* @param uri the URL
* @return the builder for the PUT request
*/
public static MockHttpServletRequestBuilder put(URI uri) {
return MockMvcRequestBuilders.put(uri);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a PATCH request. The url
* template will be captured and made available for documentation.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the PATCH request
*/
public static MockHttpServletRequestBuilder patch(String urlTemplate,
Object... urlVariables) {
return MockMvcRequestBuilders.patch(urlTemplate, urlVariables).requestAttr(
ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a PATCH request.
*
* @param uri the URL
* @return the builder for the PATCH request
*/
public static MockHttpServletRequestBuilder patch(URI uri) {
return MockMvcRequestBuilders.patch(uri);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a DELETE request. The url
* template will be captured and made available for documentation.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the DELETE request
*/
public static MockHttpServletRequestBuilder delete(String urlTemplate,
Object... urlVariables) {
return MockMvcRequestBuilders.delete(urlTemplate, urlVariables).requestAttr(
ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a DELETE request.
*
* @param uri the URL
* @return the builder for the DELETE request
*/
public static MockHttpServletRequestBuilder delete(URI uri) {
return MockMvcRequestBuilders.delete(uri);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for an OPTIONS request. The url
* template will be captured and made available for documentation.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the OPTIONS request
*/
public static MockHttpServletRequestBuilder options(String urlTemplate,
Object... urlVariables) {
return MockMvcRequestBuilders.options(urlTemplate, urlVariables).requestAttr(
ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for an OPTIONS request.
*
* @param uri the URL
* @return the builder for the OPTIONS request
*/
public static MockHttpServletRequestBuilder options(URI uri) {
return MockMvcRequestBuilders.options(uri);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a HEAD request. The url template
* will be captured and made available for documentation.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the HEAD request
*/
public static MockHttpServletRequestBuilder head(String urlTemplate,
Object... urlVariables) {
return MockMvcRequestBuilders.head(urlTemplate, urlVariables).requestAttr(
ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a HEAD request.
*
* @param uri the URL
* @return the builder for the HEAD request
*/
public static MockHttpServletRequestBuilder head(URI uri) {
return MockMvcRequestBuilders.head(uri);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a request with the given HTTP
* method. The url template will be captured and made available for documentation.
*
* @param httpMethod the HTTP method
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the request
*/
public static MockHttpServletRequestBuilder request(HttpMethod httpMethod,
String urlTemplate, Object... urlVariables) {
return MockMvcRequestBuilders.request(httpMethod, urlTemplate, urlVariables)
.requestAttr(ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a request with the given HTTP
* method.
* @param httpMethod the HTTP method (GET, POST, etc)
* @param uri the URL
* @return the builder for the request
*/
public static MockHttpServletRequestBuilder request(HttpMethod httpMethod, URI uri) {
return MockMvcRequestBuilders.request(httpMethod, uri);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a multipart request. The url
* template will be captured and made available for documentation.
*
* @param urlTemplate a URL template; the resulting URL will be encoded
* @param urlVariables zero or more URL variables
* @return the builder for the file upload request
*/
public static MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate,
Object... urlVariables) {
return (MockMultipartHttpServletRequestBuilder) MockMvcRequestBuilders
.fileUpload(urlTemplate, urlVariables).requestAttr(
ATTRIBUTE_NAME_URL_TEMPLATE, urlTemplate);
}
/**
* Create a {@link MockHttpServletRequestBuilder} for a multipart request.
*
* @param uri the URL
* @return the builder for the file upload request
*/
public static MockMultipartHttpServletRequestBuilder fileUpload(URI uri) {
return MockMvcRequestBuilders.fileUpload(uri);
}
}

View File

@@ -22,6 +22,7 @@ import static org.springframework.restdocs.http.HttpDocumentation.documentHttpRe
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.documentLinks;
import static org.springframework.restdocs.payload.PayloadDocumentation.documentRequestFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.documentResponseFields;
import static org.springframework.restdocs.request.RequestDocumentation.documentPathParameters;
import static org.springframework.restdocs.request.RequestDocumentation.documentQueryParameters;
import java.util.ArrayList;
@@ -309,6 +310,45 @@ public class RestDocumentationResultHandler implements ResultHandler {
return this;
}
/**
* Documents the parameters in the request's path using the given {@code descriptors}.
* <p>
* If a parameter is present in the path 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
* @return {@code this}
* @see RequestDocumentation#parameterWithName(String)
*/
public RestDocumentationResultHandler withPathParameters(
ParameterDescriptor... descriptors) {
return this.withQueryParameters(null, descriptors);
}
/**
* Documents the parameters in the request's path using the given {@code descriptors}.
* The given {@code attributes} are made available during the generation of the path
* parameters snippet.
* <p>
* If a parameter is present in the path 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 withPathParameters(
Map<String, Object> attributes, ParameterDescriptor... descriptors) {
this.delegates.add(documentPathParameters(this.identifier, attributes,
descriptors));
return this;
}
@Override
public void handle(MvcResult result) throws Exception {
this.curlRequest.handle(result);

View File

@@ -0,0 +1,67 @@
package org.springframework.restdocs.request;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.springframework.restdocs.snippet.SnippetWritingResultHandler;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.util.Assert;
public abstract class AbstractParametersSnippetResultHandler extends
SnippetWritingResultHandler {
private final Map<String, ParameterDescriptor> descriptorsByName = new LinkedHashMap<>();
protected AbstractParametersSnippetResultHandler(String identifier,
String snippetName, Map<String, Object> attributes,
ParameterDescriptor... descriptors) {
super(identifier, snippetName, attributes);
for (ParameterDescriptor descriptor : descriptors) {
Assert.hasText(descriptor.getName());
Assert.hasText(descriptor.getDescription());
this.descriptorsByName.put(descriptor.getName(), descriptor);
}
}
@Override
protected Map<String, Object> doHandle(MvcResult result) throws IOException {
verifyParameterDescriptors(result);
Map<String, Object> model = new HashMap<>();
List<Map<String, Object>> parameters = new ArrayList<>();
for (Entry<String, ParameterDescriptor> entry : this.descriptorsByName.entrySet()) {
parameters.add(entry.getValue().toModel());
}
model.put("parameters", parameters);
return model;
}
protected void verifyParameterDescriptors(MvcResult result) {
Set<String> actualParameters = extractActualParameters(result);
Set<String> expectedParameters = this.descriptorsByName.keySet();
Set<String> undocumentedParameters = new HashSet<String>(actualParameters);
undocumentedParameters.removeAll(expectedParameters);
Set<String> missingParameters = new HashSet<String>(expectedParameters);
missingParameters.removeAll(actualParameters);
if (!undocumentedParameters.isEmpty() || !missingParameters.isEmpty()) {
verificationFailed(undocumentedParameters, missingParameters);
}
else {
Assert.isTrue(actualParameters.equals(expectedParameters));
}
}
protected abstract Set<String> extractActualParameters(MvcResult result);
protected abstract void verificationFailed(Set<String> undocumentedParameters,
Set<String> missingParameters);
}

View File

@@ -0,0 +1,90 @@
/*
* 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.request;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.restdocs.snippet.SnippetGenerationException;
import org.springframework.restdocs.snippet.SnippetWritingResultHandler;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.util.Assert;
/**
* A {@link SnippetWritingResultHandler} that produces a snippet documenting the path
* parameters supported by a RESTful resource.
*
* @author Andy Wilkinson
*/
public class PathParametersSnippetResultHandler extends
AbstractParametersSnippetResultHandler {
private static final Pattern NAMES_PATTERN = Pattern.compile("\\{([^/]+?)\\}");
protected PathParametersSnippetResultHandler(String identifier,
Map<String, Object> attributes, ParameterDescriptor... descriptors) {
super(identifier, "path-parameters", attributes, descriptors);
}
@Override
protected Set<String> extractActualParameters(MvcResult result) {
String urlTemplate = extractUrlTemplate(result);
Matcher matcher = NAMES_PATTERN.matcher(urlTemplate);
Set<String> actualParameters = new HashSet<>();
while (matcher.find()) {
String match = matcher.group(1);
actualParameters.add(getParameterName(match));
}
return actualParameters;
}
private String extractUrlTemplate(MvcResult result) {
String urlTemplate = (String) result.getRequest().getAttribute(
"org.springframework.restdocs.urlTemplate");
Assert.notNull(urlTemplate,
"urlTemplate not found. Did you use RestDocumentationRequestBuilders to "
+ "build the request?");
return urlTemplate;
}
private static String getParameterName(String match) {
int colonIdx = match.indexOf(':');
return (colonIdx != -1 ? match.substring(0, colonIdx) : match);
}
@Override
protected void verificationFailed(Set<String> undocumentedParameters,
Set<String> missingParameters) {
String message = "";
if (!undocumentedParameters.isEmpty()) {
message += "Path parameters with the following names were not documented: "
+ undocumentedParameters;
}
if (!missingParameters.isEmpty()) {
if (message.length() > 0) {
message += ". ";
}
message += "Path parameters with the following names were not found in "
+ "the request: " + missingParameters;
}
throw new SnippetGenerationException(message);
}
}

View File

@@ -16,20 +16,12 @@
package org.springframework.restdocs.request;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.springframework.restdocs.snippet.SnippetGenerationException;
import org.springframework.restdocs.snippet.SnippetWritingResultHandler;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.util.Assert;
/**
* A {@link SnippetWritingResultHandler} that produces a snippet documenting the query
@@ -37,60 +29,35 @@ import org.springframework.util.Assert;
*
* @author Andy Wilkinson
*/
public class QueryParametersSnippetResultHandler extends SnippetWritingResultHandler {
private final Map<String, ParameterDescriptor> descriptorsByName = new LinkedHashMap<>();
public class QueryParametersSnippetResultHandler extends
AbstractParametersSnippetResultHandler {
protected QueryParametersSnippetResultHandler(String identifier,
Map<String, Object> attributes, ParameterDescriptor... descriptors) {
super(identifier, "query-parameters", attributes);
for (ParameterDescriptor descriptor : descriptors) {
Assert.hasText(descriptor.getName());
Assert.hasText(descriptor.getDescription());
this.descriptorsByName.put(descriptor.getName(), descriptor);
}
super(identifier, "query-parameters", attributes, descriptors);
}
@Override
protected Map<String, Object> doHandle(MvcResult result) throws IOException {
verifyParameterDescriptors(result);
Map<String, Object> model = new HashMap<>();
List<Map<String, Object>> parameters = new ArrayList<>();
for (Entry<String, ParameterDescriptor> entry : this.descriptorsByName.entrySet()) {
parameters.add(entry.getValue().toModel());
protected void verificationFailed(Set<String> undocumentedParameters,
Set<String> missingParameters) {
String message = "";
if (!undocumentedParameters.isEmpty()) {
message += "Query parameters with the following names were not documented: "
+ undocumentedParameters;
}
model.put("parameters", parameters);
return model;
if (!missingParameters.isEmpty()) {
if (message.length() > 0) {
message += ". ";
}
message += "Query parameters with the following names were not found in the request: "
+ missingParameters;
}
throw new SnippetGenerationException(message);
}
private void verifyParameterDescriptors(MvcResult result) {
Set<String> actualParameters = result.getRequest().getParameterMap().keySet();
Set<String> expectedParameters = this.descriptorsByName.keySet();
Set<String> undocumentedParameters = new HashSet<String>(actualParameters);
undocumentedParameters.removeAll(expectedParameters);
Set<String> missingParameters = new HashSet<String>(expectedParameters);
missingParameters.removeAll(actualParameters);
if (!undocumentedParameters.isEmpty() || !missingParameters.isEmpty()) {
String message = "";
if (!undocumentedParameters.isEmpty()) {
message += "Query parameters with the following names were not documented: "
+ undocumentedParameters;
}
if (!missingParameters.isEmpty()) {
if (message.length() > 0) {
message += ". ";
}
message += "Query parameters with the following names were not found in the request: "
+ missingParameters;
}
throw new SnippetGenerationException(message);
}
Assert.isTrue(actualParameters.equals(expectedParameters));
@Override
protected Set<String> extractActualParameters(MvcResult result) {
return result.getRequest().getParameterMap().keySet();
}
}

View File

@@ -32,6 +32,22 @@ public abstract class RequestDocumentation {
}
/**
* Creates a {@link SnippetWritingResultHandler} that will produce a snippet
* documenting a request's path parameters.
*
* @param identifier An identifier for the API call that is being documented
* @param attributes Attributes made available during rendering of the path parameters
* snippet
* @param descriptors The descriptions of the parameters in the request's path
* @return the result handler
* @see RestDocumentationResultHandler#withPathParameters(ParameterDescriptor...)
*/
public static SnippetWritingResultHandler documentPathParameters(String identifier,
Map<String, Object> attributes, ParameterDescriptor... descriptors) {
return new PathParametersSnippetResultHandler(identifier, attributes, descriptors);
}
/**
* Creates a {@link SnippetWritingResultHandler} that will produce a snippet
* documenting a request's query parameters

View File

@@ -0,0 +1,9 @@
|===
|Parameter|Description
{{#parameters}}
|{{name}}
|{{description}}
{{/parameters}}
|===

View File

@@ -22,6 +22,7 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.springframework.restdocs.RestDocumentation.document;
import static org.springframework.restdocs.RestDocumentation.modifyResponseTo;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.response.ResponsePostProcessors.maskLinks;
import static org.springframework.restdocs.response.ResponsePostProcessors.prettyPrintContent;
import static org.springframework.restdocs.response.ResponsePostProcessors.removeHeaders;
@@ -30,7 +31,6 @@ import static org.springframework.restdocs.snippet.Attributes.attributes;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.restdocs.test.SnippetMatchers.httpResponse;
import static org.springframework.restdocs.test.SnippetMatchers.snippet;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.io.File;

View File

@@ -0,0 +1,157 @@
/*
* 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 static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.delete;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.fileUpload;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.head;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.options;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.patch;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.put;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.request;
import java.net.URI;
import javax.servlet.ServletContext;
import org.junit.Test;
import org.springframework.http.HttpMethod;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
/**
* Tests for {@link RestDocumentationRequestBuilders}
*
* @author Andy Wilkinson
*
*/
public class RestDocumentationRequestBuildersTests {
private final ServletContext servletContext = new MockServletContext();
@Test
public void getTemplate() {
assertTemplate(get("{template}", "t"), HttpMethod.GET);
}
@Test
public void getUri() {
assertUri(get(URI.create("/uri")), HttpMethod.GET);
}
@Test
public void postTemplate() {
assertTemplate(post("{template}", "t"), HttpMethod.POST);
}
@Test
public void postUri() {
assertUri(post(URI.create("/uri")), HttpMethod.POST);
}
@Test
public void putTemplate() {
assertTemplate(put("{template}", "t"), HttpMethod.PUT);
}
@Test
public void putUri() {
assertUri(put(URI.create("/uri")), HttpMethod.PUT);
}
@Test
public void patchTemplate() {
assertTemplate(patch("{template}", "t"), HttpMethod.PATCH);
}
@Test
public void patchUri() {
assertUri(patch(URI.create("/uri")), HttpMethod.PATCH);
}
@Test
public void deleteTemplate() {
assertTemplate(delete("{template}", "t"), HttpMethod.DELETE);
}
@Test
public void deleteUri() {
assertUri(delete(URI.create("/uri")), HttpMethod.DELETE);
}
@Test
public void optionsTemplate() {
assertTemplate(options("{template}", "t"), HttpMethod.OPTIONS);
}
@Test
public void optionsUri() {
assertUri(options(URI.create("/uri")), HttpMethod.OPTIONS);
}
@Test
public void headTemplate() {
assertTemplate(head("{template}", "t"), HttpMethod.HEAD);
}
@Test
public void headUri() {
assertUri(head(URI.create("/uri")), HttpMethod.HEAD);
}
@Test
public void requestTemplate() {
assertTemplate(request(HttpMethod.GET, "{template}", "t"), HttpMethod.GET);
}
@Test
public void requestUri() {
assertUri(request(HttpMethod.GET, URI.create("/uri")), HttpMethod.GET);
}
@Test
public void fileUploadTemplate() {
assertTemplate(fileUpload("{template}", "t"), HttpMethod.POST);
}
@Test
public void fileUploadUri() {
assertUri(fileUpload(URI.create("/uri")), HttpMethod.POST);
}
private void assertTemplate(MockHttpServletRequestBuilder builder,
HttpMethod httpMethod) {
MockHttpServletRequest request = builder.buildRequest(this.servletContext);
assertThat(
(String) request.getAttribute("org.springframework.restdocs.urlTemplate"),
is(equalTo("{template}")));
assertThat(request.getRequestURI(), is(equalTo("t")));
assertThat(request.getMethod(), is(equalTo(httpMethod.name())));
}
private void assertUri(MockHttpServletRequestBuilder builder, HttpMethod httpMethod) {
MockHttpServletRequest request = builder.buildRequest(this.servletContext);
assertThat(request.getRequestURI(), is(equalTo("/uri")));
assertThat(request.getMethod(), is(equalTo(httpMethod.name())));
}
}

View File

@@ -19,15 +19,15 @@ 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.RestDocumentationRequestBuilders.fileUpload;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.put;
import static org.springframework.restdocs.curl.CurlDocumentation.documentCurlRequest;
import static org.springframework.restdocs.snippet.Attributes.attributes;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.restdocs.test.SnippetMatchers.codeBlock;
import static org.springframework.restdocs.test.StubMvcResult.result;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload;
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.request.MockMvcRequestBuilders.put;
import java.io.IOException;
@@ -45,7 +45,7 @@ import org.springframework.restdocs.test.ExpectedSnippet;
/**
* Tests for {@link CurlDocumentation}
*
*
* @author Andy Wilkinson
* @author Yann Le Guern
* @author Dmitriy Mayboroda

View File

@@ -21,6 +21,10 @@ 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.RestDocumentationRequestBuilders.fileUpload;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.RestDocumentationRequestBuilders.put;
import static org.springframework.restdocs.http.HttpDocumentation.documentHttpRequest;
import static org.springframework.restdocs.http.HttpDocumentation.documentHttpResponse;
import static org.springframework.restdocs.snippet.Attributes.attributes;
@@ -28,10 +32,6 @@ import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.restdocs.test.SnippetMatchers.httpRequest;
import static org.springframework.restdocs.test.SnippetMatchers.httpResponse;
import static org.springframework.restdocs.test.StubMvcResult.result;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload;
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.request.MockMvcRequestBuilders.put;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
import static org.springframework.web.bind.annotation.RequestMethod.PUT;
@@ -53,7 +53,7 @@ import org.springframework.restdocs.test.ExpectedSnippet;
/**
* Tests for {@link HttpDocumentation}
*
*
* @author Andy Wilkinson
* @author Jonathan Pearlin
*/

View File

@@ -20,6 +20,7 @@ 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.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.payload.PayloadDocumentation.documentRequestFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.documentResponseFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
@@ -27,7 +28,6 @@ import static org.springframework.restdocs.snippet.Attributes.attributes;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
import static org.springframework.restdocs.test.StubMvcResult.result;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import java.io.IOException;
@@ -45,7 +45,7 @@ import org.springframework.restdocs.test.ExpectedSnippet;
/**
* Tests for {@link PayloadDocumentation}
*
*
* @author Andy Wilkinson
*/
public class PayloadDocumentationTests {

View File

@@ -20,13 +20,14 @@ 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.request.RequestDocumentation.documentPathParameters;
import static org.springframework.restdocs.request.RequestDocumentation.documentQueryParameters;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.snippet.Attributes.attributes;
import static org.springframework.restdocs.snippet.Attributes.key;
import static org.springframework.restdocs.test.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.test.SnippetMatchers.tableWithHeader;
import static org.springframework.restdocs.test.StubMvcResult.result;
import static org.springframework.restdocs.test.TestRequestBuilders.get;
import java.io.IOException;
@@ -56,56 +57,56 @@ public class RequestDocumentationTests {
public ExpectedSnippet snippet = new ExpectedSnippet();
@Test
public void undocumentedParameter() throws IOException {
public void undocumentedQueryParameter() throws IOException {
this.thrown.expect(SnippetGenerationException.class);
this.thrown
.expectMessage(equalTo("Query parameters with the following names were"
+ " not documented: [a]"));
documentQueryParameters("undocumented-parameter", null).handle(
documentQueryParameters("undocumented-query-parameter", null).handle(
result(get("/").param("a", "alpha")));
}
@Test
public void missingParameter() throws IOException {
public void missingQueryParameter() throws IOException {
this.thrown.expect(SnippetGenerationException.class);
this.thrown
.expectMessage(equalTo("Query parameters with the following names were"
+ " not found in the request: [a]"));
documentQueryParameters("missing-parameter", null,
documentQueryParameters("missing-query-parameter", null,
parameterWithName("a").description("one")).handle(result(get("/")));
}
@Test
public void undocumentedParameterAndMissingParameter() throws IOException {
public void undocumentedAndMissingQueryParameters() throws IOException {
this.thrown.expect(SnippetGenerationException.class);
this.thrown
.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", null,
documentQueryParameters("undocumented-and-missing-query-parameters", null,
parameterWithName("a").description("one")).handle(
result(get("/").param("b", "bravo")));
}
@Test
public void parameterSnippetFromRequestParameters() throws IOException {
this.snippet.expectQueryParameters("parameter-snippet-request-parameters")
public void queryParameterSnippetFromRequestParameters() throws IOException {
this.snippet.expectQueryParameters("query-parameter-snippet-request-parameters")
.withContents(
tableWithHeader("Parameter", "Description").row("a", "one").row(
"b", "two"));
documentQueryParameters("parameter-snippet-request-parameters", null,
documentQueryParameters("query-parameter-snippet-request-parameters", null,
parameterWithName("a").description("one"),
parameterWithName("b").description("two")).handle(
result(get("/").param("a", "bravo").param("b", "bravo")));
}
@Test
public void parameterSnippetFromRequestUriQueryString() throws IOException {
this.snippet.expectQueryParameters("parameter-snippet-request-uri-query-string")
.withContents(
tableWithHeader("Parameter", "Description").row("a", "one").row(
"b", "two"));
documentQueryParameters("parameter-snippet-request-uri-query-string", null,
public void queryParameterSnippetFromRequestUriQueryString() throws IOException {
this.snippet.expectQueryParameters(
"query-parameter-snippet-request-uri-query-string").withContents(
tableWithHeader("Parameter", "Description").row("a", "one").row("b",
"two"));
documentQueryParameters("query-parameter-snippet-request-uri-query-string", null,
parameterWithName("a").description("one"),
parameterWithName("b").description("two")).handle(
result(get("/?a=alpha&b=bravo").requestAttr(
@@ -114,12 +115,11 @@ public class RequestDocumentationTests {
}
@Test
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"));
public void queryParametersWithCustomDescriptorAttributes() throws IOException {
this.snippet.expectQueryParameters(
"query-parameters-with-custom-descriptor-attributes").withContents(
tableWithHeader("Parameter", "Description", "Foo").row("a", "one",
"alpha").row("b", "two", "bravo"));
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
when(resolver.resolveTemplateResource("query-parameters"))
.thenReturn(
@@ -131,7 +131,7 @@ public class RequestDocumentationTests {
request.addParameter("a", "bravo");
request.addParameter("b", "bravo");
documentQueryParameters(
"parameters-with-custom-descriptor-attributes",
"query-parameters-with-custom-descriptor-attributes",
null,
parameterWithName("a").description("one").attributes(
key("foo").value("alpha")),
@@ -140,8 +140,8 @@ public class RequestDocumentationTests {
}
@Test
public void parametersWithCustomAttributes() throws IOException {
this.snippet.expectQueryParameters("parameters-with-custom-attributes")
public void queryParametersWithCustomAttributes() throws IOException {
this.snippet.expectQueryParameters("query-parameters-with-custom-attributes")
.withContents(startsWith(".The title"));
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
when(resolver.resolveTemplateResource("query-parameters"))
@@ -154,7 +154,7 @@ public class RequestDocumentationTests {
request.addParameter("a", "bravo");
request.addParameter("b", "bravo");
documentQueryParameters(
"parameters-with-custom-attributes",
"query-parameters-with-custom-attributes",
attributes(key("title").value("The title")),
parameterWithName("a").description("one").attributes(
key("foo").value("alpha")),
@@ -162,4 +162,91 @@ public class RequestDocumentationTests {
key("foo").value("bravo"))).handle(result(request));
}
@Test
public void undocumentedPathParameter() throws IOException {
this.thrown.expect(SnippetGenerationException.class);
this.thrown.expectMessage(equalTo("Path parameters with the following names were"
+ " not documented: [a]"));
documentPathParameters("undocumented-path-parameter", null).handle(
result(get("/{a}/", "alpha")));
}
@Test
public void missingPathParameter() throws IOException {
this.thrown.expect(SnippetGenerationException.class);
this.thrown.expectMessage(equalTo("Path parameters with the following names were"
+ " not found in the request: [a]"));
documentPathParameters("missing-path-parameter", null,
parameterWithName("a").description("one")).handle(result(get("/")));
}
@Test
public void undocumentedAndMissingPathParameters() throws IOException {
this.thrown.expect(SnippetGenerationException.class);
this.thrown.expectMessage(equalTo("Path parameters with the following names were"
+ " not documented: [b]. Path parameters with the following"
+ " names were not found in the request: [a]"));
documentPathParameters("undocumented-and-missing-path-parameters", null,
parameterWithName("a").description("one")).handle(
result(get("/{b}", "bravo")));
}
@Test
public void pathParameters() throws IOException {
this.snippet.expectPathParameters("path-parameters").withContents(
tableWithHeader("Parameter", "Description").row("a", "one").row("b",
"two"));
documentPathParameters("path-parameters", null,
parameterWithName("a").description("one"),
parameterWithName("b").description("two")).handle(
result(get("/{a}/{b}", "alpha", "banana").requestAttr(
RestDocumentationContext.class.getName(),
new RestDocumentationContext())));
}
@Test
public void pathParametersWithCustomDescriptorAttributes() throws IOException {
this.snippet.expectPathParameters(
"path-parameters-with-custom-descriptor-attributes").withContents(
tableWithHeader("Parameter", "Description", "Foo").row("a", "one",
"alpha").row("b", "two", "bravo"));
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
when(resolver.resolveTemplateResource("path-parameters"))
.thenReturn(
new FileSystemResource(
"src/test/resources/custom-snippet-templates/path-parameters-with-extra-column.snippet"));
documentPathParameters(
"path-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(get("{a}/{b}", "alpha", "bravo").requestAttr(
TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver))));
}
@Test
public void pathParametersWithCustomAttributes() throws IOException {
this.snippet.expectPathParameters("path-parameters-with-custom-attributes")
.withContents(startsWith(".The title"));
TemplateResourceResolver resolver = mock(TemplateResourceResolver.class);
when(resolver.resolveTemplateResource("path-parameters"))
.thenReturn(
new FileSystemResource(
"src/test/resources/custom-snippet-templates/path-parameters-with-title.snippet"));
documentPathParameters(
"path-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(get("{a}/{b}", "alpha", "bravo").requestAttr(
TemplateEngine.class.getName(),
new MustacheTemplateEngine(resolver))));
}
}

View File

@@ -135,6 +135,11 @@ public class ExpectedSnippet implements TestRule {
return this;
}
public ExpectedSnippet expectPathParameters(String name) {
expect(name, "path-parameters");
return this;
}
private ExpectedSnippet expect(String name, String type) {
this.expectedName = name;
this.expectedType = type;

View File

@@ -1,18 +0,0 @@
package org.springframework.restdocs.test;
import org.springframework.restdocs.config.RestDocumentationContext;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
public class RestDocumentationRequestBuilders {
private RestDocumentationRequestBuilders() {
}
public static MockHttpServletRequestBuilder get(String urlTemplate) {
return MockMvcRequestBuilders.get(urlTemplate).requestAttr(
RestDocumentationContext.class.getName(), new RestDocumentationContext());
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.test;
import org.springframework.restdocs.config.RestDocumentationContext;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
public class TestRequestBuilders {
private TestRequestBuilders() {
}
public static MockHttpServletRequestBuilder get(String urlTemplate,
Object... urlVariables) {
return org.springframework.restdocs.RestDocumentationRequestBuilders.get(
urlTemplate, urlVariables).requestAttr(
RestDocumentationContext.class.getName(), new RestDocumentationContext());
}
}

View File

@@ -0,0 +1,10 @@
|===
|Parameter|Description|Foo
{{#parameters}}
|{{name}}
|{{description}}
|{{foo}}
{{/parameters}}
|===

View File

@@ -0,0 +1,10 @@
.{{title}}
|===
|Parameter|Description
{{#parameters}}
|{{name}}
|{{description}}
{{/parameters}}
|===