Added queryparams support for custom mode
This commit is contained in:
committed by
Marcin Grzejszczak
parent
5e5782f736
commit
bd4c01a5d8
@@ -41,6 +41,7 @@ class CustomModeGiven implements Given, BodyMethodVisitor, CustomModeAcceptor {
|
||||
new CustomModeRequestGiven(blockBuilder, generatedClassMetaData)));
|
||||
this.bodyGivens.addAll(
|
||||
Arrays.asList(new CustomModeMethodWithUrlGiven(blockBuilder, bodyParser),
|
||||
new CustomModeQueryParamsGiven(blockBuilder, bodyParser),
|
||||
new CustomModeSchemeProtocolGiven(blockBuilder),
|
||||
new CustomModeHeadersGiven(blockBuilder),
|
||||
new CustomModeCookiesGiven(blockBuilder),
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.cloud.contract.verifier.builder;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.cloud.contract.spec.internal.MatchingStrategy;
|
||||
import org.springframework.cloud.contract.spec.internal.QueryParameter;
|
||||
import org.springframework.cloud.contract.spec.internal.Request;
|
||||
import org.springframework.cloud.contract.spec.internal.Url;
|
||||
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
|
||||
import org.springframework.cloud.contract.verifier.util.MapConverter;
|
||||
|
||||
class CustomModeQueryParamsGiven
|
||||
implements Given, CustomModeAcceptor, QueryParamsResolver {
|
||||
|
||||
private final BlockBuilder blockBuilder;
|
||||
|
||||
private final BodyParser bodyParser;
|
||||
|
||||
CustomModeQueryParamsGiven(BlockBuilder blockBuilder, BodyParser bodyParser) {
|
||||
this.blockBuilder = blockBuilder;
|
||||
this.bodyParser = bodyParser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor<Given> apply(SingleContractMetadata metadata) {
|
||||
Request request = metadata.getContract().getRequest();
|
||||
Url url = getUrl(request);
|
||||
addQueryParameters(url);
|
||||
return this;
|
||||
}
|
||||
|
||||
private Url getUrl(Request request) {
|
||||
if (request.getUrl() != null) {
|
||||
return request.getUrl();
|
||||
}
|
||||
if (request.getUrlPath() != null) {
|
||||
return request.getUrlPath();
|
||||
}
|
||||
throw new IllegalStateException("URL is not set!");
|
||||
}
|
||||
|
||||
private void addQueryParameters(Url buildUrl) {
|
||||
List<QueryParameter> queryParameters = buildUrl.getQueryParameters()
|
||||
.getParameters().stream().filter(this::allowedQueryParameter)
|
||||
.collect(Collectors.toList());
|
||||
Iterator<QueryParameter> iterator = queryParameters.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
QueryParameter parameter = iterator.next();
|
||||
String text = addQueryParameter(parameter);
|
||||
if (iterator.hasNext()) {
|
||||
this.blockBuilder.addLine(text);
|
||||
}
|
||||
else {
|
||||
this.blockBuilder.addIndented(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean allowedQueryParameter(Object o) {
|
||||
if (o instanceof QueryParameter) {
|
||||
return allowedQueryParameter(((QueryParameter) o).getServerValue());
|
||||
}
|
||||
else if (o instanceof MatchingStrategy) {
|
||||
return !MatchingStrategy.Type.ABSENT.equals(((MatchingStrategy) o).getType());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private String addQueryParameter(QueryParameter queryParam) {
|
||||
return "." + "queryParam(" + this.bodyParser.quotedLongText(queryParam.getName())
|
||||
+ "," + this.bodyParser.quotedLongText(resolveParamValue(
|
||||
MapConverter.getTestSideValuesForNonBody(queryParam)))
|
||||
+ ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(SingleContractMetadata metadata) {
|
||||
return acceptType(metadata) && metadata.getContract().getRequest() != null
|
||||
&& getUrl(metadata.getContract().getRequest())
|
||||
.getQueryParameters() != null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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.cloud.contract.verifier.builder;
|
||||
|
||||
import org.springframework.cloud.contract.spec.internal.Request;
|
||||
import org.springframework.cloud.contract.spec.internal.Url;
|
||||
import org.springframework.cloud.contract.verifier.file.SingleContractMetadata;
|
||||
|
||||
class CustomQueryParamsGiven implements Given, CustomModeAcceptor, QueryParamsResolver {
|
||||
|
||||
@Override
|
||||
public MethodVisitor<Given> apply(SingleContractMetadata metadata) {
|
||||
throw new UnsupportedOperationException("Query params are not supported");
|
||||
}
|
||||
|
||||
private Url getUrl(Request request) {
|
||||
if (request.getUrl() != null) {
|
||||
return request.getUrl();
|
||||
}
|
||||
if (request.getUrlPath() != null) {
|
||||
return request.getUrlPath();
|
||||
}
|
||||
throw new IllegalStateException("URL is not set!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(SingleContractMetadata metadata) {
|
||||
Request request = metadata.getContract().getRequest();
|
||||
Url url = getUrl(request);
|
||||
return url.getQueryParameters() != null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,11 +17,11 @@
|
||||
package org.springframework.cloud.contract.verifier.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -57,9 +57,7 @@ public class OkHttpHttpVerifier implements HttpVerifier {
|
||||
public Response exchange(Request request) {
|
||||
String requestContentType = request.contentType();
|
||||
OkHttpClient client = new OkHttpClient.Builder()
|
||||
.protocols(Collections
|
||||
.singletonList(toProtocol(request.protocol().toString())))
|
||||
.build();
|
||||
.protocols(toProtocol(request.protocol().toString())).build();
|
||||
Map<String, String> headers = stringTyped(request.headers());
|
||||
if (!request.cookies().isEmpty()) {
|
||||
headers.put("Set-Cookie",
|
||||
@@ -67,10 +65,7 @@ public class OkHttpHttpVerifier implements HttpVerifier {
|
||||
.map(e -> e.getKey() + "=" + e.getValue().toString())
|
||||
.collect(Collectors.joining(";")));
|
||||
}
|
||||
okhttp3.Request req = new okhttp3.Request.Builder()
|
||||
.url(request.scheme().name().toLowerCase() + ":" + this.hostAndPort
|
||||
+ (request.path().startsWith("/") ? request.path()
|
||||
: "/" + request.path()))
|
||||
okhttp3.Request req = new okhttp3.Request.Builder().url(url(request))
|
||||
.method(request.method().name(), requestBody(request, requestContentType))
|
||||
.headers(Headers.of(headers)).build();
|
||||
try (okhttp3.Response res = client.newCall(req).execute()) {
|
||||
@@ -81,9 +76,33 @@ public class OkHttpHttpVerifier implements HttpVerifier {
|
||||
}
|
||||
}
|
||||
|
||||
private Protocol toProtocol(String string) {
|
||||
private String url(Request request) {
|
||||
String url = request.scheme().name().toLowerCase() + ":" + this.hostAndPort
|
||||
+ (request.path().startsWith("/") ? request.path()
|
||||
: "/" + request.path());
|
||||
if (!request.queryParams().isEmpty()) {
|
||||
return url + "?"
|
||||
+ request.queryParams().stream()
|
||||
.map(e -> e.getKey() + "=" + e.getValue())
|
||||
.collect(Collectors.joining("&"));
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
private List<Protocol> toProtocol(String string) {
|
||||
try {
|
||||
return Protocol.get(string);
|
||||
Protocol protocol = Protocol.get(string);
|
||||
switch (protocol) {
|
||||
case HTTP_1_0:
|
||||
case HTTP_2:
|
||||
case QUIC:
|
||||
return Arrays.asList(protocol, Protocol.HTTP_1_1);
|
||||
case HTTP_1_1:
|
||||
return Collections.singletonList(Protocol.HTTP_1_1);
|
||||
case H2_PRIOR_KNOWLEDGE:
|
||||
return Collections.singletonList(Protocol.H2_PRIOR_KNOWLEDGE);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
@@ -114,34 +133,6 @@ public class OkHttpHttpVerifier implements HttpVerifier {
|
||||
return RequestBody.create(MediaType.parse(requestContentType), bodyArray);
|
||||
}
|
||||
|
||||
private boolean isGrpc(String contentType) {
|
||||
return contentType.startsWith("application/grpc");
|
||||
}
|
||||
|
||||
// the encoded body should already have proper byte values
|
||||
// TODO: This should be removed?
|
||||
private byte[] grpcRequestBody(Request request) {
|
||||
byte[] bodyArray; // TODO: Add compression support
|
||||
byte compressedFlag = 0;
|
||||
byte[] message = request.body().asByteArray();
|
||||
byte[] messageLength = ByteBuffer.allocate(4).putInt(message.length).array();
|
||||
bodyArray = ByteBuffer.allocate(1 + messageLength.length + message.length)
|
||||
.put(compressedFlag).put(messageLength).put(message).array();
|
||||
return bodyArray;
|
||||
}
|
||||
|
||||
// TODO: This should be removed?
|
||||
private byte[] grpcResponseBody(byte[] responseBody) {
|
||||
// 5 value = 4th index
|
||||
// 1 for compression, 4 for message size
|
||||
int actualPayloadSize = responseBody.length - 5;
|
||||
byte[] destination = new byte[actualPayloadSize];
|
||||
System.arraycopy(responseBody, 5, destination, 0, actualPayloadSize);
|
||||
responseBody = destination;
|
||||
return responseBody;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private byte[] responseBody(okhttp3.Response res) throws IOException {
|
||||
return res.body() != null ? res.body().bytes() : null;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.cloud.contract.verifier.http;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.contract.spec.internal.HttpMethods;
|
||||
@@ -40,6 +42,8 @@ public class Request {
|
||||
|
||||
private final String path;
|
||||
|
||||
private final List<AbstractMap.SimpleEntry<String, String>> queryParameters;
|
||||
|
||||
private final Body body;
|
||||
|
||||
private final Map<String, Object> headers;
|
||||
@@ -48,12 +52,14 @@ public class Request {
|
||||
|
||||
Request(ContractVerifierHttpMetadata.Protocol protocol,
|
||||
ContractVerifierHttpMetadata.Scheme scheme, HttpMethods.HttpMethod method,
|
||||
String path, Body body, Map<String, Object> headers,
|
||||
Map<String, Object> cookies) {
|
||||
String path, List<AbstractMap.SimpleEntry<String, String>> queryParameters,
|
||||
Body body, Map<String, Object> headers, Map<String, Object> cookies) {
|
||||
this.protocol = protocol;
|
||||
this.scheme = scheme;
|
||||
this.method = method;
|
||||
this.path = path;
|
||||
this.queryParameters = queryParameters == null ? new LinkedList<>()
|
||||
: queryParameters;
|
||||
this.body = body;
|
||||
this.headers = headers == null ? new HashMap<>() : headers;
|
||||
this.cookies = cookies == null ? new HashMap<>() : cookies;
|
||||
@@ -121,6 +127,13 @@ public class Request {
|
||||
return this.cookies;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return query parameters
|
||||
*/
|
||||
public List<AbstractMap.SimpleEntry<String, String>> queryParams() {
|
||||
return this.queryParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to pick the HTTP method.
|
||||
* @return method builder
|
||||
@@ -217,6 +230,8 @@ public class Request {
|
||||
|
||||
final String path;
|
||||
|
||||
List<AbstractMap.SimpleEntry<String, String>> queryParameters = new LinkedList<>();
|
||||
|
||||
ContractVerifierHttpMetadata.Protocol protocol = ContractVerifierHttpMetadata.Protocol.HTTP_1_1;
|
||||
|
||||
ContractVerifierHttpMetadata.Scheme scheme = ContractVerifierHttpMetadata.Scheme.HTTP;
|
||||
@@ -259,6 +274,16 @@ public class Request {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name - query parameter name
|
||||
* @param value - query parameter value
|
||||
* @return builder
|
||||
*/
|
||||
public Request.Builder queryParam(String name, String value) {
|
||||
this.queryParameters.add(new AbstractMap.SimpleEntry<>(name, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param headers HTTP headers
|
||||
* @return builder
|
||||
@@ -292,7 +317,7 @@ public class Request {
|
||||
*/
|
||||
public Request build() {
|
||||
return new Request(this.protocol, this.scheme, this.method, this.path,
|
||||
this.body, this.headers, this.cookies);
|
||||
this.queryParameters, this.body, this.headers, this.cookies);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -553,6 +553,7 @@ response:
|
||||
"spock" | { properties.testFramework = TestFramework.SPOCK }
|
||||
"mockmvc" | { properties.testMode = TestMode.MOCKMVC }
|
||||
"webclient" | { properties.testMode = TestMode.WEBTESTCLIENT }
|
||||
"custom" | { properties.testMode = TestMode.CUSTOM }
|
||||
}
|
||||
|
||||
@Issue('#169')
|
||||
@@ -627,6 +628,7 @@ response:
|
||||
"spock" | { properties.testFramework = TestFramework.SPOCK }
|
||||
"mockmvc" | { properties.testMode = TestMode.MOCKMVC }
|
||||
"webclient" | { properties.testMode = TestMode.WEBTESTCLIENT }
|
||||
"custom" | { properties.testMode = TestMode.CUSTOM }
|
||||
}
|
||||
|
||||
def 'should generate test for empty body with #methodBuilderName'() {
|
||||
|
||||
Reference in New Issue
Block a user