Improve handling of empty parameters in curl and HTTP request snippets
Previously, both the curl and HTTP request snippets would ignore a parameter with no value, for example from the query string of the url http://localhost:8080/foo?bar. This commit updates both snippets so that such parameters are included in the generated snippet, including a multi-part request that is uploading form data and a field in the form has no value. Additions have been made to the tests for both snippets. While the request parameters snippet correctly handled parameters with no value, there was no test verifying that this was the case. One has been added in this commit. Closes gh-200
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -19,6 +19,8 @@ package org.springframework.restdocs.curl;
|
||||
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.restdocs.operation.Parameters;
|
||||
@@ -58,10 +60,18 @@ public class QueryStringParser {
|
||||
|
||||
private void processParameter(String parameter, Parameters parameters) {
|
||||
String[] components = parameter.split("=");
|
||||
if (components.length == 2) {
|
||||
String name = components[0];
|
||||
String value = components[1];
|
||||
parameters.add(decode(name), decode(value));
|
||||
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.get(components[0]);
|
||||
if (values == null) {
|
||||
parameters.put(components[0], new LinkedList<String>());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException(
|
||||
|
||||
@@ -127,10 +127,16 @@ public class HttpRequestSnippet extends TemplatedSnippet {
|
||||
private void writeParts(OperationRequest request, PrintWriter writer) {
|
||||
writer.println();
|
||||
for (Entry<String, List<String>> parameter : request.getParameters().entrySet()) {
|
||||
for (String value : parameter.getValue()) {
|
||||
if (parameter.getValue().isEmpty()) {
|
||||
writePartBoundary(writer);
|
||||
writePart(parameter.getKey(), value, null, writer);
|
||||
writer.println();
|
||||
writePart(parameter.getKey(), "", null, writer);
|
||||
}
|
||||
else {
|
||||
for (String value : parameter.getValue()) {
|
||||
writePartBoundary(writer);
|
||||
writePart(parameter.getKey(), value, null, writer);
|
||||
writer.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
for (OperationRequestPart part : request.getParts()) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -40,17 +40,33 @@ public class Parameters extends LinkedMultiValueMap<String, String> {
|
||||
public String toQueryString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Map.Entry<String, List<String>> entry : entrySet()) {
|
||||
for (String value : entry.getValue()) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append("&");
|
||||
if (entry.getValue().isEmpty()) {
|
||||
append(sb, entry.getKey());
|
||||
}
|
||||
else {
|
||||
for (String value : entry.getValue()) {
|
||||
append(sb, entry.getKey(), value);
|
||||
}
|
||||
sb.append(urlEncodeUTF8(entry.getKey())).append('=')
|
||||
.append(urlEncodeUTF8(value));
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
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) {
|
||||
try {
|
||||
return URLEncoder.encode(s, "UTF-8");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -95,6 +95,17 @@ public class CurlRequestSnippetTests {
|
||||
.request("http://localhost/foo?param=value").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestWithQueryStringWithNoValue() throws IOException {
|
||||
this.snippet.expectCurlRequest("request-with-query-string-with-no-value")
|
||||
.withContents(codeBlock("bash")
|
||||
.content("$ curl 'http://localhost/foo?param' -i"));
|
||||
new CurlRequestSnippet()
|
||||
.document(new OperationBuilder("request-with-query-string-with-no-value",
|
||||
this.snippet.getOutputDirectory())
|
||||
.request("http://localhost/foo?param").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postRequestWithQueryString() throws IOException {
|
||||
this.snippet.expectCurlRequest("post-request-with-query-string")
|
||||
@@ -107,6 +118,18 @@ public class CurlRequestSnippetTests {
|
||||
.method("POST").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postRequestWithQueryStringWithNoValue() throws IOException {
|
||||
this.snippet.expectCurlRequest("post-request-with-query-string-with-no-value")
|
||||
.withContents(codeBlock("bash")
|
||||
.content("$ curl 'http://localhost/foo?param' -i -X POST"));
|
||||
new CurlRequestSnippet().document(
|
||||
new OperationBuilder("post-request-with-query-string-with-no-value",
|
||||
this.snippet.getOutputDirectory())
|
||||
.request("http://localhost/foo?param").method("POST")
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postRequestWithOneParameter() throws IOException {
|
||||
this.snippet.expectCurlRequest("post-request-with-one-parameter")
|
||||
@@ -118,6 +141,17 @@ public class CurlRequestSnippetTests {
|
||||
.method("POST").param("k1", "v1").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postRequestWithOneParameterWithNoValue() throws IOException {
|
||||
this.snippet.expectCurlRequest("post-request-with-one-parameter-with-no-value")
|
||||
.withContents(codeBlock("bash")
|
||||
.content("$ curl 'http://localhost/foo' -i -X POST -d 'k1='"));
|
||||
new CurlRequestSnippet().document(
|
||||
new OperationBuilder("post-request-with-one-parameter-with-no-value",
|
||||
this.snippet.getOutputDirectory()).request("http://localhost/foo")
|
||||
.method("POST").param("k1").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postRequestWithMultipleParameters() throws IOException {
|
||||
this.snippet.expectCurlRequest("post-request-with-multiple-parameters")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -75,6 +75,18 @@ public class HttpRequestSnippetTests {
|
||||
.request("http://localhost/foo?bar=baz").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRequestWithQueryStringWithNoValue() throws IOException {
|
||||
this.snippet.expectHttpRequest("get-request-with-query-string-with-no-value")
|
||||
.withContents(httpRequest(RequestMethod.GET, "/foo?bar")
|
||||
.header(HttpHeaders.HOST, "localhost"));
|
||||
|
||||
new HttpRequestSnippet().document(
|
||||
new OperationBuilder("get-request-with-query-string-with-no-value",
|
||||
this.snippet.getOutputDirectory())
|
||||
.request("http://localhost/foo?bar").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postRequestWithContent() throws IOException {
|
||||
String content = "Hello, world";
|
||||
@@ -123,6 +135,20 @@ public class HttpRequestSnippetTests {
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postRequestWithParameterWithNoValue() throws IOException {
|
||||
this.snippet.expectHttpRequest("post-request-with-parameter")
|
||||
.withContents(httpRequest(RequestMethod.POST, "/foo")
|
||||
.header(HttpHeaders.HOST, "localhost")
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.content("bar="));
|
||||
|
||||
new HttpRequestSnippet()
|
||||
.document(new OperationBuilder("post-request-with-parameter",
|
||||
this.snippet.getOutputDirectory()).request("http://localhost/foo")
|
||||
.method("POST").param("bar").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putRequestWithContent() throws IOException {
|
||||
String content = "Hello, world";
|
||||
@@ -201,6 +227,30 @@ public class HttpRequestSnippetTests {
|
||||
.part("image", "<< data >>".getBytes()).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipartPostWithParameterWithNoValue() throws IOException {
|
||||
String paramPart = createPart(
|
||||
String.format("Content-Disposition: form-data; " + "name=a%n"), false);
|
||||
String filePart = createPart(String
|
||||
.format("Content-Disposition: form-data; " + "name=image%n%n<< data >>"));
|
||||
String expectedContent = paramPart + filePart;
|
||||
this.snippet
|
||||
.expectHttpRequest(
|
||||
"multipart-post-with-parameter-with-no-value")
|
||||
.withContents(httpRequest(RequestMethod.POST, "/upload")
|
||||
.header("Content-Type",
|
||||
"multipart/form-data; boundary=" + BOUNDARY)
|
||||
.header(HttpHeaders.HOST, "localhost").content(expectedContent));
|
||||
new HttpRequestSnippet().document(
|
||||
new OperationBuilder("multipart-post-with-parameter-with-no-value",
|
||||
this.snippet.getOutputDirectory())
|
||||
.request("http://localhost/upload").method("POST")
|
||||
.header(HttpHeaders.CONTENT_TYPE,
|
||||
MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
.param("a").part("image", "<< data >>".getBytes())
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipartPostWithContentType() throws IOException {
|
||||
String expectedContent = createPart(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -109,6 +109,18 @@ public class RequestParametersSnippetTests {
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestParameterWithNoValue() throws IOException {
|
||||
this.snippet.expectRequestParameters("request-parameter-with-no-value")
|
||||
.withContents(
|
||||
tableWithHeader("Parameter", "Description").row("a", "one"));
|
||||
new RequestParametersSnippet(
|
||||
Arrays.asList(parameterWithName("a").description("one")))
|
||||
.document(new OperationBuilder("request-parameter-with-no-value",
|
||||
this.snippet.getOutputDirectory())
|
||||
.request("http://localhost").param("a").build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ignoredRequestParameter() throws IOException {
|
||||
this.snippet.expectRequestParameters("ignored-request-parameter").withContents(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2015 the original author or authors.
|
||||
* Copyright 2014-2016 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.
|
||||
@@ -19,6 +19,7 @@ package org.springframework.restdocs.test;
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -146,8 +147,13 @@ public class OperationBuilder {
|
||||
}
|
||||
|
||||
public OperationRequestBuilder param(String name, String... values) {
|
||||
for (String value : values) {
|
||||
this.parameters.add(name, value);
|
||||
if (values.length > 0) {
|
||||
for (String value : values) {
|
||||
this.parameters.add(name, value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.parameters.put(name, Collections.<String>emptyList());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user