Hello,
My API will return an object with property allParentGroups that will return a json array of string with 0 or more elements.
I couldnt find any ServerDslProperties to match arrays, so I tried using regex to match for the array (probably stupid already).
My regex (\[(("[^"]*",)*("[^"]*"))*]) seems to match only the type or array that I want, so I tried putting that in the contract DSL.
I tried putting that into the contract like this :
package contracts
import org.springframework.cloud.contract.spec.Contract
String stringArrayMayBeEmptyRegex = '(\\[(("[^"]*",)*("[^"]*"))*])'
Contract.make {
request {
method 'GET'
url '/api/product-groups'
headers {
accept('application/json;charset=UTF-8')
}
}
response {
status 200
body(
/*
private String code;
private String description;
private String parentCode;
private List<String> allParentGroups = new ArrayList<>();
*/
content:[[
"code" : value($(anyNonBlankString())),
"description" : value($(anyNonBlankString())),
"parentCode" : $(optional(anyNonBlankString())),
"allParentGroups": $(regex(stringArrayMayBeEmptyRegex))
]]
)
headers {
contentType('application/json;charset=UTF-8')
}
}
}
This gives me the following error during generateTests :
[ERROR] Exception occurred while trying to evaluate the contract at path [/home/***hidden for company purposes***src/test/resources/contracts/ProductGroupContract.groovy]
java.lang.IllegalArgumentException: expected '"' at position 28
at dk.brics.automaton.RegExp.parseSimpleExp(Unknown Source)
at dk.brics.automaton.RegExp.parseCharClassExp(Unknown Source)
at dk.brics.automaton.RegExp.parseComplExp(Unknown Source)
at dk.brics.automaton.RegExp.parseRepeatExp(Unknown Source)
at dk.brics.automaton.RegExp.parseConcatExp(Unknown Source)
at dk.brics.automaton.RegExp.parseConcatExp(Unknown Source)
at dk.brics.automaton.RegExp.parseConcatExp(Unknown Source)
at dk.brics.automaton.RegExp.parseConcatExp(Unknown Source)
at dk.brics.automaton.RegExp.parseConcatExp(Unknown Source)
at dk.brics.automaton.RegExp.parseInterExp(Unknown Source)
at dk.brics.automaton.RegExp.parseUnionExp(Unknown Source)
at dk.brics.automaton.RegExp.parseSimpleExp(Unknown Source)
at dk.brics.automaton.RegExp.parseCharClassExp(Unknown Source)
at dk.brics.automaton.RegExp.parseComplExp(Unknown Source)
at dk.brics.automaton.RegExp.parseRepeatExp(Unknown Source)
at dk.brics.automaton.RegExp.parseConcatExp(Unknown Source)
at dk.brics.automaton.RegExp.parseInterExp(Unknown Source)
at dk.brics.automaton.RegExp.parseUnionExp(Unknown Source)
at dk.brics.automaton.RegExp.parseSimpleExp(Unknown Source)
at dk.brics.automaton.RegExp.parseCharClassExp(Unknown Source)
at dk.brics.automaton.RegExp.parseComplExp(Unknown Source)
at dk.brics.automaton.RegExp.parseRepeatExp(Unknown Source)
at dk.brics.automaton.RegExp.parseConcatExp(Unknown Source)
at dk.brics.automaton.RegExp.parseConcatExp(Unknown Source)
at dk.brics.automaton.RegExp.parseInterExp(Unknown Source)
at dk.brics.automaton.RegExp.parseUnionExp(Unknown Source)
at dk.brics.automaton.RegExp.parseSimpleExp(Unknown Source)
at dk.brics.automaton.RegExp.parseCharClassExp(Unknown Source)
at dk.brics.automaton.RegExp.parseComplExp(Unknown Source)
at dk.brics.automaton.RegExp.parseRepeatExp(Unknown Source)
at dk.brics.automaton.RegExp.parseConcatExp(Unknown Source)
at dk.brics.automaton.RegExp.parseInterExp(Unknown Source)
at dk.brics.automaton.RegExp.parseUnionExp(Unknown Source)
at dk.brics.automaton.RegExp.<init>(Unknown Source)
at dk.brics.automaton.RegExp.<init>(Unknown Source)
at repackaged.nl.flotsam.xeger.Xeger.<init>(Xeger.java:50)
at repackaged.nl.flotsam.xeger.Xeger.<init>(Xeger.java:61)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:83)
at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:105)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:247)
at org.springframework.cloud.contract.spec.internal.Response.value(Response.groovy:96)
at org.springframework.cloud.contract.spec.internal.Response$value.callCurrent(Unknown Source)
at org.springframework.cloud.contract.spec.internal.Response.value(Response.groovy:108)
at org.springframework.cloud.contract.spec.internal.Response.$(Response.groovy:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:384)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1027)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:69)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:52)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:154)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:166)
at contracts.ProductGroupContract$_run_closure1$_closure3.doCall(ProductGroupContract.groovy:28)
at contracts.ProductGroupContract$_run_closure1$_closure3.doCall(ProductGroupContract.groovy)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1027)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:42)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:57)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:117)
at org.springframework.cloud.contract.spec.Contract.response(Contract.groovy:120)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:384)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1027)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:69)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:52)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:154)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:166)
at contracts.ProductGroupContract$_run_closure1.doCall(ProductGroupContract.groovy:15)
at contracts.ProductGroupContract$_run_closure1.doCall(ProductGroupContract.groovy)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1027)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:42)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:57)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:117)
at org.springframework.cloud.contract.spec.Contract.make(Contract.groovy:91)
at org.springframework.cloud.contract.spec.Contract$make.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
at contracts.ProductGroupContract.run(ProductGroupContract.groovy:7)
at groovy.lang.GroovyShell.evaluate(GroovyShell.java:585)
at groovy.lang.GroovyShell.evaluate(GroovyShell.java:632)
at org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter.convertAsCollection(ContractVerifierDslConverter.groovy:69)
at org.springframework.cloud.contract.verifier.file.ContractFileScanner$_appendRecursively_closure2.doCall(ContractFileScanner.groovy:100)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1027)
at groovy.lang.Closure.call(Closure.java:414)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.eachWithIndex(DefaultGroovyMethods.java:1910)
at org.springframework.cloud.contract.verifier.file.ContractFileScanner.appendRecursively(ContractFileScanner.groovy:94)
at org.springframework.cloud.contract.verifier.file.ContractFileScanner.findContracts(ContractFileScanner.groovy:77)
at org.springframework.cloud.contract.verifier.file.ContractFileScanner$findContracts.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:117)
at org.springframework.cloud.contract.verifier.TestGenerator.generateTestClasses(TestGenerator.groovy:82)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:210)
at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:59)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:52)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:154)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:166)
at org.springframework.cloud.contract.verifier.TestGenerator.generate(TestGenerator.groovy:76)
at org.springframework.cloud.contract.maven.verifier.GenerateTestsMojo.execute(GenerateTestsMojo.java:221)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:134)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:207)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:128)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:307)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:193)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:106)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:863)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:288)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:199)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
at org.codehaus.classworlds.Launcher.main(Launcher.java:47)
even though this regex compiles just fine on online test websites.
I would like to not use regex in the first place, is there another approach I can take ? And if not, why can't we compile the above relatively simple regex ?
I'm using 1.1.0.RELEASE
I also tried with String stringArrayMayBeEmptyRegex = '^\\[(?:(?:\"[^\"]*\",)*\"[^\"]*\")?\\]\$' to represent ^\[(?:(?:"[^"]*",)*"[^"]*")?\]$ and that has the same error
To begin with this value($(..)) makes no sense. value(...) == $(...), so you should use only one of them.
Second of all if I understand correctly, what you're trying to do is to apply a regex for an array but you can't do that in Spring Cloud Contract. What you can do is to go the docs and read this section - https://cloud.spring.io/spring-cloud-contract/multi/multi__contract_dsl.html#_dynamic_properties_in_the_matchers_sections - where I describe how you can use the matchers and byType comparison. Notice these examples:
jsonPath('$.valueWithMin', byType {
// results in verification of size of array (min 1)
minOccurrence(1)
})
jsonPath('$.valueWithMax', byType {
// results in verification of size of array (max 3)
maxOccurrence(3)
})
jsonPath('$.valueWithMinMax', byType {
// results in verification of size of array (min 1 & max 3)
minOccurrence(1)
maxOccurrence(3)
})
jsonPath('$.valueWithMinEmpty', byType {
// results in verification of size of array (min 0)
minOccurrence(0)
})
jsonPath('$.valueWithMaxEmpty', byType {
// results in verification of size of array (max 0)
maxOccurrence(0)
})
Ping @boyenn ?
Didn't get a notification for your last message, I'll check it out later today and get back to you!
@marcingrzejszczak the issue we had concerning this is thrown back on our backlog, I probably won't get back around to this for a while, so I'll close this issue and re-open it if I can pick up this ticket again.
Thank you for your help :+1: