Add support for documenting a request’s query parameters
This commit adds support for documenting a request's query parameters.
For example:
mockMvc.perform(
get("/issues").param("page", "2").param("limit", "20"))
.andDo(documentQueryParameters("list-issues",
parameterWithName("page")
.description("Page number to return"),
parameterWithName("limit")
.description("Maximum page size")));
Closes gh-39
This commit is contained in:
@@ -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}.
|
||||
* <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
|
||||
* @return {@code this}
|
||||
* @see RequestDocumentation#parameterWithName(String)
|
||||
*/
|
||||
public RestDocumentationResultHandler withQueryParameters(
|
||||
ParameterDescriptor... descriptors) {
|
||||
this.delegates.add(documentQueryParameters(this.outputDir, descriptors));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String, ParameterDescriptor> 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<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));
|
||||
}
|
||||
|
||||
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<String, ParameterDescriptor> entry : QueryParametersSnippetResultHandler.this.descriptorsByName
|
||||
.entrySet()) {
|
||||
tableWriter.row(entry.getKey(), entry.getValue().getDescription());
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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")));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user