diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentationResultHandler.java b/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentationResultHandler.java
index 11b55534..912d4650 100644
--- a/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentationResultHandler.java
+++ b/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentationResultHandler.java
@@ -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.documentQueryParameters;
import java.util.ArrayList;
import java.util.List;
@@ -32,6 +33,8 @@ import org.springframework.restdocs.hypermedia.LinkExtractor;
import org.springframework.restdocs.hypermedia.LinkExtractors;
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.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultHandler;
@@ -138,4 +141,22 @@ public class RestDocumentationResultHandler implements ResultHandler {
return this;
}
+ /**
+ * Documents the parameters in the request's query string using the given
+ * {@code descriptors}.
+ *
+ * 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
+ * @return {@code this}
+ * @see RequestDocumentation#parameterWithName(String)
+ */
+ public RestDocumentationResultHandler withQueryParameters(
+ ParameterDescriptor... descriptors) {
+ this.delegates.add(documentQueryParameters(this.outputDir, descriptors));
+ return this;
+ }
}
diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/request/ParameterDescriptor.java b/spring-restdocs/src/main/java/org/springframework/restdocs/request/ParameterDescriptor.java
new file mode 100644
index 00000000..63a1be58
--- /dev/null
+++ b/spring-restdocs/src/main/java/org/springframework/restdocs/request/ParameterDescriptor.java
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+/**
+ * A descriptor of a parameter in a query string
+ *
+ * @author Andy Wilkinson
+ * @see RequestDocumentation#parameterWithName
+ *
+ */
+public class ParameterDescriptor {
+
+ private final String name;
+
+ private String description;
+
+ ParameterDescriptor(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Specifies the description of the parameter
+ *
+ * @param description The parameter's description
+ * @return {@code this}
+ */
+ public ParameterDescriptor description(String description) {
+ this.description = description;
+ return this;
+ }
+
+ String getName() {
+ return this.name;
+ }
+
+ String getDescription() {
+ return this.description;
+ }
+
+}
diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/request/QueryParametersSnippetResultHandler.java b/spring-restdocs/src/main/java/org/springframework/restdocs/request/QueryParametersSnippetResultHandler.java
new file mode 100644
index 00000000..1058b0a8
--- /dev/null
+++ b/spring-restdocs/src/main/java/org/springframework/restdocs/request/QueryParametersSnippetResultHandler.java
@@ -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.request;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.springframework.restdocs.snippet.DocumentationWriter;
+import org.springframework.restdocs.snippet.DocumentationWriter.TableAction;
+import org.springframework.restdocs.snippet.DocumentationWriter.TableWriter;
+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
+ * parameters supported by a RESTful resource.
+ *
+ * @author Andy Wilkinson
+ */
+public class QueryParametersSnippetResultHandler extends SnippetWritingResultHandler {
+
+ private final Map descriptorsByName = new LinkedHashMap<>();
+
+ protected QueryParametersSnippetResultHandler(String outputDir,
+ ParameterDescriptor... descriptors) {
+ super(outputDir, "query-parameters");
+ for (ParameterDescriptor descriptor : descriptors) {
+ Assert.hasText(descriptor.getName());
+ Assert.hasText(descriptor.getDescription());
+ this.descriptorsByName.put(descriptor.getName(), descriptor);
+ }
+ }
+
+ @Override
+ protected void handle(MvcResult result, DocumentationWriter writer)
+ throws IOException {
+ verifyParameterDescriptors(result);
+ documentParameters(writer);
+ }
+
+ private void verifyParameterDescriptors(MvcResult result) {
+ Set actualParameters = result.getRequest().getParameterMap().keySet();
+ Set expectedParameters = this.descriptorsByName.keySet();
+
+ Set undocumentedParameters = new HashSet(actualParameters);
+ undocumentedParameters.removeAll(expectedParameters);
+
+ Set missingParameters = new HashSet(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));
+ }
+
+ private void documentParameters(DocumentationWriter writer) throws IOException {
+ writer.table(new TableAction() {
+
+ @Override
+ public void perform(TableWriter tableWriter) throws IOException {
+ tableWriter.headers("Parameter", "Description");
+ for (Entry entry : QueryParametersSnippetResultHandler.this.descriptorsByName
+ .entrySet()) {
+ tableWriter.row(entry.getKey(), entry.getValue().getDescription());
+ }
+ }
+
+ });
+ }
+
+}
diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java b/spring-restdocs/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java
new file mode 100644
index 00000000..cef76d16
--- /dev/null
+++ b/spring-restdocs/src/main/java/org/springframework/restdocs/request/RequestDocumentation.java
@@ -0,0 +1,59 @@
+/*
+ * 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 org.springframework.restdocs.RestDocumentationResultHandler;
+import org.springframework.restdocs.snippet.SnippetWritingResultHandler;
+
+/**
+ * Static factory methods for documenting aspects of a request sent to a RESTful API.
+ *
+ * @author Andy Wilkinson
+ */
+public abstract class RequestDocumentation {
+
+ private RequestDocumentation() {
+
+ }
+
+ /**
+ * Creates a {@link SnippetWritingResultHandler} that will produce a snippet
+ * documenting a request's query parameters
+ *
+ * @param outputDir The directory to which the snippet should be written
+ * @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);
+ }
+
+ /**
+ * Creates a {@link ParameterDescriptor} that describes a query string parameter with
+ * the given {@code name}.
+ *
+ * @param name The name of the parameter
+ * @return a {@link ParameterDescriptor} ready for further configuration
+ * @see RestDocumentationResultHandler#withQueryParameters(ParameterDescriptor...)
+ */
+ public static ParameterDescriptor parameterWithName(String name) {
+ return new ParameterDescriptor(name);
+ }
+
+}
diff --git a/spring-restdocs/src/test/java/org/springframework/restdocs/request/RequestDocumentationTests.java b/spring-restdocs/src/test/java/org/springframework/restdocs/request/RequestDocumentationTests.java
new file mode 100644
index 00000000..32c794b8
--- /dev/null
+++ b/spring-restdocs/src/test/java/org/springframework/restdocs/request/RequestDocumentationTests.java
@@ -0,0 +1,103 @@
+/*
+ * 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 static org.hamcrest.CoreMatchers.equalTo;
+import static org.springframework.restdocs.request.RequestDocumentation.documentQueryParameters;
+import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
+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;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.springframework.restdocs.snippet.SnippetGenerationException;
+import org.springframework.restdocs.test.ExpectedSnippet;
+
+/**
+ * Requests for {@link RequestDocumentation}
+ *
+ * @author Andy Wilkinson
+ */
+public class RequestDocumentationTests {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Rule
+ public ExpectedSnippet snippet = new ExpectedSnippet();
+
+ @Test
+ public void undocumentedParameter() throws IOException {
+ this.thrown.expect(SnippetGenerationException.class);
+ this.thrown
+ .expectMessage(equalTo("Query parameters with the following names were"
+ + " not documented: [a]"));
+ documentQueryParameters("undocumented-parameter").handle(
+ result(get("/").param("a", "alpha")));
+ }
+
+ @Test
+ public void missingParameter() 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",
+ parameterWithName("a").description("one")).handle(result(get("/")));
+ }
+
+ @Test
+ public void undocumentedParameterAndMissingParameter() 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",
+ parameterWithName("a").description("one")).handle(
+ result(get("/").param("b", "bravo")));
+ }
+
+ @Test
+ public void parameterSnippetFromRequestParameters() throws IOException {
+ this.snippet.expectQueryParameters("parameter-snippet-request-parameters")
+ .withContents(
+ tableWithHeader("Parameter", "Description").row("a", "one").row(
+ "b", "two"));
+ documentQueryParameters("parameter-snippet-request-parameters",
+ 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",
+ parameterWithName("a").description("one"),
+ parameterWithName("b").description("two")).handle(
+ result(get("/?a=alpha&b=bravo")));
+ }
+
+}
diff --git a/spring-restdocs/src/test/java/org/springframework/restdocs/test/ExpectedSnippet.java b/spring-restdocs/src/test/java/org/springframework/restdocs/test/ExpectedSnippet.java
index 5c7f9b52..d7ec0d71 100644
--- a/spring-restdocs/src/test/java/org/springframework/restdocs/test/ExpectedSnippet.java
+++ b/spring-restdocs/src/test/java/org/springframework/restdocs/test/ExpectedSnippet.java
@@ -142,6 +142,11 @@ public class ExpectedSnippet implements TestRule {
return this;
}
+ public ExpectedSnippet expectQueryParameters(String name) {
+ expect(name, "query-parameters");
+ return this;
+ }
+
private ExpectedSnippet expect(String name, String type) {
this.expectedName = name;
this.expectedType = type;