Added support for fromRequest.path
without this change you can't referrence path of the request in the response with this change that functionality is added also Removed lang3 dependency and changed it to commons-text fixes #388
This commit is contained in:
@@ -132,3 +132,11 @@ The flow for setting the generated tests package name will look like this:
|
||||
|
||||
Related to
|
||||
https://github.com/spring-cloud/spring-cloud-contract/issues/260[issue 260].
|
||||
|
||||
==== New methods in TemplateProcessor
|
||||
|
||||
In order to add support for `fromRequest.path` some methods had to be added to the
|
||||
`TemplateProcessor` interface.
|
||||
|
||||
Related to
|
||||
https://github.com/spring-cloud/spring-cloud-contract/issues/388[issue 388].
|
||||
4
pom.xml
4
pom.xml
@@ -130,8 +130,8 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.6</version>
|
||||
<artifactId>commons-text</artifactId>
|
||||
<version>1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>au.com.dius</groupId>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<artifactId>commons-text</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
|
||||
@@ -23,7 +23,7 @@ interface ContractTemplate {
|
||||
String closingTemplate()
|
||||
|
||||
/**
|
||||
* Returns the template for retrieving a URL from request
|
||||
* Returns the template for retrieving a URL path and query from request
|
||||
*/
|
||||
String url()
|
||||
|
||||
@@ -40,6 +40,17 @@ interface ContractTemplate {
|
||||
*/
|
||||
String query(String key, int index)
|
||||
|
||||
/**
|
||||
* Returns the template for retrieving a URL path
|
||||
*/
|
||||
String path()
|
||||
|
||||
/**
|
||||
* Returns the template for retrieving nth value of a URL path (zero indexed) e.g. {{{ request.path.[2] }}}
|
||||
* @param index
|
||||
*/
|
||||
String path(int index)
|
||||
|
||||
/**
|
||||
* Returns the template for retrieving the first value of a request header e.g. {{{ request.headers.X-Request-Id }}}
|
||||
* @param key
|
||||
|
||||
@@ -2,13 +2,13 @@ package org.springframework.cloud.contract.spec.internal
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import org.springframework.cloud.contract.spec.ContractTemplate
|
||||
|
||||
/**
|
||||
* Helper class to reference the request body parameters
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.1.0
|
||||
*/
|
||||
|
||||
@CompileStatic
|
||||
class FromRequest {
|
||||
|
||||
@@ -46,6 +46,21 @@ class FromRequest {
|
||||
return new DslProperty(template.query(key, index))
|
||||
}
|
||||
|
||||
/**
|
||||
* URL path
|
||||
*/
|
||||
DslProperty path() {
|
||||
return new DslProperty(template.path())
|
||||
}
|
||||
|
||||
/**
|
||||
* nth value of a URL path (zero indexed) e.g. {{{ request.path.[2] }}}
|
||||
* @param index
|
||||
*/
|
||||
DslProperty path(int index) {
|
||||
return new DslProperty(template.path(index))
|
||||
}
|
||||
|
||||
/**
|
||||
* First value of a request header e.g. request.headers.X-Request-Id
|
||||
* @param key
|
||||
|
||||
@@ -38,6 +38,16 @@ class HandlebarsContractTemplate implements ContractTemplate {
|
||||
return wrapped("request.query.${key}.[${index}]")
|
||||
}
|
||||
|
||||
@Override
|
||||
String path() {
|
||||
return wrapped("request.path")
|
||||
}
|
||||
|
||||
@Override
|
||||
String path(int index) {
|
||||
return wrapped("request.path.[${index}]")
|
||||
}
|
||||
|
||||
@Override
|
||||
String header(String key) {
|
||||
return header(key, 0)
|
||||
|
||||
@@ -27,7 +27,7 @@ import repackaged.nl.flotsam.xeger.Xeger
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
import static org.apache.commons.lang3.StringEscapeUtils.escapeJava
|
||||
import static org.apache.commons.text.StringEscapeUtils.escapeJava
|
||||
|
||||
/**
|
||||
* Converts WireMock stubs into the DSL format
|
||||
|
||||
@@ -20,9 +20,8 @@ import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
import wiremock.com.google.common.collect.ListMultimap
|
||||
import groovy.transform.PackageScope
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import wiremock.com.google.common.collect.ListMultimap
|
||||
|
||||
import org.springframework.cloud.contract.spec.ContractVerifierException
|
||||
import org.springframework.cloud.contract.verifier.builder.JavaTestGenerator
|
||||
@@ -31,13 +30,13 @@ import org.springframework.cloud.contract.verifier.config.ContractVerifierConfig
|
||||
import org.springframework.cloud.contract.verifier.file.ContractFileScanner
|
||||
import org.springframework.cloud.contract.verifier.file.ContractMetadata
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader
|
||||
import org.springframework.util.StringUtils
|
||||
|
||||
import static org.springframework.cloud.contract.verifier.util.NamesUtil.afterLast
|
||||
import static org.springframework.cloud.contract.verifier.util.NamesUtil.beforeLast
|
||||
import static org.springframework.cloud.contract.verifier.util.NamesUtil.convertIllegalPackageChars
|
||||
import static org.springframework.cloud.contract.verifier.util.NamesUtil.directoryToPackage
|
||||
import static org.springframework.cloud.contract.verifier.util.NamesUtil.toLastDot
|
||||
|
||||
/**
|
||||
* @author Jakub Kubrynski, codearte.io
|
||||
*/
|
||||
@@ -104,7 +103,7 @@ class TestGenerator {
|
||||
|
||||
private String relativizeContractPath(Map.Entry<Path, Collection<Path>> entry) {
|
||||
Path relativePath = configProperties.contractsDslDir.toPath().relativize(entry.getKey())
|
||||
if (StringUtils.isBlank(relativePath.toString())) {
|
||||
if (StringUtils.isEmpty(relativePath.toString())) {
|
||||
return DEFAULT_CLASS_PREFIX
|
||||
}
|
||||
return relativePath.toString()
|
||||
|
||||
@@ -22,7 +22,7 @@ import com.jayway.jsonpath.PathNotFoundException
|
||||
import groovy.json.JsonOutput
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.transform.TypeChecked
|
||||
import org.apache.commons.lang3.StringEscapeUtils
|
||||
import org.apache.commons.text.StringEscapeUtils
|
||||
import org.apache.commons.logging.Log
|
||||
import org.apache.commons.logging.LogFactory
|
||||
import org.springframework.cloud.contract.spec.Contract
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.springframework.cloud.contract.verifier.util.ContentUtils
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import static org.apache.commons.lang3.StringEscapeUtils.escapeJava
|
||||
import static org.apache.commons.text.StringEscapeUtils.escapeJava
|
||||
import static org.springframework.cloud.contract.verifier.util.ContentUtils.getGroovyMultipartFileParameterContent
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,8 @@ package org.springframework.cloud.contract.verifier.builder
|
||||
import groovy.json.JsonOutput
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.Immutable
|
||||
import org.apache.commons.lang3.StringEscapeUtils
|
||||
import org.apache.commons.text.StringEscapeUtils
|
||||
|
||||
import org.springframework.cloud.contract.spec.internal.DslProperty
|
||||
import org.springframework.cloud.contract.spec.internal.Request
|
||||
import org.springframework.cloud.contract.verifier.util.ContentUtils
|
||||
@@ -29,6 +30,11 @@ class TestSideRequestTemplateModel {
|
||||
*/
|
||||
final Map<String, List<String>> query
|
||||
|
||||
/**
|
||||
* List of path entries
|
||||
*/
|
||||
final Path path
|
||||
|
||||
/**
|
||||
* Map containing request headers
|
||||
*/
|
||||
@@ -46,10 +52,14 @@ class TestSideRequestTemplateModel {
|
||||
|
||||
static TestSideRequestTemplateModel from(final Request request) {
|
||||
String url = MapConverter.getTestSideValues(request.url ?: request.urlPath)
|
||||
Path paths = new Path(buildPathsFromUrl(url))
|
||||
Map<String, List<String>> query = (Map<String, List<String>>) (request.url ?: request.urlPath)
|
||||
.queryParameters?.parameters?.groupBy { it.name }?.collectEntries {
|
||||
[(it.key): it.value.collect { MapConverter.getTestSideValues(it) }]
|
||||
}
|
||||
String fullUrl = (query == null || query.isEmpty()) ? url :
|
||||
url + "?" + query.collect { String name, List<String> values ->
|
||||
return values.collect { "${name}=${it}"}.join("&") }.join("&")
|
||||
Map<String, List<String>> headers = (Map<String, List<String>>) (request.headers?.entries?.groupBy {
|
||||
it.name
|
||||
}?.collectEntries {
|
||||
@@ -57,7 +67,14 @@ class TestSideRequestTemplateModel {
|
||||
})
|
||||
String body = trimmedAndEscapedBody(request.body)
|
||||
String rawBody = getBodyAsRawJson(request.body)
|
||||
return new TestSideRequestTemplateModel(url, query, headers, body, rawBody)
|
||||
return new TestSideRequestTemplateModel(fullUrl, query, paths, headers, body, rawBody)
|
||||
}
|
||||
|
||||
private static List<String> buildPathsFromUrl(String url) {
|
||||
String fakeUrl = "http://foo.bar" + (url.startsWith("/") ? url : "/" + url)
|
||||
List<String> paths = new URL(fakeUrl).path.split("/") as List<String>
|
||||
paths.remove(0)
|
||||
return paths
|
||||
}
|
||||
|
||||
private static String trimmedAndEscapedBody(Object body) {
|
||||
@@ -81,3 +98,16 @@ class TestSideRequestTemplateModel {
|
||||
return bodyValue
|
||||
}
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
class Path extends ArrayList<String> {
|
||||
|
||||
Path(List<String> list) {
|
||||
this.addAll(list)
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
return "/" + this.join("/")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import wiremock.com.github.jknack.handlebars.Helper
|
||||
import wiremock.com.github.jknack.handlebars.Options
|
||||
import com.github.tomakehurst.wiremock.extension.responsetemplating.RequestTemplateModel
|
||||
import groovy.transform.CompileStatic
|
||||
import org.apache.commons.lang3.StringEscapeUtils
|
||||
import org.apache.commons.text.StringEscapeUtils
|
||||
import org.springframework.cloud.contract.verifier.builder.TestSideRequestTemplateModel
|
||||
/**
|
||||
* A Handlebars helper for the {@code escapejsonbody} helper function.
|
||||
|
||||
@@ -20,7 +20,6 @@ import wiremock.com.google.common.collect.ArrayListMultimap
|
||||
import wiremock.com.google.common.collect.ListMultimap
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.util.logging.Slf4j
|
||||
import org.apache.commons.lang3.SystemUtils
|
||||
import org.springframework.cloud.contract.spec.Contract
|
||||
import org.springframework.cloud.contract.spec.ContractConverter
|
||||
import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter
|
||||
@@ -43,6 +42,10 @@ import java.util.regex.Pattern
|
||||
@Slf4j
|
||||
class ContractFileScanner {
|
||||
|
||||
private static final String OS_NAME = System.getProperty("os.name");
|
||||
private static final String OS_NAME_WINDOWS_PREFIX = "Windows";
|
||||
protected static final boolean IS_OS_WINDOWS = getOSMatchesName(OS_NAME_WINDOWS_PREFIX);
|
||||
|
||||
private static final String MATCH_PREFIX = "glob:"
|
||||
private static final Pattern SCENARIO_STEP_FILENAME_PATTERN = Pattern.compile("[0-9]+_.*")
|
||||
private final File baseDir
|
||||
@@ -62,7 +65,7 @@ class ContractFileScanner {
|
||||
return patterns.collect({
|
||||
String syntaxAndPattern = MATCH_PREFIX + '**' + File.separator + it
|
||||
// FIXME: This looks strange, need to be checked on windows
|
||||
if (SystemUtils.IS_OS_WINDOWS) {
|
||||
if (IS_OS_WINDOWS) {
|
||||
syntaxAndPattern = syntaxAndPattern.replace("\\", "\\\\")
|
||||
}
|
||||
fileSystem.getPathMatcher(syntaxAndPattern)
|
||||
@@ -177,4 +180,31 @@ class ContractFileScanner {
|
||||
}
|
||||
return path.substring(extIndex + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides if the operating system matches.
|
||||
*
|
||||
* @param osNamePrefix the prefix for the os name
|
||||
* @return true if matches, or false if not or can't determine
|
||||
*/
|
||||
private static boolean getOSMatchesName(final String osNamePrefix) {
|
||||
return isOSNameMatch(OS_NAME, osNamePrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides if the operating system matches.
|
||||
* <p>
|
||||
* This method is package private instead of private to support unit test invocation.
|
||||
* </p>
|
||||
*
|
||||
* @param osName the actual OS name
|
||||
* @param osNamePrefix the prefix for the expected OS name
|
||||
* @return true if matches, or false if not or can't determine
|
||||
*/
|
||||
private static boolean isOSNameMatch(final String osName, final String osNamePrefix) {
|
||||
if (osName == null) {
|
||||
return false;
|
||||
}
|
||||
return osName.startsWith(osNamePrefix);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.springframework.cloud.contract.verifier.template
|
||||
|
||||
import org.springframework.cloud.contract.spec.internal.Request
|
||||
|
||||
/**
|
||||
* Contract for conversion of templated responses.
|
||||
*
|
||||
|
||||
@@ -32,9 +32,9 @@ import org.springframework.cloud.contract.spec.internal.OptionalProperty
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import static org.apache.commons.lang3.StringEscapeUtils.escapeJava
|
||||
import static org.apache.commons.lang3.StringEscapeUtils.escapeJson
|
||||
import static org.apache.commons.lang3.StringEscapeUtils.escapeXml11
|
||||
import static org.apache.commons.text.StringEscapeUtils.escapeJava
|
||||
import static org.apache.commons.text.StringEscapeUtils.escapeJson
|
||||
import static org.apache.commons.text.StringEscapeUtils.escapeXml11
|
||||
|
||||
/**
|
||||
* A utility class that can operate on a message body basing on the provided Content Type.
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
|
||||
package org.springframework.cloud.contract.verifier.util;
|
||||
|
||||
import com.toomuchcoding.jsonassert.JsonVerifiable;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.apache.commons.lang3.StringEscapeUtils.escapeJava;
|
||||
import com.toomuchcoding.jsonassert.JsonVerifiable;
|
||||
|
||||
import static org.apache.commons.text.StringEscapeUtils.escapeJava;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link MethodBufferingJsonVerifiable} that contains a list
|
||||
|
||||
@@ -23,7 +23,7 @@ import org.springframework.cloud.contract.spec.util.RegexpUtils
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import static org.apache.commons.lang3.StringEscapeUtils.escapeJson
|
||||
import static org.apache.commons.text.StringEscapeUtils.escapeJson
|
||||
import static ContentType.*
|
||||
import static ContentUtils.extractValue
|
||||
|
||||
|
||||
@@ -2291,6 +2291,8 @@ DocumentContext parsedJson = JsonPath.parse(json);
|
||||
}
|
||||
body(
|
||||
url: fromRequest().url(),
|
||||
path: fromRequest().path(),
|
||||
pathIndex: fromRequest().path(1),
|
||||
param: fromRequest().query("foo"),
|
||||
paramIndex: fromRequest().query("foo", 1),
|
||||
authorization: fromRequest().header("Authorization"),
|
||||
@@ -2312,7 +2314,9 @@ DocumentContext parsedJson = JsonPath.parse(json);
|
||||
SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, test)
|
||||
then:
|
||||
!test.contains('''DslProperty''')
|
||||
test.contains('''assertThatJson(parsedJson).field("['url']").isEqualTo("/api/v1/xxxx")''')
|
||||
test.contains('''assertThatJson(parsedJson).field("['url']").isEqualTo("/api/v1/xxxx?foo=bar&foo=bar2")''')
|
||||
test.contains('''assertThatJson(parsedJson).field("['path']").isEqualTo("/api/v1/xxxx")''')
|
||||
test.contains('''assertThatJson(parsedJson).field("['pathIndex']").isEqualTo("v1")''')
|
||||
test.contains('''assertThatJson(parsedJson).field("['fullBody']").isEqualTo("{\\"foo\\":\\"bar\\",\\"baz\\":5}")''')
|
||||
test.contains('''assertThatJson(parsedJson).field("['paramIndex']").isEqualTo("bar2")''')
|
||||
test.contains('''assertThatJson(parsedJson).field("['responseFoo']").isEqualTo("bar")''')
|
||||
|
||||
@@ -1759,6 +1759,8 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie
|
||||
}
|
||||
body(
|
||||
url: fromRequest().url(),
|
||||
path: fromRequest().path(),
|
||||
pathIndex: fromRequest().path(1),
|
||||
param: fromRequest().query("foo"),
|
||||
paramIndex: fromRequest().query("foo", 1),
|
||||
authorization: fromRequest().header("Authorization"),
|
||||
@@ -1796,7 +1798,7 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"body" : "{\\"url\\":\\"{{{request.url}}}\\",\\"param\\":\\"{{{request.query.foo.[0]}}}\\",\\"paramIndex\\":\\"{{{request.query.foo.[1]}}}\\",\\"authorization\\":\\"{{{request.headers.Authorization.[0]}}}\\",\\"authorization2\\":\\"{{{request.headers.Authorization.[1]}}}\\",\\"fullBody\\":\\"{{{escapejsonbody}}}\\",\\"responseFoo\\":\\"{{{jsonpath this '$.foo'}}}\\",\\"responseBaz\\":{{{jsonpath this '$.baz'}}} ,\\"responseBaz2\\":\\"Bla bla {{{jsonpath this '$.foo'}}} bla bla\\"}",
|
||||
"body" : "{\\"url\\":\\"{{{request.url}}}\\",\\"path\\":\\"{{{request.path}}}\\",\\"pathIndex\\":\\"{{{request.path.[1]}}}\\",\\"param\\":\\"{{{request.query.foo.[0]}}}\\",\\"paramIndex\\":\\"{{{request.query.foo.[1]}}}\\",\\"authorization\\":\\"{{{request.headers.Authorization.[0]}}}\\",\\"authorization2\\":\\"{{{request.headers.Authorization.[1]}}}\\",\\"fullBody\\":\\"{{{escapejsonbody}}}\\",\\"responseFoo\\":\\"{{{jsonpath this '$.foo'}}}\\",\\"responseBaz\\":{{{jsonpath this '$.baz'}}} ,\\"responseBaz2\\":\\"Bla bla {{{jsonpath this '$.foo'}}} bla bla\\"}",
|
||||
"headers" : {
|
||||
"Authorization" : "{{{request.headers.Authorization.[0]}}};foo"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user