Handle form and query parameters separately

Previously, form and query parameters were handled together as
request parameters. Howeer, request parameters are a server-side
construct that's specific to the servlet specification. As such
they're not appropriate for the client-side documentation that
Spring REST Docs aims to produce.

This commit replaces support for documenting request parameters
with support for documenting query paramters found in the query
string of the request's URI and for documenting form parameters
found in the form URL encoded body of the request.

Closes gh-832
This commit is contained in:
Andy Wilkinson
2022-10-10 15:28:30 +01:00
parent b4be34bf8e
commit f5a629af34
63 changed files with 1730 additions and 1356 deletions

View File

@@ -24,13 +24,11 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.restdocs.operation.OperationRequest;
import org.springframework.restdocs.operation.OperationRequestPart;
import org.springframework.restdocs.operation.Parameters;
import org.springframework.restdocs.operation.RequestCookie;
import org.springframework.util.Base64Utils;
@@ -65,15 +63,6 @@ final class CliOperationRequest implements OperationRequest {
return null;
}
Parameters getNonPartParameters() {
Parameters parameters = getParameters();
Parameters nonPartParameters = new Parameters();
nonPartParameters.putAll(parameters);
Set<String> partNames = getParts().stream().map(OperationRequestPart::getName).collect(Collectors.toSet());
nonPartParameters.keySet().removeAll(partNames);
return nonPartParameters;
}
@Override
public byte[] getContent() {
return this.delegate.getContent();
@@ -115,11 +104,6 @@ final class CliOperationRequest implements OperationRequest {
return this.delegate.getMethod();
}
@Override
public Parameters getParameters() {
return this.delegate.getParameters();
}
@Override
public Collection<OperationRequestPart> getParts() {
return this.delegate.getParts();

View File

@@ -22,12 +22,11 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.restdocs.operation.Operation;
import org.springframework.restdocs.operation.OperationRequest;
import org.springframework.restdocs.operation.OperationRequestPart;
import org.springframework.restdocs.operation.Parameters;
import org.springframework.restdocs.operation.RequestCookie;
import org.springframework.restdocs.snippet.Snippet;
import org.springframework.restdocs.snippet.TemplatedSnippet;
@@ -82,21 +81,9 @@ public class CurlRequestSnippet extends TemplatedSnippet {
private String getUrl(Operation operation) {
OperationRequest request = operation.getRequest();
Parameters uniqueParameters = request.getParameters().getUniqueParameters(operation.getRequest().getUri());
if (!uniqueParameters.isEmpty() && includeParametersInUri(request)) {
return String.format("'%s%s%s'", request.getUri(),
StringUtils.hasText(request.getUri().getRawQuery()) ? "&" : "?", uniqueParameters.toQueryString());
}
return String.format("'%s'", request.getUri());
}
private boolean includeParametersInUri(OperationRequest request) {
HttpMethod method = request.getMethod();
return (method != HttpMethod.PUT && method != HttpMethod.POST && method != HttpMethod.PATCH)
|| (request.getContent().length > 0 && !MediaType.APPLICATION_FORM_URLENCODED
.isCompatibleWith(request.getHeaders().getContentType()));
}
private String getOptions(Operation operation) {
StringBuilder builder = new StringBuilder();
writeIncludeHeadersInOutputOption(builder);
@@ -147,6 +134,10 @@ public class CurlRequestSnippet extends TemplatedSnippet {
private void writeHeaders(CliOperationRequest request, List<String> lines) {
for (Entry<String, List<String>> entry : request.getHeaders().entrySet()) {
for (String header : entry.getValue()) {
if (StringUtils.hasText(request.getContentAsString()) && HttpHeaders.CONTENT_TYPE.equals(entry.getKey())
&& MediaType.APPLICATION_FORM_URLENCODED.equals(request.getHeaders().getContentType())) {
continue;
}
lines.add(String.format("-H '%s: %s'", entry.getKey(), header));
}
}
@@ -176,24 +167,6 @@ public class CurlRequestSnippet extends TemplatedSnippet {
if (StringUtils.hasText(content)) {
lines.add(String.format("-d '%s'", content));
}
else if (!request.getParts().isEmpty()) {
for (Entry<String, List<String>> entry : request.getNonPartParameters().entrySet()) {
for (String value : entry.getValue()) {
lines.add(String.format("-F '%s=%s'", entry.getKey(), value));
}
}
}
else if (request.isPutOrPost()) {
writeContentUsingParameters(request, lines);
}
}
private void writeContentUsingParameters(OperationRequest request, List<String> lines) {
Parameters uniqueParameters = request.getParameters().getUniqueParameters(request.getUri());
String queryString = uniqueParameters.toQueryString();
if (StringUtils.hasText(queryString)) {
lines.add(String.format("-d '%s'", queryString));
}
}
}

View File

@@ -25,12 +25,11 @@ import java.util.Map;
import java.util.Map.Entry;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.restdocs.operation.FormParameters;
import org.springframework.restdocs.operation.Operation;
import org.springframework.restdocs.operation.OperationRequest;
import org.springframework.restdocs.operation.OperationRequestPart;
import org.springframework.restdocs.operation.Parameters;
import org.springframework.restdocs.operation.RequestCookie;
import org.springframework.restdocs.snippet.Snippet;
import org.springframework.restdocs.snippet.TemplatedSnippet;
@@ -85,6 +84,9 @@ public class HttpieRequestSnippet extends TemplatedSnippet {
}
private Object getContentStandardIn(CliOperationRequest request) {
if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(request.getHeaders().getContentType())) {
return "";
}
String content = request.getContentAsString();
if (StringUtils.hasText(content)) {
return String.format("echo '%s' | ", content);
@@ -102,21 +104,15 @@ public class HttpieRequestSnippet extends TemplatedSnippet {
}
private String getUrl(OperationRequest request) {
Parameters uniqueParameters = request.getParameters().getUniqueParameters(request.getUri());
if (!uniqueParameters.isEmpty() && includeParametersInUri(request)) {
return String.format("'%s%s%s'", request.getUri(),
StringUtils.hasText(request.getUri().getRawQuery()) ? "&" : "?", uniqueParameters.toQueryString());
}
return String.format("'%s'", request.getUri());
}
private String getRequestItems(CliOperationRequest request) {
List<String> lines = new ArrayList<>();
writeFormDataIfNecessary(request, lines);
writeHeaders(request, lines);
writeCookies(request, lines);
writeParametersIfNecessary(request, lines);
writeFormDataIfNecessary(request, lines);
return this.commandFormatter.format(lines);
}
@@ -125,24 +121,11 @@ public class HttpieRequestSnippet extends TemplatedSnippet {
if (!request.getParts().isEmpty()) {
writer.print("--multipart ");
}
else if (!request.getParameters().getUniqueParameters(request.getUri()).isEmpty()
&& !includeParametersInUri(request) && includeParametersAsFormOptions(request)) {
else if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(request.getHeaders().getContentType())) {
writer.print("--form ");
}
}
private boolean includeParametersInUri(OperationRequest request) {
HttpMethod method = request.getMethod();
return (method != HttpMethod.PUT && method != HttpMethod.POST && method != HttpMethod.PATCH)
|| (request.getContent().length > 0 && !MediaType.APPLICATION_FORM_URLENCODED
.isCompatibleWith(request.getHeaders().getContentType()));
}
private boolean includeParametersAsFormOptions(OperationRequest request) {
return request.getMethod() != HttpMethod.GET && (request.getContent().length == 0
|| !MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(request.getHeaders().getContentType()));
}
private void writeUserOptionIfNecessary(CliOperationRequest request, PrintWriter writer) {
String credentials = request.getBasicAuthCredentials();
if (credentials != null) {
@@ -155,23 +138,33 @@ public class HttpieRequestSnippet extends TemplatedSnippet {
}
private void writeFormDataIfNecessary(OperationRequest request, List<String> lines) {
for (OperationRequestPart part : request.getParts()) {
StringBuilder oneLine = new StringBuilder();
oneLine.append(String.format("'%s'", part.getName()));
if (!StringUtils.hasText(part.getSubmittedFileName())) {
oneLine.append(String.format("='%s'", part.getContentAsString()));
}
else {
oneLine.append(String.format("@'%s'", part.getSubmittedFileName()));
}
if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(request.getHeaders().getContentType())) {
FormParameters.from(request).forEach(
(key, values) -> values.forEach((value) -> lines.add(String.format("'%s=%s'", key, value))));
}
else {
for (OperationRequestPart part : request.getParts()) {
StringBuilder oneLine = new StringBuilder();
oneLine.append(String.format("'%s'", part.getName()));
if (!StringUtils.hasText(part.getSubmittedFileName())) {
oneLine.append(String.format("='%s'", part.getContentAsString()));
}
else {
oneLine.append(String.format("@'%s'", part.getSubmittedFileName()));
}
lines.add(oneLine.toString());
lines.add(oneLine.toString());
}
}
}
private void writeHeaders(OperationRequest request, List<String> lines) {
HttpHeaders headers = request.getHeaders();
for (Entry<String, List<String>> entry : headers.entrySet()) {
if (entry.getKey().equals(HttpHeaders.CONTENT_TYPE)
&& headers.getContentType().isCompatibleWith(MediaType.APPLICATION_FORM_URLENCODED)) {
continue;
}
for (String header : entry.getValue()) {
// HTTPie adds Content-Type automatically with --form
if (!request.getParts().isEmpty() && entry.getKey().equals(HttpHeaders.CONTENT_TYPE)
@@ -189,29 +182,4 @@ public class HttpieRequestSnippet extends TemplatedSnippet {
}
}
private void writeParametersIfNecessary(CliOperationRequest request, List<String> lines) {
if (StringUtils.hasText(request.getContentAsString())) {
return;
}
if (!request.getParts().isEmpty()) {
writeContentUsingParameters(request.getNonPartParameters(), lines);
}
else if (request.isPutOrPost()) {
writeContentUsingParameters(request.getParameters().getUniqueParameters(request.getUri()), lines);
}
}
private void writeContentUsingParameters(Parameters parameters, List<String> lines) {
for (Map.Entry<String, List<String>> entry : parameters.entrySet()) {
if (entry.getValue().isEmpty()) {
lines.add(String.format("'%s='", entry.getKey()));
}
else {
for (String value : entry.getValue()) {
lines.add(String.format("'%s=%s'", entry.getKey(), value));
}
}
}
}
}

View File

@@ -23,8 +23,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
@@ -32,7 +30,6 @@ import org.springframework.http.MediaType;
import org.springframework.restdocs.operation.Operation;
import org.springframework.restdocs.operation.OperationRequest;
import org.springframework.restdocs.operation.OperationRequestPart;
import org.springframework.restdocs.operation.Parameters;
import org.springframework.restdocs.operation.RequestCookie;
import org.springframework.restdocs.snippet.Snippet;
import org.springframework.restdocs.snippet.TemplatedSnippet;
@@ -78,15 +75,6 @@ public class HttpRequestSnippet extends TemplatedSnippet {
private String getPath(OperationRequest request) {
String path = request.getUri().getRawPath();
String queryString = request.getUri().getRawQuery();
Parameters uniqueParameters = request.getParameters().getUniqueParameters(request.getUri());
if (!uniqueParameters.isEmpty() && includeParametersInUri(request)) {
if (StringUtils.hasText(queryString)) {
queryString = queryString + "&" + uniqueParameters.toQueryString();
}
else {
queryString = uniqueParameters.toQueryString();
}
}
if (StringUtils.hasText(queryString)) {
path = path + "?" + queryString;
}
@@ -133,14 +121,7 @@ public class HttpRequestSnippet extends TemplatedSnippet {
writer.printf("%n%s", content);
}
else if (isPutOrPost(request)) {
if (request.getParts().isEmpty()) {
String queryString = request.getParameters().getUniqueParameters(request.getUri()).toQueryString();
if (StringUtils.hasText(queryString)) {
writer.println();
writer.print(queryString);
}
}
else {
if (!request.getParts().isEmpty()) {
writeParts(request, writer);
}
}
@@ -153,23 +134,6 @@ public class HttpRequestSnippet extends TemplatedSnippet {
private void writeParts(OperationRequest request, PrintWriter writer) {
writer.println();
Set<String> partNames = request.getParts().stream().map(OperationRequestPart::getName)
.collect(Collectors.toSet());
for (Entry<String, List<String>> parameter : request.getParameters().entrySet()) {
if (!partNames.contains(parameter.getKey())) {
if (parameter.getValue().isEmpty()) {
writePartBoundary(writer);
writePart(parameter.getKey(), "", null, null, writer);
}
else {
for (String value : parameter.getValue()) {
writePartBoundary(writer);
writePart(parameter.getKey(), value, null, null, writer);
writer.println();
}
}
}
}
for (OperationRequestPart part : request.getParts()) {
writePartBoundary(writer);
writePart(part, writer);
@@ -206,7 +170,6 @@ public class HttpRequestSnippet extends TemplatedSnippet {
private boolean requiresFormEncodingContentTypeHeader(OperationRequest request) {
return request.getHeaders().get(HttpHeaders.CONTENT_TYPE) == null && isPutOrPost(request)
&& !request.getParameters().getUniqueParameters(request.getUri()).isEmpty()
&& !includeParametersInUri(request);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2019 the original author or authors.
* Copyright 2014-2022 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.
@@ -17,36 +17,46 @@
package org.springframework.restdocs.operation;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
import org.springframework.util.LinkedMultiValueMap;
/**
* A parser for the query string of a URI.
* A request's form parameters, derived from its form URL encoded body content.
*
* @author Andy Wilkinson
* @since 3.0.0
*/
public class QueryStringParser {
public final class FormParameters extends LinkedMultiValueMap<String, String> {
private FormParameters() {
/**
* Parses the query string of the given {@code uri} and returns the resulting
* {@link Parameters}.
* @param uri the uri to parse
* @return the parameters parsed from the query string
*/
public Parameters parse(URI uri) {
String query = uri.getRawQuery();
if (query != null) {
return parse(query);
}
return new Parameters();
}
private Parameters parse(String query) {
Parameters parameters = new Parameters();
try (Scanner scanner = new Scanner(query)) {
/**
* Extracts the form parameters from the body of the given {@code request}. If the
* request has no body content, an empty {@code FormParameters} is returned, rather
* than {@code null}.
* @param request the request
* @return the form parameters extracted from the body content
*/
public static FormParameters from(OperationRequest request) {
return of(request.getContentAsString());
}
private static FormParameters of(String bodyContent) {
if (bodyContent == null || bodyContent.length() == 0) {
return new FormParameters();
}
return parse(bodyContent);
}
private static FormParameters parse(String bodyContent) {
FormParameters parameters = new FormParameters();
try (Scanner scanner = new Scanner(bodyContent)) {
scanner.useDelimiter("&");
while (scanner.hasNext()) {
processParameter(scanner.next(), parameters);
@@ -55,7 +65,7 @@ public class QueryStringParser {
return parameters;
}
private void processParameter(String parameter, Parameters parameters) {
private static void processParameter(String parameter, FormParameters parameters) {
String[] components = parameter.split("=");
if (components.length > 0 && components.length < 3) {
if (components.length == 2) {
@@ -64,7 +74,7 @@ public class QueryStringParser {
parameters.add(decode(name), decode(value));
}
else {
List<String> values = parameters.computeIfAbsent(components[0], (p) -> new LinkedList<String>());
List<String> values = parameters.computeIfAbsent(components[0], (p) -> new LinkedList<>());
values.add("");
}
}
@@ -73,12 +83,12 @@ public class QueryStringParser {
}
}
private String decode(String encoded) {
private static String decode(String encoded) {
try {
return URLDecoder.decode(encoded, "UTF-8");
}
catch (UnsupportedEncodingException ex) {
throw new IllegalStateException("Unable to URL encode " + encoded + " using UTF-8", ex);
throw new IllegalStateException("Unable to URL decode " + encoded + " using UTF-8", ex);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 the original author or authors.
* Copyright 2014-2022 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.
@@ -58,14 +58,6 @@ public interface OperationRequest {
*/
HttpMethod getMethod();
/**
* Returns the request's parameters. For a {@code GET} request, the parameters are
* derived from the query string. For a {@code POST} request, the parameters are
* derived form the request's body.
* @return the parameters
*/
Parameters getParameters();
/**
* Returns the request's parts, provided that it is a multipart request. If not, then
* an empty {@link Collection} is returned.

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2020 the original author or authors.
* Copyright 2014-2022 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.
@@ -17,7 +17,6 @@
package org.springframework.restdocs.operation;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.Collections;
@@ -39,15 +38,15 @@ public class OperationRequestFactory {
* @param method the request method
* @param content the content of the request
* @param headers the request's headers
* @param parameters the request's parameters
* @param parts the request's parts
* @param cookies the request's cookies
* @return the {@code OperationRequest}
* @since 3.0.0
*/
public OperationRequest create(URI uri, HttpMethod method, byte[] content, HttpHeaders headers,
Parameters parameters, Collection<OperationRequestPart> parts, Collection<RequestCookie> cookies) {
return new StandardOperationRequest(uri, method, content, augmentHeaders(headers, uri, content), parameters,
parts, cookies);
Collection<OperationRequestPart> parts, Collection<RequestCookie> cookies) {
return new StandardOperationRequest(uri, method, content, augmentHeaders(headers, uri, content),
(parts != null) ? parts : Collections.emptyList(), cookies);
}
/**
@@ -58,13 +57,13 @@ public class OperationRequestFactory {
* @param method the request method
* @param content the content of the request
* @param headers the request's headers
* @param parameters the request's parameters
* @param parts the request's parts
* @return the {@code OperationRequest}
* @since 3.0.0
*/
public OperationRequest create(URI uri, HttpMethod method, byte[] content, HttpHeaders headers,
Parameters parameters, Collection<OperationRequestPart> parts) {
return create(uri, method, content, headers, parameters, parts, Collections.<RequestCookie>emptyList());
Collection<OperationRequestPart> parts) {
return create(uri, method, content, headers, parts, Collections.emptyList());
}
/**
@@ -77,8 +76,7 @@ public class OperationRequestFactory {
*/
public OperationRequest createFrom(OperationRequest original, byte[] newContent) {
return new StandardOperationRequest(original.getUri(), original.getMethod(), newContent,
getUpdatedHeaders(original.getHeaders(), newContent), original.getParameters(), original.getParts(),
original.getCookies());
getUpdatedHeaders(original.getHeaders(), newContent), original.getParts(), original.getCookies());
}
/**
@@ -90,33 +88,7 @@ public class OperationRequestFactory {
*/
public OperationRequest createFrom(OperationRequest original, HttpHeaders newHeaders) {
return new StandardOperationRequest(original.getUri(), original.getMethod(), original.getContent(), newHeaders,
original.getParameters(), original.getParts(), original.getCookies());
}
/**
* Creates a new {@code OperationRequest} based on the given {@code original} but with
* the given {@code newParameters} applied. The query string of a {@code GET} request
* will be updated to reflect the new parameters.
* @param original the original request
* @param newParameters the new parameters
* @return the new request with the parameters applied
*/
public OperationRequest createFrom(OperationRequest original, Parameters newParameters) {
URI uri = (original.getMethod() == HttpMethod.GET) ? updateQueryString(original.getUri(), newParameters)
: original.getUri();
return new StandardOperationRequest(uri, original.getMethod(), original.getContent(), original.getHeaders(),
newParameters, original.getParts(), original.getCookies());
}
private URI updateQueryString(URI originalUri, Parameters parameters) {
try {
return new URI(originalUri.getScheme(), originalUri.getUserInfo(), originalUri.getHost(),
originalUri.getPort(), originalUri.getPath(),
parameters.isEmpty() ? null : parameters.toQueryString(), originalUri.getFragment());
}
catch (URISyntaxException ex) {
throw new RuntimeException(ex);
}
original.getParts(), original.getCookies());
}
private HttpHeaders augmentHeaders(HttpHeaders originalHeaders, URI uri, byte[] content) {

View File

@@ -1,115 +0,0 @@
/*
* Copyright 2014-2019 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
*
* https://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.operation;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.StringUtils;
/**
* The parameters received in a request.
*
* @author Andy Wilkinson
*/
@SuppressWarnings("serial")
public class Parameters extends LinkedMultiValueMap<String, String> {
/**
* Converts the parameters to a query string suitable for use in a URI or the body of
* a form-encoded request.
* @return the query string
*/
public String toQueryString() {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, List<String>> entry : entrySet()) {
if (entry.getValue().isEmpty()) {
append(sb, entry.getKey());
}
else {
for (String value : entry.getValue()) {
append(sb, entry.getKey(), value);
}
}
}
return sb.toString();
}
/**
* Returns a new {@code Parameters} containing only the parameters that do no appear
* in the query string of the given {@code uri}.
* @param uri the uri
* @return the unique parameters
*/
public Parameters getUniqueParameters(URI uri) {
Parameters queryStringParameters = new QueryStringParser().parse(uri);
Parameters uniqueParameters = new Parameters();
for (Map.Entry<String, List<String>> parameter : entrySet()) {
addIfUnique(parameter, queryStringParameters, uniqueParameters);
}
return uniqueParameters;
}
private void addIfUnique(Map.Entry<String, List<String>> parameter, Parameters queryStringParameters,
Parameters uniqueParameters) {
if (!queryStringParameters.containsKey(parameter.getKey())) {
uniqueParameters.put(parameter.getKey(), parameter.getValue());
}
else {
List<String> candidates = parameter.getValue();
List<String> existing = queryStringParameters.get(parameter.getKey());
for (String candidate : candidates) {
if (!existing.contains(candidate)) {
uniqueParameters.add(parameter.getKey(), candidate);
}
}
}
}
private static void append(StringBuilder sb, String key) {
append(sb, key, "");
}
private static void append(StringBuilder sb, String key, String value) {
doAppend(sb, urlEncodeUTF8(key) + "=" + urlEncodeUTF8(value));
}
private static void doAppend(StringBuilder sb, String toAppend) {
if (sb.length() > 0) {
sb.append("&");
}
sb.append(toAppend);
}
private static String urlEncodeUTF8(String s) {
if (!StringUtils.hasLength(s)) {
return "";
}
try {
return URLEncoder.encode(s, "UTF-8");
}
catch (UnsupportedEncodingException ex) {
throw new IllegalStateException("Unable to URL encode " + s + " using UTF-8", ex);
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2014-2022 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
*
* https://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.operation;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
import org.springframework.util.LinkedMultiValueMap;
/**
* A request's query parameters, derived from its URI's query string.
*
* @author Andy Wilkinson
* @since 3.0.0
*/
public final class QueryParameters extends LinkedMultiValueMap<String, String> {
private QueryParameters() {
}
/**
* Extracts the query parameters from the query string of the given {@code request}.
* If the request has no query string, an empty {@code QueryParameters} is returned,
* rather than {@code null}.
* @param request the request
* @return the query parameters extracted from the request's query string
*/
public static QueryParameters from(OperationRequest request) {
return from(request.getUri().getRawQuery());
}
private static QueryParameters from(String queryString) {
if (queryString == null || queryString.length() == 0) {
return new QueryParameters();
}
return parse(queryString);
}
private static QueryParameters parse(String query) {
QueryParameters parameters = new QueryParameters();
try (Scanner scanner = new Scanner(query)) {
scanner.useDelimiter("&");
while (scanner.hasNext()) {
processParameter(scanner.next(), parameters);
}
}
return parameters;
}
private static void processParameter(String parameter, QueryParameters parameters) {
String[] components = parameter.split("=");
if (components.length > 0 && components.length < 3) {
if (components.length == 2) {
String name = components[0];
String value = components[1];
parameters.add(decode(name), decode(value));
}
else {
List<String> values = parameters.computeIfAbsent(components[0], (p) -> new LinkedList<>());
values.add("");
}
}
else {
throw new IllegalArgumentException("The parameter '" + parameter + "' is malformed");
}
}
private static String decode(String encoded) {
return URLDecoder.decode(encoded, StandardCharsets.UTF_8);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2019 the original author or authors.
* Copyright 2014-2022 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.
@@ -32,8 +32,6 @@ class StandardOperationRequest extends AbstractOperationMessage implements Opera
private HttpMethod method;
private Parameters parameters;
private Collection<OperationRequestPart> parts;
private URI uri;
@@ -48,16 +46,14 @@ class StandardOperationRequest extends AbstractOperationMessage implements Opera
* @param method the method
* @param content the content
* @param headers the headers
* @param parameters the parameters
* @param parts the parts
* @param cookies the cookies
*/
StandardOperationRequest(URI uri, HttpMethod method, byte[] content, HttpHeaders headers, Parameters parameters,
StandardOperationRequest(URI uri, HttpMethod method, byte[] content, HttpHeaders headers,
Collection<OperationRequestPart> parts, Collection<RequestCookie> cookies) {
super(content, headers);
this.uri = uri;
this.method = method;
this.parameters = parameters;
this.parts = parts;
this.cookies = cookies;
}
@@ -67,11 +63,6 @@ class StandardOperationRequest extends AbstractOperationMessage implements Opera
return this.method;
}
@Override
public Parameters getParameters() {
return this.parameters;
}
@Override
public Collection<OperationRequestPart> getParts() {
return Collections.unmodifiableCollection(this.parts);

View File

@@ -134,9 +134,9 @@ public class UriModifyingOperationPreprocessor implements OperationPreprocessor
HttpHeaders modifiedHeaders = modify(request.getHeaders());
modifiedHeaders.set(HttpHeaders.HOST,
modifiedUri.getHost() + ((modifiedUri.getPort() != -1) ? ":" + modifiedUri.getPort() : ""));
return this.contentModifyingDelegate.preprocess(new OperationRequestFactory().create(
uriBuilder.build(true).toUri(), request.getMethod(), request.getContent(), modifiedHeaders,
request.getParameters(), modify(request.getParts()), request.getCookies()));
return this.contentModifyingDelegate
.preprocess(new OperationRequestFactory().create(uriBuilder.build(true).toUri(), request.getMethod(),
request.getContent(), modifiedHeaders, modify(request.getParts()), request.getCookies()));
}
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2020 the original author or authors.
* Copyright 2014-2022 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.
@@ -22,36 +22,33 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.restdocs.operation.FormParameters;
import org.springframework.restdocs.operation.Operation;
import org.springframework.restdocs.operation.OperationRequest;
import org.springframework.restdocs.snippet.Snippet;
import org.springframework.restdocs.snippet.SnippetException;
/**
* A {@link Snippet} that documents the request parameters supported by a RESTful
* resource.
* <p>
* Request parameters are sent as part of the query string or as POSTed form data.
* A {@link Snippet} that documents the form parameters supported by a RESTful resource.
*
* @author Andy Wilkinson
* @see OperationRequest#getParameters()
* @see RequestDocumentation#requestParameters(ParameterDescriptor...)
* @see RequestDocumentation#requestParameters(Map, ParameterDescriptor...)
* @since 3.0.0
* @see RequestDocumentation#formParameters(ParameterDescriptor...)
* @see RequestDocumentation#formParameters(Map, ParameterDescriptor...)
*/
public class RequestParametersSnippet extends AbstractParametersSnippet {
public class FormParametersSnippet extends AbstractParametersSnippet {
/**
* Creates a new {@code RequestParametersSnippet} that will document the request's
* Creates a new {@code FormParametersSnippet} that will document the request's form
* parameters using the given {@code descriptors}. Undocumented parameters will
* trigger a failure.
* @param descriptors the parameter descriptors
*/
protected RequestParametersSnippet(List<ParameterDescriptor> descriptors) {
protected FormParametersSnippet(List<ParameterDescriptor> descriptors) {
this(descriptors, null, false);
}
/**
* Creates a new {@code RequestParametersSnippet} that will document the request's
* Creates a new {@code FormParametersSnippet} that will document the request's form
* parameters using the given {@code descriptors}. If
* {@code ignoreUndocumentedParameters} is {@code true}, undocumented parameters will
* be ignored and will not trigger a failure.
@@ -59,24 +56,24 @@ public class RequestParametersSnippet extends AbstractParametersSnippet {
* @param ignoreUndocumentedParameters whether undocumented parameters should be
* ignored
*/
protected RequestParametersSnippet(List<ParameterDescriptor> descriptors, boolean ignoreUndocumentedParameters) {
protected FormParametersSnippet(List<ParameterDescriptor> descriptors, boolean ignoreUndocumentedParameters) {
this(descriptors, null, ignoreUndocumentedParameters);
}
/**
* Creates a new {@code RequestParametersSnippet} that will document the request's
* Creates a new {@code FormParametersSnippet} that will document the request's form
* parameters using the given {@code descriptors}. The given {@code attributes} will
* be included in the model during template rendering. Undocumented parameters will
* trigger a failure.
* @param descriptors the parameter descriptors
* @param attributes the additional attributes
*/
protected RequestParametersSnippet(List<ParameterDescriptor> descriptors, Map<String, Object> attributes) {
protected FormParametersSnippet(List<ParameterDescriptor> descriptors, Map<String, Object> attributes) {
this(descriptors, attributes, false);
}
/**
* Creates a new {@code RequestParametersSnippet} that will document the request's
* Creates a new {@code FormParametersSnippet} that will document the request's form
* parameters using the given {@code descriptors}. The given {@code attributes} will
* be included in the model during template rendering. If
* {@code ignoreUndocumentedParameters} is {@code true}, undocumented parameters will
@@ -86,54 +83,53 @@ public class RequestParametersSnippet extends AbstractParametersSnippet {
* @param ignoreUndocumentedParameters whether undocumented parameters should be
* ignored
*/
protected RequestParametersSnippet(List<ParameterDescriptor> descriptors, Map<String, Object> attributes,
protected FormParametersSnippet(List<ParameterDescriptor> descriptors, Map<String, Object> attributes,
boolean ignoreUndocumentedParameters) {
super("request-parameters", descriptors, attributes, ignoreUndocumentedParameters);
super("form-parameters", descriptors, attributes, ignoreUndocumentedParameters);
}
@Override
protected void verificationFailed(Set<String> undocumentedParameters, Set<String> missingParameters) {
String message = "";
if (!undocumentedParameters.isEmpty()) {
message += "Request parameters with the following names were not documented: " + undocumentedParameters;
message += "Form parameters with the following names were not documented: " + undocumentedParameters;
}
if (!missingParameters.isEmpty()) {
if (message.length() > 0) {
message += ". ";
}
message += "Request parameters with the following names were not found in the request: "
+ missingParameters;
message += "Form parameters with the following names were not found in the request: " + missingParameters;
}
throw new SnippetException(message);
}
@Override
protected Set<String> extractActualParameters(Operation operation) {
return operation.getRequest().getParameters().keySet();
return FormParameters.from(operation.getRequest()).keySet();
}
/**
* Returns a new {@code RequestParametersSnippet} configured with this snippet's
* Returns a new {@code FormParametersSnippet} configured with this snippet's
* attributes and its descriptors combined with the given
* {@code additionalDescriptors}.
* @param additionalDescriptors the additional descriptors
* @return the new snippet
*/
public RequestParametersSnippet and(ParameterDescriptor... additionalDescriptors) {
public FormParametersSnippet and(ParameterDescriptor... additionalDescriptors) {
return and(Arrays.asList(additionalDescriptors));
}
/**
* Returns a new {@code RequestParametersSnippet} configured with this snippet's
* Returns a new {@code FormParametersSnippet} configured with this snippet's
* attributes and its descriptors combined with the given
* {@code additionalDescriptors}.
* @param additionalDescriptors the additional descriptors
* @return the new snippet
*/
public RequestParametersSnippet and(List<ParameterDescriptor> additionalDescriptors) {
public FormParametersSnippet and(List<ParameterDescriptor> additionalDescriptors) {
List<ParameterDescriptor> combinedDescriptors = new ArrayList<>(getParameterDescriptors().values());
combinedDescriptors.addAll(additionalDescriptors);
return new RequestParametersSnippet(combinedDescriptors, this.getAttributes(),
return new FormParametersSnippet(combinedDescriptors, this.getAttributes(),
this.isIgnoreUndocumentedParameters());
}

View File

@@ -0,0 +1,136 @@
/*
* Copyright 2014-2022 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
*
* https://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.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.restdocs.operation.Operation;
import org.springframework.restdocs.operation.QueryParameters;
import org.springframework.restdocs.snippet.Snippet;
import org.springframework.restdocs.snippet.SnippetException;
/**
* A {@link Snippet} that documents the query parameters supported by a RESTful resource.
*
* @author Andy Wilkinson
* @since 3.0.0
* @see RequestDocumentation#queryParameters(ParameterDescriptor...)
* @see RequestDocumentation#queryParameters(Map, ParameterDescriptor...)
*/
public class QueryParametersSnippet extends AbstractParametersSnippet {
/**
* Creates a new {@code QueryParametersSnippet} that will document the request's query
* parameters using the given {@code descriptors}. Undocumented parameters will
* trigger a failure.
* @param descriptors the parameter descriptors
*/
protected QueryParametersSnippet(List<ParameterDescriptor> descriptors) {
this(descriptors, null, false);
}
/**
* Creates a new {@code QueryParametersSnippet} that will document the request's query
* parameters using the given {@code descriptors}. If
* {@code ignoreUndocumentedParameters} is {@code true}, undocumented parameters will
* be ignored and will not trigger a failure.
* @param descriptors the parameter descriptors
* @param ignoreUndocumentedParameters whether undocumented parameters should be
* ignored
*/
protected QueryParametersSnippet(List<ParameterDescriptor> descriptors, boolean ignoreUndocumentedParameters) {
this(descriptors, null, ignoreUndocumentedParameters);
}
/**
* Creates a new {@code QueryParametersSnippet} that will document the request's query
* parameters using the given {@code descriptors}. The given {@code attributes} will
* be included in the model during template rendering. Undocumented parameters will
* trigger a failure.
* @param descriptors the parameter descriptors
* @param attributes the additional attributes
*/
protected QueryParametersSnippet(List<ParameterDescriptor> descriptors, Map<String, Object> attributes) {
this(descriptors, attributes, false);
}
/**
* Creates a new {@code QueryParametersSnippet} that will document the request's query
* parameters using the given {@code descriptors}. The given {@code attributes} will
* be included in the model during template rendering. If
* {@code ignoreUndocumentedParameters} is {@code true}, undocumented parameters will
* be ignored and will not trigger a failure.
* @param descriptors the parameter descriptors
* @param attributes the additional attributes
* @param ignoreUndocumentedParameters whether undocumented parameters should be
* ignored
*/
protected QueryParametersSnippet(List<ParameterDescriptor> descriptors, Map<String, Object> attributes,
boolean ignoreUndocumentedParameters) {
super("query-parameters", descriptors, attributes, ignoreUndocumentedParameters);
}
@Override
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;
}
if (!missingParameters.isEmpty()) {
if (message.length() > 0) {
message += ". ";
}
message += "Query parameters with the following names were not found in the request: " + missingParameters;
}
throw new SnippetException(message);
}
@Override
protected Set<String> extractActualParameters(Operation operation) {
return QueryParameters.from(operation.getRequest()).keySet();
}
/**
* Returns a new {@code QueryParametersSnippet} configured with this snippet's
* attributes and its descriptors combined with the given
* {@code additionalDescriptors}.
* @param additionalDescriptors the additional descriptors
* @return the new snippet
*/
public QueryParametersSnippet and(ParameterDescriptor... additionalDescriptors) {
return and(Arrays.asList(additionalDescriptors));
}
/**
* Returns a new {@code QueryParametersSnippet} configured with this snippet's
* attributes and its descriptors combined with the given
* {@code additionalDescriptors}.
* @param additionalDescriptors the additional descriptors
* @return the new snippet
*/
public QueryParametersSnippet and(List<ParameterDescriptor> additionalDescriptors) {
List<ParameterDescriptor> combinedDescriptors = new ArrayList<>(getParameterDescriptors().values());
combinedDescriptors.addAll(additionalDescriptors);
return new QueryParametersSnippet(combinedDescriptors, this.getAttributes(),
this.isIgnoreUndocumentedParameters());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2019 the original author or authors.
* Copyright 2014-2022 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.
@@ -203,159 +203,321 @@ public abstract class RequestDocumentation {
}
/**
* Returns a {@code Snippet} that will document the parameters from the API
* operation's request. The parameters will be documented using the given
* Returns a {@code Snippet} that will document the query parameters from the API
* operation's request. The query parameters will be documented using the given
* {@code descriptors}.
* <p>
* If a parameter is present in the request, but is not documented by one of the
* If a query parameter is present in the request, but is not documented by one of the
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
* parameter is documented, is not marked as optional, and is not present in the
* query parameter is documented, is not marked as optional, and is not present in the
* request, a failure will also occur.
* <p>
* If you do not want to document a request parameter, a parameter descriptor can be
* If you do not want to document a query parameter, a parameter descriptor can be
* marked as {@link ParameterDescriptor#ignored()}. This will prevent it from
* appearing in the generated snippet while avoiding the failure described above.
* @param descriptors the descriptions of the request's parameters
* @param descriptors the descriptions of the request's query parameters
* @return the snippet
* @see OperationRequest#getParameters()
* @since 3.0.0
*/
public static RequestParametersSnippet requestParameters(ParameterDescriptor... descriptors) {
return requestParameters(Arrays.asList(descriptors));
public static QueryParametersSnippet queryParameters(ParameterDescriptor... descriptors) {
return queryParameters(Arrays.asList(descriptors));
}
/**
* Returns a {@code Snippet} that will document the parameters from the API
* operation's request. The parameters will be documented using the given
* Returns a {@code Snippet} that will document the query parameters from the API
* operation's request. The query parameters will be documented using the given
* {@code descriptors}.
* <p>
* If a parameter is present in the request, but is not documented by one of the
* If a query parameter is present in the request, but is not documented by one of the
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
* parameter is documented, is not marked as optional, and is not present in the
* query parameter is documented, is not marked as optional, and is not present in the
* request, a failure will also occur.
* <p>
* If you do not want to document a request parameter, a parameter descriptor can be
* If you do not want to document a query parameter, a parameter descriptor can be
* marked as {@link ParameterDescriptor#ignored()}. This will prevent it from
* appearing in the generated snippet while avoiding the failure described above.
* @param descriptors the descriptions of the request's parameters
* @param descriptors the descriptions of the request's query parameters
* @return the snippet
* @see OperationRequest#getParameters()
* @since 3.0.0
*/
public static RequestParametersSnippet requestParameters(List<ParameterDescriptor> descriptors) {
return new RequestParametersSnippet(descriptors);
public static QueryParametersSnippet queryParameters(List<ParameterDescriptor> descriptors) {
return new QueryParametersSnippet(descriptors);
}
/**
* Returns a {@code Snippet} that will document the parameters from the API
* operation's request. The parameters will be documented using the given
* Returns a {@code Snippet} that will document the query parameters from the API
* operation's request. The query parameters will be documented using the given
* {@code descriptors}.
* <p>
* If a query parameter is documented, is not marked as optional, and is not present
* in the response, a failure will occur. Any undocumented query parameters will be
* ignored.
* @param descriptors the descriptions of the request's query parameters
* @return the snippet
* @since 3.0.0
*/
public static QueryParametersSnippet relaxedQueryParameters(ParameterDescriptor... descriptors) {
return relaxedQueryParameters(Arrays.asList(descriptors));
}
/**
* Returns a {@code Snippet} that will document the query parameters from the API
* operation's request. The query parameters will be documented using the given
* {@code descriptors}.
* <p>
* If a parameter is documented, is not marked as optional, and is not present in the
* response, a failure will occur. Any undocumented parameters will be ignored.
* @param descriptors the descriptions of the request's parameters
* response, a failure will occur. Any undocumented query parameters will be ignored.
* @param descriptors the descriptions of the request's query parameters
* @return the snippet
* @see OperationRequest#getParameters()
* @since 3.0.0
*/
public static RequestParametersSnippet relaxedRequestParameters(ParameterDescriptor... descriptors) {
return relaxedRequestParameters(Arrays.asList(descriptors));
public static QueryParametersSnippet relaxedQueryParameters(List<ParameterDescriptor> descriptors) {
return new QueryParametersSnippet(descriptors, true);
}
/**
* Returns a {@code Snippet} that will document the parameters from the API
* operation's request. The parameters will be documented using the given
* {@code descriptors}.
* <p>
* If a parameter is documented, is not marked as optional, and is not present in the
* response, a failure will occur. Any undocumented parameters will be ignored.
* @param descriptors the descriptions of the request's parameters
* @return the snippet
* @see OperationRequest#getParameters()
*/
public static RequestParametersSnippet relaxedRequestParameters(List<ParameterDescriptor> descriptors) {
return new RequestParametersSnippet(descriptors, true);
}
/**
* Returns a {@code Snippet} that will document the parameters from the API
* Returns a {@code Snippet} that will document the query parameters from the API
* operation's request. The given {@code attributes} will be available during snippet
* rendering and the parameters will be documented using the given {@code descriptors}
* .
* rendering and the query parameters will be documented using the given
* {@code descriptors} .
* <p>
* If a parameter is present in the request, but is not documented by one of the
* If a query parameter is present in the request, but is not documented by one of the
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
* parameter is documented, is not marked as optional, and is not present in the
* query parameter is documented, is not marked as optional, and is not present in the
* request, a failure will also occur.
* <p>
* If you do not want to document a request parameter, a parameter descriptor can be
* If you do not want to document a query parameter, a parameter descriptor can be
* marked as {@link ParameterDescriptor#ignored()}. This will prevent it from
* appearing in the generated snippet while avoiding the failure described above.
* @param attributes the attributes
* @param descriptors the descriptions of the request's parameters
* @return the snippet that will document the parameters
* @see OperationRequest#getParameters()
* @param descriptors the descriptions of the request's query parameters
* @return the snippet that will document the query parameters
* @since 3.0.0
*/
public static RequestParametersSnippet requestParameters(Map<String, Object> attributes,
public static QueryParametersSnippet queryParameters(Map<String, Object> attributes,
ParameterDescriptor... descriptors) {
return requestParameters(attributes, Arrays.asList(descriptors));
return queryParameters(attributes, Arrays.asList(descriptors));
}
/**
* Returns a {@code Snippet} that will document the parameters from the API
* Returns a {@code Snippet} that will document the query parameters from the API
* operation's request. The given {@code attributes} will be available during snippet
* rendering and the parameters will be documented using the given {@code descriptors}
* .
* rendering and the query parameters will be documented using the given
* {@code descriptors} .
* <p>
* If a parameter is present in the request, but is not documented by one of the
* If a query parameter is present in the request, but is not documented by one of the
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a
* parameter is documented, is not marked as optional, and is not present in the
* query parameter is documented, is not marked as optional, and is not present in the
* request, a failure will also occur.
* <p>
* If you do not want to document a request parameter, a parameter descriptor can be
* If you do not want to document a query parameter, a parameter descriptor can be
* marked as {@link ParameterDescriptor#ignored()}. This will prevent it from
* appearing in the generated snippet while avoiding the failure described above.
* @param attributes the attributes
* @param descriptors the descriptions of the request's parameters
* @return the snippet that will document the parameters
* @see OperationRequest#getParameters()
* @param descriptors the descriptions of the request's query parameters
* @return the snippet that will document the query parameters
* @since 3.0.0
*/
public static RequestParametersSnippet requestParameters(Map<String, Object> attributes,
public static QueryParametersSnippet queryParameters(Map<String, Object> attributes,
List<ParameterDescriptor> descriptors) {
return new RequestParametersSnippet(descriptors, attributes);
return new QueryParametersSnippet(descriptors, attributes);
}
/**
* Returns a {@code Snippet} that will document the parameters from the API
* Returns a {@code Snippet} that will document the query parameters from the API
* operation's request. The given {@code attributes} will be available during snippet
* rendering and the parameters will be documented using the given {@code descriptors}
* .
* rendering and the query parameters will be documented using the given
* {@code descriptors} .
* <p>
* If a parameter is documented, is not marked as optional, and is not present in the
* response, a failure will occur. Any undocumented parameters will be ignored.
* If a query parameter is documented, is not marked as optional, and is not present
* in the response, a failure will occur. Any undocumented query parameters will be
* ignored.
* @param attributes the attributes
* @param descriptors the descriptions of the request's parameters
* @return the snippet that will document the parameters
* @see OperationRequest#getParameters()
* @param descriptors the descriptions of the request's query parameters
* @return the snippet that will document the query parameters
* @since 3.0.0
*/
public static RequestParametersSnippet relaxedRequestParameters(Map<String, Object> attributes,
public static QueryParametersSnippet relaxedQueryParameters(Map<String, Object> attributes,
ParameterDescriptor... descriptors) {
return relaxedRequestParameters(attributes, Arrays.asList(descriptors));
return relaxedQueryParameters(attributes, Arrays.asList(descriptors));
}
/**
* Returns a {@code Snippet} that will document the parameters from the API
* Returns a {@code Snippet} that will document the query parameters from the API
* operation's request. The given {@code attributes} will be available during snippet
* rendering and the parameters will be documented using the given {@code descriptors}
* .
* rendering and the query parameters will be documented using the given
* {@code descriptors} .
* <p>
* If a query parameter is documented, is not marked as optional, and is not present
* in the response, a failure will occur. Any undocumented query parameters will be
* ignored.
* @param attributes the attributes
* @param descriptors the descriptions of the request's query parameters
* @return the snippet that will document the query parameters
* @since 3.0.0
*/
public static QueryParametersSnippet relaxedQueryParameters(Map<String, Object> attributes,
List<ParameterDescriptor> descriptors) {
return new QueryParametersSnippet(descriptors, attributes, true);
}
/**
* Returns a {@code Snippet} that will document the form parameters from the API
* operation's request. The form parameters will be documented using the given
* {@code descriptors}.
* <p>
* If a form parameter is present in the request, but is not documented by one of the
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a form
* parameter is documented, is not marked as optional, and is not present in the
* request, a failure will also occur.
* <p>
* If you do not want to document a form parameter, a parameter descriptor can be
* marked as {@link ParameterDescriptor#ignored()}. This will prevent it from
* appearing in the generated snippet while avoiding the failure described above.
* @param descriptors the descriptions of the request's form parameters
* @return the snippet
* @since 3.0.0
*/
public static FormParametersSnippet formParameters(ParameterDescriptor... descriptors) {
return formParameters(Arrays.asList(descriptors));
}
/**
* Returns a {@code Snippet} that will document the form parameters from the API
* operation's request. The form parameters will be documented using the given
* {@code descriptors}.
* <p>
* If a form parameter is present in the request, but is not documented by one of the
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a form
* parameter is documented, is not marked as optional, and is not present in the
* request, a failure will also occur.
* <p>
* If you do not want to document a form parameter, a parameter descriptor can be
* marked as {@link ParameterDescriptor#ignored()}. This will prevent it from
* appearing in the generated snippet while avoiding the failure described above.
* @param descriptors the descriptions of the request's form parameters
* @return the snippet
* @since 3.0.0
*/
public static FormParametersSnippet formParameters(List<ParameterDescriptor> descriptors) {
return new FormParametersSnippet(descriptors);
}
/**
* Returns a {@code Snippet} that will document the form parameters from the API
* operation's request. The form parameters will be documented using the given
* {@code descriptors}.
* <p>
* If a form parameter is documented, is not marked as optional, and is not present in
* the response, a failure will occur. Any undocumented form parameters will be
* ignored.
* @param descriptors the descriptions of the request's form parameters
* @return the snippet
* @since 3.0.0
*/
public static FormParametersSnippet relaxedFormParameters(ParameterDescriptor... descriptors) {
return relaxedFormParameters(Arrays.asList(descriptors));
}
/**
* Returns a {@code Snippet} that will document the form parameters from the API
* operation's request. The form parameters will be documented using the given
* {@code descriptors}.
* <p>
* If a parameter is documented, is not marked as optional, and is not present in the
* response, a failure will occur. Any undocumented parameters will be ignored.
* @param attributes the attributes
* @param descriptors the descriptions of the request's parameters
* @return the snippet that will document the parameters
* @see OperationRequest#getParameters()
* response, a failure will occur. Any undocumented form parameters will be ignored.
* @param descriptors the descriptions of the request's form parameters
* @return the snippet
* @since 3.0.0
*/
public static RequestParametersSnippet relaxedRequestParameters(Map<String, Object> attributes,
public static FormParametersSnippet relaxedFormParameters(List<ParameterDescriptor> descriptors) {
return new FormParametersSnippet(descriptors, true);
}
/**
* Returns a {@code Snippet} that will document the form parameters from the API
* operation's request. The given {@code attributes} will be available during snippet
* rendering and the form parameters will be documented using the given
* {@code descriptors} .
* <p>
* If a form parameter is present in the request, but is not documented by one of the
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a form
* parameter is documented, is not marked as optional, and is not present in the
* request, a failure will also occur.
* <p>
* If you do not want to document a form parameter, a parameter descriptor can be
* marked as {@link ParameterDescriptor#ignored()}. This will prevent it from
* appearing in the generated snippet while avoiding the failure described above.
* @param attributes the attributes
* @param descriptors the descriptions of the request's form parameters
* @return the snippet that will document the form parameters
* @since 3.0.0
*/
public static FormParametersSnippet formParameters(Map<String, Object> attributes,
ParameterDescriptor... descriptors) {
return formParameters(attributes, Arrays.asList(descriptors));
}
/**
* Returns a {@code Snippet} that will document the form parameters from the API
* operation's request. The given {@code attributes} will be available during snippet
* rendering and the form parameters will be documented using the given
* {@code descriptors} .
* <p>
* If a form parameter is present in the request, but is not documented by one of the
* descriptors, a failure will occur when the snippet is invoked. Similarly, if a form
* parameter is documented, is not marked as optional, and is not present in the
* request, a failure will also occur.
* <p>
* If you do not want to document a form parameter, a parameter descriptor can be
* marked as {@link ParameterDescriptor#ignored()}. This will prevent it from
* appearing in the generated snippet while avoiding the failure described above.
* @param attributes the attributes
* @param descriptors the descriptions of the request's form parameters
* @return the snippet that will document the form parameters
* @since 3.0.0
*/
public static FormParametersSnippet formParameters(Map<String, Object> attributes,
List<ParameterDescriptor> descriptors) {
return new RequestParametersSnippet(descriptors, attributes, true);
return new FormParametersSnippet(descriptors, attributes);
}
/**
* Returns a {@code Snippet} that will document the form parameters from the API
* operation's request. The given {@code attributes} will be available during snippet
* rendering and the form parameters will be documented using the given
* {@code descriptors} .
* <p>
* If a form parameter is documented, is not marked as optional, and is not present in
* the response, a failure will occur. Any undocumented form parameters will be
* ignored.
* @param attributes the attributes
* @param descriptors the descriptions of the request's form parameters
* @return the snippet that will document the form parameters
* @since 3.0.0
*/
public static FormParametersSnippet relaxedFormParameters(Map<String, Object> attributes,
ParameterDescriptor... descriptors) {
return relaxedFormParameters(attributes, Arrays.asList(descriptors));
}
/**
* Returns a {@code Snippet} that will document the form parameters from the API
* operation's request. The given {@code attributes} will be available during snippet
* rendering and the form parameters will be documented using the given
* {@code descriptors} .
* <p>
* If a form parameter is documented, is not marked as optional, and is not present in
* the response, a failure will occur. Any undocumented form parameters will be
* ignored.
* @param attributes the attributes
* @param descriptors the descriptions of the request's form parameters
* @return the snippet that will document the form parameters
* @since 3.0.0
*/
public static FormParametersSnippet relaxedFormParameters(Map<String, Object> attributes,
List<ParameterDescriptor> descriptors) {
return new FormParametersSnippet(descriptors, attributes, true);
}
/**
@@ -482,7 +644,6 @@ public abstract class RequestDocumentation {
* @param attributes the attributes
* @param descriptors the descriptions of the request's parts
* @return the snippet
* @see OperationRequest#getParameters()
*/
public static RequestPartsSnippet relaxedRequestParts(Map<String, Object> attributes,
RequestPartDescriptor... descriptors) {
@@ -499,7 +660,6 @@ public abstract class RequestDocumentation {
* @param attributes the attributes
* @param descriptors the descriptions of the request's parts
* @return the snippet
* @see OperationRequest#getParameters()
*/
public static RequestPartsSnippet relaxedRequestParts(Map<String, Object> attributes,
List<RequestPartDescriptor> descriptors) {

View File

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

View File

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

View File

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

View File

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