Allows passing of regex type (#832)
without this change if one does $(regex("[0-9]")) we have no knowledge of whether the result should be text or a number. What we do ATM is we always generate a String
with this change once can pass the type of regular expression and we will generate the concrete value of that given type
fixes gh-768
This commit is contained in:
committed by
GitHub
parent
58bf533462
commit
56fbc11f62
@@ -21,7 +21,6 @@ import groovy.transform.Canonical
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.ToString
|
||||
|
||||
/**
|
||||
* Matching strategy of dynamic parts of the body.
|
||||
*
|
||||
@@ -58,15 +57,18 @@ class BodyMatchers {
|
||||
return new MatchingTypeValue(MatchingType.TIMESTAMP, this.regexPatterns.isoDateTime())
|
||||
}
|
||||
|
||||
MatchingTypeValue byRegex(String regex) {
|
||||
assert regex
|
||||
return new MatchingTypeValue(MatchingType.REGEX, regex)
|
||||
RegexMatchingTypeValue byRegex(String regex) {
|
||||
return byRegex(Pattern.compile(regex))
|
||||
}
|
||||
|
||||
// Backward compatibility with RegexPatterns
|
||||
MatchingTypeValue byRegex(Pattern regex) {
|
||||
RegexMatchingTypeValue byRegex(RegexProperty regex) {
|
||||
assert regex
|
||||
return new MatchingTypeValue(MatchingType.REGEX, regex)
|
||||
return new RegexMatchingTypeValue(MatchingType.REGEX, regex)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue byRegex(Pattern regex) {
|
||||
assert regex
|
||||
return new RegexMatchingTypeValue(MatchingType.REGEX, new RegexProperty(regex))
|
||||
}
|
||||
|
||||
MatchingTypeValue byEquality() {
|
||||
@@ -130,6 +132,61 @@ class JsonPathBodyMatcher implements BodyMatcher {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matching type with corresponding values
|
||||
*/
|
||||
|
||||
@ToString(includePackage = false)
|
||||
@EqualsAndHashCode
|
||||
class RegexMatchingTypeValue extends MatchingTypeValue {
|
||||
|
||||
RegexMatchingTypeValue(MatchingType type, Object value, Integer minTypeOccurrence, Integer maxTypeOccurrence) {
|
||||
super(type, value, minTypeOccurrence, maxTypeOccurrence)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue(MatchingType type, Object value) {
|
||||
super(type, value)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue asInteger() {
|
||||
return typed(Integer)
|
||||
}
|
||||
|
||||
private RegexMatchingTypeValue typed(Class clazz) {
|
||||
assert this.value instanceof RegexProperty
|
||||
RegexProperty regexProperty = (RegexProperty) this.value
|
||||
return new RegexMatchingTypeValue(
|
||||
this.type, new RegexProperty(regexProperty.clientValue,
|
||||
regexProperty.serverValue, clazz),
|
||||
this.minTypeOccurrence, this.maxTypeOccurrence
|
||||
)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue asDouble() {
|
||||
return typed(Double)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue asFloat() {
|
||||
return typed(Float)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue asLong() {
|
||||
return typed(Long)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue asShort() {
|
||||
return typed(Short)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue asString() {
|
||||
return typed(String)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue asBooleanType() {
|
||||
return typed(Boolean)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matching type with corresponding values
|
||||
*/
|
||||
|
||||
@@ -114,19 +114,27 @@ class Common {
|
||||
return value(server, client)
|
||||
}
|
||||
|
||||
Pattern regex(String regex) {
|
||||
return Pattern.compile(regex)
|
||||
RegexProperty regex(String regex) {
|
||||
return regexProperty(Pattern.compile(regex))
|
||||
}
|
||||
|
||||
RegexProperty regex(RegexProperty regex) {
|
||||
return regex
|
||||
}
|
||||
|
||||
// Backward compatibility with RegexPatterns
|
||||
Pattern regex(Pattern regex) {
|
||||
return regex
|
||||
RegexProperty regex(Pattern regex) {
|
||||
return regexProperty(regex)
|
||||
}
|
||||
|
||||
OptionalProperty optional(Object object) {
|
||||
return new OptionalProperty(object)
|
||||
}
|
||||
|
||||
RegexProperty regexProperty(Object object) {
|
||||
return new RegexProperty(object)
|
||||
}
|
||||
|
||||
ExecutionProperty execute(String commandToExecute) {
|
||||
return new ExecutionProperty(commandToExecute)
|
||||
}
|
||||
|
||||
@@ -16,16 +16,13 @@
|
||||
|
||||
package org.springframework.cloud.contract.spec.internal
|
||||
|
||||
import groovy.util.logging.Commons
|
||||
import groovy.util.logging.Slf4j
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.ToString
|
||||
import groovy.transform.TypeChecked
|
||||
import repackaged.nl.flotsam.xeger.Xeger
|
||||
import groovy.util.logging.Commons
|
||||
/**
|
||||
* Represents an input for messaging. The input can be a message or some
|
||||
* action inside the application.
|
||||
@@ -89,17 +86,31 @@ class Input extends Common {
|
||||
}
|
||||
|
||||
DslProperty value(ClientDslProperty client) {
|
||||
Object clientValue = client.clientValue
|
||||
if (client.clientValue instanceof Pattern) {
|
||||
clientValue = new Xeger(((Pattern)client.clientValue).pattern()).generate()
|
||||
Object dynamicValue = client.clientValue
|
||||
Object concreteValue = client.serverValue
|
||||
if (dynamicValue instanceof RegexProperty) {
|
||||
return dynamicValue.dynamicClientConcreteProducer()
|
||||
}
|
||||
return new DslProperty(client.clientValue, clientValue)
|
||||
return new DslProperty(dynamicValue, concreteValue)
|
||||
}
|
||||
|
||||
DslProperty value(RegexProperty prop) {
|
||||
return value(client(prop))
|
||||
}
|
||||
|
||||
DslProperty $(RegexProperty prop) {
|
||||
return value(client(prop))
|
||||
}
|
||||
|
||||
DslProperty $(ClientDslProperty client) {
|
||||
return value(client)
|
||||
}
|
||||
|
||||
@Override
|
||||
RegexProperty regexProperty(Object object) {
|
||||
return new RegexProperty(object).dynamicClientConcreteProducer()
|
||||
}
|
||||
|
||||
@EqualsAndHashCode(includeFields = true, callSuper = true)
|
||||
@ToString(includeSuper = true)
|
||||
static class BodyType extends DslProperty {
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.cloud.contract.spec.internal
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.transform.ToString
|
||||
|
||||
/**
|
||||
@@ -41,7 +40,12 @@ class OptionalProperty implements Serializable {
|
||||
* in an optional function
|
||||
*/
|
||||
String optionalPattern() {
|
||||
return "($value)?"
|
||||
return "(${value()})?"
|
||||
}
|
||||
|
||||
String value() {
|
||||
return this.value instanceof RegexProperty ?
|
||||
((RegexProperty) this.value).pattern.pattern() : this.value
|
||||
}
|
||||
|
||||
protected Pattern optionalPatternValue() {
|
||||
|
||||
@@ -16,17 +16,13 @@
|
||||
|
||||
package org.springframework.cloud.contract.spec.internal
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.ToString
|
||||
import groovy.transform.TypeChecked
|
||||
import groovy.util.logging.Commons
|
||||
import groovy.util.logging.Slf4j
|
||||
import org.apache.commons.lang3.StringEscapeUtils
|
||||
import repackaged.nl.flotsam.xeger.Xeger
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
* Represents an output for messaging. Used for verifying
|
||||
* the body and headers that are sent.
|
||||
@@ -83,26 +79,57 @@ class OutputMessage extends Common {
|
||||
this.assertThat = new ExecutionProperty(assertThat)
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated - use the server dsl property
|
||||
*/
|
||||
@Deprecated
|
||||
DslProperty value(ClientDslProperty clientDslProperty) {
|
||||
Object clientValue = clientDslProperty.clientValue
|
||||
// for the output messages ran via stub runner,
|
||||
// entries have to have fixed values
|
||||
if (clientDslProperty.clientValue instanceof Pattern) {
|
||||
clientValue = StringEscapeUtils.escapeJava(new Xeger(((Pattern)clientDslProperty.clientValue).pattern()).generate())
|
||||
}
|
||||
return new DslProperty(clientValue, clientDslProperty.clientValue)
|
||||
return value(new ServerDslProperty(clientDslProperty.serverValue, clientDslProperty.clientValue))
|
||||
}
|
||||
|
||||
DslProperty value(ServerDslProperty serverDslProperty) {
|
||||
Object concreteValue = serverDslProperty.clientValue
|
||||
Object dynamicValue = serverDslProperty.serverValue
|
||||
// for the output messages ran via stub runner,
|
||||
// entries have to have fixed values
|
||||
if (dynamicValue instanceof RegexProperty) {
|
||||
return dynamicValue
|
||||
.concreteClientEscapedDynamicProducer()
|
||||
}
|
||||
return new DslProperty(concreteValue, dynamicValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated - use the server dsl property
|
||||
*/
|
||||
@Deprecated
|
||||
DslProperty $(ClientDslProperty client) {
|
||||
return value(client)
|
||||
}
|
||||
|
||||
DslProperty $(ServerDslProperty property) {
|
||||
return value(property)
|
||||
}
|
||||
|
||||
DslProperty $(Pattern pattern) {
|
||||
return value(client(pattern))
|
||||
return value(new RegexProperty(pattern))
|
||||
}
|
||||
|
||||
DslProperty $(RegexProperty pattern) {
|
||||
return value(pattern)
|
||||
}
|
||||
|
||||
DslProperty value(RegexProperty pattern) {
|
||||
return value(producer(pattern))
|
||||
}
|
||||
|
||||
DslProperty $(OptionalProperty property) {
|
||||
return value(client(property.optionalPatternValue()))
|
||||
return value(producer(property.optionalPatternValue()))
|
||||
}
|
||||
|
||||
@Override
|
||||
RegexProperty regexProperty(Object object) {
|
||||
return new RegexProperty(object).concreteClientDynamicProducer()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -60,80 +60,80 @@ class RegexPatterns {
|
||||
return Pattern.compile(values.collect({"^$it\$"}).join("|"))
|
||||
}
|
||||
|
||||
Pattern onlyAlphaUnicode() {
|
||||
return ONLY_ALPHA_UNICODE
|
||||
RegexProperty onlyAlphaUnicode() {
|
||||
return new RegexProperty(ONLY_ALPHA_UNICODE).asString()
|
||||
}
|
||||
|
||||
Pattern alphaNumeric() {
|
||||
return ALPHA_NUMERIC
|
||||
RegexProperty alphaNumeric() {
|
||||
return new RegexProperty(ALPHA_NUMERIC).asString()
|
||||
}
|
||||
|
||||
Pattern number() {
|
||||
return NUMBER
|
||||
RegexProperty number() {
|
||||
return new RegexProperty(NUMBER).asDouble()
|
||||
}
|
||||
|
||||
Pattern positiveInt() {
|
||||
return POSITIVE_INT
|
||||
RegexProperty positiveInt() {
|
||||
return new RegexProperty(POSITIVE_INT).asInteger()
|
||||
}
|
||||
|
||||
Pattern anyBoolean() {
|
||||
return TRUE_OR_FALSE
|
||||
RegexProperty anyBoolean() {
|
||||
return new RegexProperty(TRUE_OR_FALSE).asBooleanType()
|
||||
}
|
||||
|
||||
Pattern anInteger() {
|
||||
return INTEGER
|
||||
RegexProperty anInteger() {
|
||||
return new RegexProperty(INTEGER).asInteger()
|
||||
}
|
||||
|
||||
Pattern aDouble() {
|
||||
return DOUBLE
|
||||
RegexProperty aDouble() {
|
||||
return new RegexProperty(DOUBLE).asDouble()
|
||||
}
|
||||
|
||||
Pattern ipAddress() {
|
||||
return IP_ADDRESS
|
||||
RegexProperty ipAddress() {
|
||||
return new RegexProperty(IP_ADDRESS).asString()
|
||||
}
|
||||
|
||||
Pattern hostname() {
|
||||
return HOSTNAME_PATTERN
|
||||
RegexProperty hostname() {
|
||||
return new RegexProperty(HOSTNAME_PATTERN).asString()
|
||||
}
|
||||
|
||||
Pattern email() {
|
||||
return EMAIL
|
||||
RegexProperty email() {
|
||||
return new RegexProperty(EMAIL).asString()
|
||||
}
|
||||
|
||||
Pattern url() {
|
||||
return URL
|
||||
RegexProperty url() {
|
||||
return new RegexProperty(URL).asString()
|
||||
}
|
||||
|
||||
Pattern httpsUrl() {
|
||||
return HTTPS_URL
|
||||
RegexProperty httpsUrl() {
|
||||
return new RegexProperty(HTTPS_URL).asString()
|
||||
}
|
||||
|
||||
Pattern uuid(){
|
||||
return UUID
|
||||
RegexProperty uuid(){
|
||||
return new RegexProperty(UUID).asString()
|
||||
}
|
||||
|
||||
Pattern isoDate() {
|
||||
return ANY_DATE
|
||||
RegexProperty isoDate() {
|
||||
return new RegexProperty(ANY_DATE).asString()
|
||||
}
|
||||
|
||||
Pattern isoDateTime() {
|
||||
return ANY_DATE_TIME
|
||||
RegexProperty isoDateTime() {
|
||||
return new RegexProperty(ANY_DATE_TIME).asString()
|
||||
}
|
||||
|
||||
Pattern isoTime() {
|
||||
return ANY_TIME
|
||||
RegexProperty isoTime() {
|
||||
return new RegexProperty(ANY_TIME).asString()
|
||||
}
|
||||
|
||||
Pattern iso8601WithOffset() {
|
||||
return ISO8601_WITH_OFFSET
|
||||
RegexProperty iso8601WithOffset() {
|
||||
return new RegexProperty(ISO8601_WITH_OFFSET).asString()
|
||||
}
|
||||
|
||||
Pattern nonEmpty() {
|
||||
return NON_EMPTY
|
||||
RegexProperty nonEmpty() {
|
||||
return new RegexProperty(NON_EMPTY).asString()
|
||||
}
|
||||
|
||||
Pattern nonBlank() {
|
||||
return NON_BLANK
|
||||
RegexProperty nonBlank() {
|
||||
return new RegexProperty(NON_BLANK).asString()
|
||||
}
|
||||
|
||||
// end::regexps[]
|
||||
@@ -150,7 +150,7 @@ class RegexPatterns {
|
||||
if (contentType == null) {
|
||||
return '.*'
|
||||
}
|
||||
if (contentType instanceof Pattern) {
|
||||
if (contentType instanceof RegexProperty) {
|
||||
return contentType.pattern()
|
||||
}
|
||||
return contentType.toString()
|
||||
@@ -163,7 +163,7 @@ class RegexPatterns {
|
||||
class UrlHelper {
|
||||
/**
|
||||
* Example: "http". Also called 'protocol'.
|
||||
* Scheme component is optional, even though the RFC doesn't make it optional. Since this regex is validating a
|
||||
* Scheme component is optional, even though the RFC doesn't make it optional. Since ((RegexProperty) this regex is validating a
|
||||
* submitted callback url, which determines where the browser will navigate to after a successful authentication,
|
||||
* the browser will use http or https for the scheme by default.
|
||||
* Not borrowed from dperini in order to allow any scheme type.
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.contract.spec.internal
|
||||
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.ToString
|
||||
import groovy.transform.TypeChecked
|
||||
import org.apache.commons.text.StringEscapeUtils
|
||||
import repackaged.nl.flotsam.xeger.Xeger
|
||||
/**
|
||||
* Represents a regular expression property
|
||||
*
|
||||
* @since 2.1.0
|
||||
*/
|
||||
@TypeChecked
|
||||
@EqualsAndHashCode
|
||||
@ToString(includePackage = false, includeNames = true)
|
||||
class RegexProperty extends DslProperty {
|
||||
final Pattern pattern
|
||||
private final Class clazz
|
||||
|
||||
RegexProperty(Object value) {
|
||||
this(value, value, null)
|
||||
}
|
||||
|
||||
RegexProperty(Object client, Object server) {
|
||||
this(client, server, null)
|
||||
}
|
||||
|
||||
RegexProperty(Object client, Object server, Class clazz) {
|
||||
super(client, server)
|
||||
boolean clientDynamic = client instanceof Pattern ||
|
||||
client instanceof RegexProperty
|
||||
boolean serverDynamic = server instanceof Pattern ||
|
||||
server instanceof RegexProperty
|
||||
if (!clientDynamic && !serverDynamic) {
|
||||
throw new IllegalStateException("Neither client not server side is dynamic")
|
||||
}
|
||||
Object dynamicValue = clientDynamic ? client : server
|
||||
if (dynamicValue instanceof Pattern) {
|
||||
this.pattern = dynamicValue
|
||||
this.clazz = clazz ?: String
|
||||
} else if (dynamicValue instanceof RegexProperty) {
|
||||
RegexProperty regexProperty = ((RegexProperty) dynamicValue)
|
||||
this.pattern = regexProperty.pattern
|
||||
this.clazz = clazz ?: regexProperty.clazz
|
||||
} else {
|
||||
this.clazz = clazz
|
||||
}
|
||||
}
|
||||
|
||||
Matcher matcher(CharSequence input) {
|
||||
return this.pattern.matcher(input)
|
||||
}
|
||||
|
||||
String pattern() {
|
||||
return this.pattern.pattern()
|
||||
}
|
||||
|
||||
Class clazz() {
|
||||
return this.class
|
||||
}
|
||||
|
||||
RegexProperty asInteger() {
|
||||
return new RegexProperty(this.clientValue, this.serverValue, Integer)
|
||||
}
|
||||
|
||||
RegexProperty asDouble() {
|
||||
return new RegexProperty(this.clientValue, this.serverValue, Double)
|
||||
}
|
||||
|
||||
RegexProperty asFloat() {
|
||||
return new RegexProperty(this.clientValue, this.serverValue, Float)
|
||||
}
|
||||
|
||||
RegexProperty asLong() {
|
||||
return new RegexProperty(this.clientValue, this.serverValue, Long)
|
||||
}
|
||||
|
||||
RegexProperty asShort() {
|
||||
return new RegexProperty(this.clientValue, this.serverValue, Short)
|
||||
}
|
||||
|
||||
RegexProperty asString() {
|
||||
return new RegexProperty(this.clientValue, this.serverValue, String)
|
||||
}
|
||||
|
||||
RegexProperty asBooleanType() {
|
||||
return new RegexProperty(this.clientValue, this.serverValue, Boolean)
|
||||
}
|
||||
|
||||
Object generate() {
|
||||
String generatedValue = new Xeger(this.pattern.pattern()).generate()
|
||||
switch (this.clazz) {
|
||||
case Integer: return Integer.parseInt(generatedValue)
|
||||
case Double: return Double.parseDouble(generatedValue)
|
||||
case Float: return Float.parseFloat(generatedValue)
|
||||
case Long: return Long.parseLong(generatedValue)
|
||||
case Short: return Short.parseShort(generatedValue)
|
||||
case Boolean: return Boolean.parseBoolean(generatedValue)
|
||||
default: return generatedValue
|
||||
}
|
||||
}
|
||||
|
||||
Object generateAndEscapeJavaStringIfNeeded() {
|
||||
Object generated = generate()
|
||||
if (isNumber()) {
|
||||
return generated
|
||||
}
|
||||
return StringEscapeUtils.escapeJava(generated as String)
|
||||
}
|
||||
|
||||
private boolean isNumber() {
|
||||
return Number.isAssignableFrom(this.clazz)
|
||||
}
|
||||
|
||||
RegexProperty dynamicClientConcreteProducer() {
|
||||
return new RegexProperty(this.pattern, generate(), this.clazz)
|
||||
}
|
||||
|
||||
RegexProperty concreteClientDynamicProducer() {
|
||||
return new RegexProperty(generate(), this.pattern, this.clazz)
|
||||
}
|
||||
|
||||
RegexProperty concreteClientEscapedDynamicProducer() {
|
||||
return new RegexProperty(generateAndEscapeJavaStringIfNeeded(), this.pattern, this.clazz)
|
||||
}
|
||||
|
||||
RegexProperty dynamicClientEscapedConcreteProducer() {
|
||||
return new RegexProperty(this.pattern, generateAndEscapeJavaStringIfNeeded(), this.clazz)
|
||||
}
|
||||
|
||||
boolean equals(o) {
|
||||
if (this.is(o)) return true
|
||||
if (getClass() != o.class) return false
|
||||
if (!super.equals(o)) return false
|
||||
RegexProperty that = (RegexProperty) o
|
||||
if (this.clazz != that.clazz) return false
|
||||
if (this.pattern != that.pattern) return false
|
||||
return true
|
||||
}
|
||||
|
||||
int hashCode() {
|
||||
int result = super.hashCode()
|
||||
result = 31 * result + (this.pattern != null ? pattern.hashCode() : 0)
|
||||
result = 31 * result + (this.clazz != null ? clazz.hashCode() : 0)
|
||||
return result
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
return this.pattern()
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,6 @@ import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.ToString
|
||||
import groovy.transform.TypeChecked
|
||||
import groovy.util.logging.Commons
|
||||
import org.apache.commons.lang3.StringEscapeUtils
|
||||
import repackaged.nl.flotsam.xeger.Xeger
|
||||
|
||||
import org.springframework.cloud.contract.spec.util.RegexpUtils
|
||||
/**
|
||||
@@ -208,13 +206,22 @@ class Request extends Common {
|
||||
}
|
||||
|
||||
DslProperty value(ClientDslProperty client) {
|
||||
Object clientValue = client.clientValue
|
||||
if (client.clientValue instanceof Pattern && client.isSingleValue()) {
|
||||
clientValue = StringEscapeUtils.escapeJava(new Xeger(((Pattern)client.clientValue).pattern()).generate())
|
||||
} else if (client.clientValue instanceof Pattern && !client.isSingleValue()) {
|
||||
clientValue = client.serverValue
|
||||
Object concreteValue = client.serverValue
|
||||
Object dynamicValue = client.clientValue
|
||||
if (dynamicValue instanceof RegexProperty && client.isSingleValue()) {
|
||||
return dynamicValue.dynamicClientEscapedConcreteProducer()
|
||||
} else if (concreteValue instanceof RegexProperty && !client.isSingleValue()) {
|
||||
concreteValue = dynamicValue
|
||||
}
|
||||
return new DslProperty(client.clientValue, clientValue)
|
||||
return new DslProperty(dynamicValue, concreteValue)
|
||||
}
|
||||
|
||||
DslProperty $(RegexProperty property) {
|
||||
return value(property)
|
||||
}
|
||||
|
||||
DslProperty value(RegexProperty property) {
|
||||
return value(client(property))
|
||||
}
|
||||
|
||||
DslProperty $(ClientDslProperty client) {
|
||||
@@ -222,13 +229,18 @@ class Request extends Common {
|
||||
}
|
||||
|
||||
DslProperty value(Pattern client) {
|
||||
return value(new ClientDslProperty(client))
|
||||
return value(new RegexProperty(client))
|
||||
}
|
||||
|
||||
DslProperty $(Pattern client) {
|
||||
return value(client)
|
||||
}
|
||||
|
||||
@Override
|
||||
RegexProperty regexProperty(Object object) {
|
||||
return new RegexProperty(object).dynamicClientConcreteProducer()
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated in favor of bodyMatchers to support other future bodyMatchers too
|
||||
*/
|
||||
@@ -246,7 +258,7 @@ class Request extends Common {
|
||||
|
||||
@Override
|
||||
DslProperty value(ClientDslProperty client, ServerDslProperty server) {
|
||||
if (server.clientValue instanceof Pattern) {
|
||||
if (server.clientValue instanceof RegexProperty) {
|
||||
throw new IllegalStateException("You can't have a regular expression for the request on the server side")
|
||||
}
|
||||
return super.value(client, server)
|
||||
@@ -254,7 +266,7 @@ class Request extends Common {
|
||||
|
||||
@Override
|
||||
DslProperty value(ServerDslProperty server, ClientDslProperty client) {
|
||||
if (server.clientValue instanceof Pattern) {
|
||||
if (server.clientValue instanceof RegexProperty) {
|
||||
throw new IllegalStateException("You can't have a regular expression for the request on the server side")
|
||||
}
|
||||
return super.value(server, client)
|
||||
|
||||
@@ -23,7 +23,6 @@ import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.ToString
|
||||
import groovy.transform.TypeChecked
|
||||
import groovy.util.logging.Commons
|
||||
import repackaged.nl.flotsam.xeger.Xeger
|
||||
|
||||
import org.springframework.cloud.contract.spec.util.RegexpUtils
|
||||
/**
|
||||
@@ -109,13 +108,12 @@ class Response extends Common {
|
||||
}
|
||||
|
||||
DslProperty value(ServerDslProperty server) {
|
||||
Object value = server.clientValue
|
||||
if (server.clientValue instanceof Pattern && server.isSingleValue()) {
|
||||
value = new Xeger(((Pattern)server.clientValue).pattern()).generate()
|
||||
} else if (server.clientValue instanceof Pattern && !server.isSingleValue()) {
|
||||
value = server.serverValue
|
||||
Object dynamicValue = server.serverValue
|
||||
Object concreteValue = server.clientValue
|
||||
if (dynamicValue instanceof RegexProperty && server.isSingleValue()) {
|
||||
return ((RegexProperty) dynamicValue).concreteClientDynamicProducer()
|
||||
}
|
||||
return new DslProperty(value, server.serverValue)
|
||||
return new DslProperty(concreteValue, dynamicValue)
|
||||
}
|
||||
|
||||
DslProperty $(ServerDslProperty server) {
|
||||
@@ -123,13 +121,26 @@ class Response extends Common {
|
||||
}
|
||||
|
||||
DslProperty value(Pattern server) {
|
||||
return value(new RegexProperty(server))
|
||||
}
|
||||
|
||||
DslProperty value(RegexProperty server) {
|
||||
return value(new ServerDslProperty(server))
|
||||
}
|
||||
|
||||
DslProperty $(Pattern server) {
|
||||
DslProperty $(RegexProperty server) {
|
||||
return value(server)
|
||||
}
|
||||
|
||||
DslProperty $(Pattern server) {
|
||||
return value(new RegexProperty(server))
|
||||
}
|
||||
|
||||
@Override
|
||||
RegexProperty regexProperty(Object object) {
|
||||
return new RegexProperty(object).concreteClientDynamicProducer()
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated in favor of bodyMatchers to support other future bodyMatchers too
|
||||
*/
|
||||
@@ -151,7 +162,7 @@ class Response extends Common {
|
||||
|
||||
@Override
|
||||
DslProperty value(ClientDslProperty client, ServerDslProperty server) {
|
||||
if (client.clientValue instanceof Pattern) {
|
||||
if (client.clientValue instanceof RegexProperty) {
|
||||
throw new IllegalStateException("You can't have a regular expression for the response on the client side")
|
||||
}
|
||||
return super.value(client, server)
|
||||
@@ -159,7 +170,7 @@ class Response extends Common {
|
||||
|
||||
@Override
|
||||
DslProperty value(ServerDslProperty server, ClientDslProperty client) {
|
||||
if (client.clientValue instanceof Pattern) {
|
||||
if (client.clientValue instanceof RegexProperty) {
|
||||
throw new IllegalStateException("You can't have a regular expression for the response on the client side")
|
||||
}
|
||||
return super.value(server, client)
|
||||
|
||||
@@ -16,16 +16,12 @@
|
||||
|
||||
package org.springframework.cloud.contract.spec.internal
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.ToString
|
||||
import org.codehaus.groovy.runtime.GStringImpl
|
||||
import repackaged.nl.flotsam.xeger.Xeger
|
||||
|
||||
import static org.springframework.cloud.contract.spec.util.ValidateUtils.validateServerValueIsAvailable
|
||||
|
||||
/**
|
||||
* Represents a URL that may contain query parameters
|
||||
*
|
||||
@@ -50,13 +46,13 @@ class Url extends DslProperty {
|
||||
|
||||
private static Object testUrl(Object url) {
|
||||
if (url instanceof GString) {
|
||||
boolean anyPattern = url.values.any { it instanceof Pattern }
|
||||
boolean anyPattern = url.values.any { it instanceof RegexProperty }
|
||||
if (!anyPattern) {
|
||||
return url
|
||||
}
|
||||
String newUrl = new GStringImpl(
|
||||
url.values.collect { it instanceof Pattern ?
|
||||
new Xeger(it.pattern()).generate() : it
|
||||
url.values.collect { it instanceof RegexProperty ?
|
||||
it.generate() : it
|
||||
} as String[],
|
||||
Arrays.copyOf(url.strings, url.strings.length) as String[]
|
||||
).toString()
|
||||
|
||||
@@ -14,7 +14,84 @@ class InputSpec extends Specification {
|
||||
input.with {
|
||||
property = $(consumer(regex("[0-9]{5}")))
|
||||
}
|
||||
def value = Integer.valueOf(property.serverValue as String)
|
||||
def generatedValue = property.serverValue
|
||||
generatedValue instanceof String
|
||||
def value = Integer.valueOf(generatedValue as String)
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Double'() {
|
||||
given:
|
||||
Input input = new Input()
|
||||
DslProperty property
|
||||
when:
|
||||
input.with {
|
||||
property = $(consumer(regex("[0-9]{5}").asDouble()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Double
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Short'() {
|
||||
given:
|
||||
Input input = new Input()
|
||||
DslProperty property
|
||||
when:
|
||||
input.with {
|
||||
property = $(consumer(regex("[0-9]{1}").asShort()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Short
|
||||
then:
|
||||
value >= 0
|
||||
value <= 9
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Long'() {
|
||||
given:
|
||||
Input input = new Input()
|
||||
DslProperty property
|
||||
when:
|
||||
input.with {
|
||||
property = $(consumer(regex("[0-9]{5}").asLong()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Long
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Integer'() {
|
||||
given:
|
||||
Input input = new Input()
|
||||
DslProperty property
|
||||
when:
|
||||
input.with {
|
||||
property = $(consumer(regex("[0-9]{5}").asInteger()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Integer
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Float'() {
|
||||
given:
|
||||
Input input = new Input()
|
||||
DslProperty property
|
||||
when:
|
||||
input.with {
|
||||
property = $(consumer(regex("[0-9]{5}").asFloat()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Float
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
|
||||
@@ -8,13 +8,90 @@ class OutputMessageSpec extends Specification {
|
||||
|
||||
def 'should set property when using the $() convenience method'() {
|
||||
given:
|
||||
Input input = new Input()
|
||||
OutputMessage contract = new OutputMessage()
|
||||
DslProperty property
|
||||
when:
|
||||
input.with {
|
||||
property = $(consumer(regex("[0-9]{5}")))
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}")))
|
||||
}
|
||||
def value = Integer.valueOf(property.serverValue as String)
|
||||
def generatedValue = property.clientValue
|
||||
generatedValue instanceof String
|
||||
def value = Integer.valueOf(generatedValue as String)
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Double'() {
|
||||
given:
|
||||
OutputMessage contract = new OutputMessage()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}").asDouble()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Double
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Short'() {
|
||||
given:
|
||||
OutputMessage contract = new OutputMessage()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{1}").asShort()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Short
|
||||
then:
|
||||
value >= 0
|
||||
value <= 9
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Long'() {
|
||||
given:
|
||||
OutputMessage contract = new OutputMessage()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}").asLong()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Long
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Integer'() {
|
||||
given:
|
||||
OutputMessage contract = new OutputMessage()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}").asInteger()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Integer
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Float'() {
|
||||
given:
|
||||
OutputMessage contract = new OutputMessage()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}").asFloat()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Float
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
|
||||
@@ -8,30 +8,106 @@ class RequestSpec extends Specification {
|
||||
|
||||
def 'should throw exception when on request side a value contains regex for server'() {
|
||||
given:
|
||||
Request request = new Request()
|
||||
Request contract = new Request()
|
||||
when:
|
||||
request.with {
|
||||
contract.with {
|
||||
value(consumer("foo"), producer(regex("foo")))
|
||||
}
|
||||
then:
|
||||
thrown(IllegalStateException)
|
||||
when:
|
||||
request.with {
|
||||
contract.with {
|
||||
value(producer(regex("foo")), consumer("foo"))
|
||||
}
|
||||
then:
|
||||
thrown(IllegalStateException)
|
||||
}
|
||||
|
||||
def 'should generate a value if only regex is passed for client'() {
|
||||
def 'should set property when using the $() convenience method'() {
|
||||
given:
|
||||
Request request = new Request()
|
||||
Request contract = new Request()
|
||||
DslProperty property
|
||||
when:
|
||||
request.with {
|
||||
property = value(consumer(regex("[0-9]{5}")))
|
||||
contract.with {
|
||||
property = $(consumer(regex("[0-9]{5}")))
|
||||
}
|
||||
def value = Integer.valueOf(property.serverValue as String)
|
||||
def generatedValue = property.serverValue
|
||||
generatedValue instanceof String
|
||||
def value = Integer.valueOf(generatedValue as String)
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Double'() {
|
||||
given:
|
||||
Request contract = new Request()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(consumer(regex("[0-9]{5}").asDouble()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Double
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Short'() {
|
||||
given:
|
||||
Request contract = new Request()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(consumer(regex("[0-9]{1}").asShort()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Short
|
||||
then:
|
||||
value >= 0
|
||||
value <= 9
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Long'() {
|
||||
given:
|
||||
Request contract = new Request()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(consumer(regex("[0-9]{5}").asLong()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Long
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Integer'() {
|
||||
given:
|
||||
Request contract = new Request()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(consumer(regex("[0-9]{5}").asInteger()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Integer
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Float'() {
|
||||
given:
|
||||
Request contract = new Request()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(consumer(regex("[0-9]{5}").asFloat()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Float
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package org.springframework.cloud.contract.spec.internal
|
||||
|
||||
import spock.lang.Specification
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import spock.lang.Specification
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@@ -26,18 +23,95 @@ class ResponseSpec extends Specification {
|
||||
thrown(IllegalStateException)
|
||||
}
|
||||
|
||||
def 'should generate a value if only regex is passed for server'() {
|
||||
|
||||
def 'should set property when using the $() convenience method'() {
|
||||
given:
|
||||
Response request = new Response()
|
||||
Response contract = new Response()
|
||||
DslProperty property
|
||||
when:
|
||||
request.with {
|
||||
property = value(producer(regex("[0-9]{5}")))
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}")))
|
||||
}
|
||||
def value = Integer.valueOf(property.clientValue as String)
|
||||
def generatedValue = property.clientValue
|
||||
generatedValue instanceof String
|
||||
def value = Integer.valueOf(generatedValue as String)
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Double'() {
|
||||
given:
|
||||
Response contract = new Response()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}").asDouble()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Double
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Short'() {
|
||||
given:
|
||||
Response contract = new Response()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{1}").asShort()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Short
|
||||
then:
|
||||
value >= 0
|
||||
value <= 9
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Long'() {
|
||||
given:
|
||||
Response contract = new Response()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}").asLong()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Long
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Integer'() {
|
||||
given:
|
||||
Response contract = new Response()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}").asInteger()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Integer
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Float'() {
|
||||
given:
|
||||
Response contract = new Response()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}").asFloat()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Float
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
(property.serverValue as Pattern).pattern() == '[0-9]{5}'
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user