diff --git a/spring-cloud-contract.html b/spring-cloud-contract.html
index 4c209096e9..32501dfbd4 100644
--- a/spring-cloud-contract.html
+++ b/spring-cloud-contract.html
@@ -555,28 +555,12 @@ $(addBlockSwitches);
-
Pluggable architecture
-
-
-Links
+Converters
+Stub converters
+Example of a custom HTTP Server Stub
+Example of a custom Stub Downloader Provider
@@ -1265,58 +1249,7 @@ all the contract are present in the producer’s repository
Maven
-
<repositories>
- <repository>
- <id>spring-snapshots</id>
- <name>Spring Snapshots</name>
- <url>https://repo.spring.io/snapshot</url>
- <snapshots>
- <enabled>true</enabled>
- </snapshots>
- </repository>
- <repository>
- <id>spring-milestones</id>
- <name>Spring Milestones</name>
- <url>https://repo.spring.io/milestone</url>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- </repository>
- <repository>
- <id>spring-releases</id>
- <name>Spring Releases</name>
- <url>https://repo.spring.io/release</url>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- </repository>
-</repositories>
-<pluginRepositories>
- <pluginRepository>
- <id>spring-snapshots</id>
- <name>Spring Snapshots</name>
- <url>https://repo.spring.io/snapshot</url>
- <snapshots>
- <enabled>true</enabled>
- </snapshots>
- </pluginRepository>
- <pluginRepository>
- <id>spring-milestones</id>
- <name>Spring Milestones</name>
- <url>https://repo.spring.io/milestone</url>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- </pluginRepository>
- <pluginRepository>
- <id>spring-releases</id>
- <name>Spring Releases</name>
- <url>https://repo.spring.io/release</url>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- </pluginRepository>
-</pluginRepositories>
+
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/1.0.x/samples/standalone/dsl/http-server/pom.xml[tags=repos,indent=0]
@@ -1661,11 +1594,7 @@ It’s really important that you understand the map notation to set up contr
-
@RunWith(SpringRunner.class)
-@SpringBootTest(webEnvironment=WebEnvironment.NONE)
-@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, workOffline = true)
-@DirtiesContext
-public class LoanApplicationServiceTests {
+
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/1.0.x/samples/standalone/dsl/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java[tags=autoconfigure_stubrunner,indent=0]
@@ -1714,7 +1643,7 @@ public class LoanApplicationServiceTests {
consumes = FRAUD_SERVICE_JSON_VERSION_1,
produces = FRAUD_SERVICE_JSON_VERSION_1)
public FraudCheckResult fraudCheck(@RequestBody FraudCheck fraudCheck) {
-return new FraudCheckResult(FraudCheckStatus.OK, NO_REASON);
+Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/1.0.x/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudDetectionController.java[tags=initial_impl,indent=0]
}
@@ -1837,7 +1766,9 @@ public void validate_shouldMarkClientAsFraud() throws Exception {
consumes = FRAUD_SERVICE_JSON_VERSION_1,
produces = FRAUD_SERVICE_JSON_VERSION_1)
public FraudCheckResult fraudCheck(@RequestBody FraudCheck fraudCheck) {
-Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/1.0.x/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudDetectionController.java[tags=new_impl,indent=0]
+if (amountGreaterThanThreshold(fraudCheck)) {
+ return new FraudCheckResult(FraudCheckStatus.FRAUD, AMOUNT_TOO_HIGH);
+}
return new FraudCheckResult(FraudCheckStatus.OK, NO_REASON);
}
@@ -7300,28 +7231,7 @@ Now you have to add the dependency for the plugin to reuse at runtime.
-
<plugin>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-contract-maven-plugin</artifactId>
- <version>${spring-cloud-contract.version}</version>
- <extensions>true</extensions>
- <configuration>
- <packageWithBaseClasses>com.example</packageWithBaseClasses>
- </configuration>
- <dependencies>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-contract-verifier</artifactId>
- <version>${spring-cloud-contract.version}</version>
- </dependency>
- <dependency>
- <groupId>com.example</groupId>
- <artifactId>beer-common</artifactId>
- <version>${project.version}</version>
- <scope>compile</scope>
- </dependency>
- </dependencies>
-</plugin>
+
Unresolved directive in verifier/contract.adoc - include::https://raw.githubusercontent.com/spring-cloud-samples/spring-cloud-contract-samples/master/producer/pom.xml[tags=test_dep_in_plugin,indent=0]
@@ -7342,29 +7252,71 @@ Now you can reference your classes in your DSL. Example:
-
Unresolved directive in verifier/contract.adoc - include::https://raw.githubusercontent.com/spring-cloud-samples/spring-cloud-contract-samples/master/producer/src/test/resources/contracts/beer/rest/shouldGrantABeerIfOldEnough.groovy[indent=0]
+
package contracts.beer.rest
-
-
-
-
-
Pluggable architecture
-
There are cases where you have your contracts defined in other formats
-like YAML, RAML or PACT. On the other hand you’d like to profit from
-the test and stubs generation. It’s really easy to add your own implementation
-of either of those. Also you can customize the way tests are generated (for example you can generate
-tests for other languages) and you can do the same for stubs generation (you can generate
-stubs for other stub http server implementations).
+
import org.springframework.cloud.contract.spec.Contract
-
-
Custom contract converter
-
Let’s assume that your contract is written in a YAML file like this:
+
import static com.example.ConsumerUtils.oldEnough
+import static com.example.ProducerUtils.ok
+
+
+
Contract.make {
+ request {
+ description("""
+Represents a successful scenario of getting a beer
+
+
+
given:
+ client is old enough
+when:
+ he applies for a beer
+then:
+ we’ll grant him the beer
+""")
+ method 'POST'
+ url '/check'
+ body(
+ age: $(oldEnough())
+ )
+ headers {
+ contentType(applicationJson())
+ }
+ }
+ response {
+ status 200
+ body("""
+ {
+ "status": "${value(ok())}"
+ }
+ """)
+ headers {
+ contentType(applicationJson())
+ }
+ }
+}
-
request:
+=== Pluggable architecture
+
+There are cases where you have your contracts defined in other formats
+like YAML, RAML or PACT. On the other hand you'd like to profit from
+the test and stubs generation. It's really easy to add your own implementation
+of either of those. Also you can customize the way tests are generated (for example you can generate
+tests for other languages) and you can do the same for stubs generation (you can generate
+stubs for other stub http server implementations).
+
+==== Custom contract converter
+
+Let's assume that your contract is written in a YAML file like this:
+
+[source,yml]
+
+
+
+
request:
url: /foo
method: PUT
headers:
@@ -7376,17 +7328,20 @@ response:
headers:
foo2: bar
body:
- foo2: bar
-
-
-
-
Thanks to the interface
+ foo2: bar
-
package org.springframework.cloud.contract.spec
+Thanks to the interface
-/**
+[source,groovy]
+
+
+
+
package org.springframework.cloud.contract.spec
+
+
+
/**
* Converter to be used to convert FROM {@link File} TO {@link Contract}
* and from {@link Contract} to {@code T}
*
@@ -7395,120 +7350,137 @@ response:
* @author Marcin Grzejszczak
* @since 1.1.0
*/
-interface ContractConverter<T> {
-
- /**
- * Should this file be accepted by the converter. Can use the file extension
- * to check if the conversion is possible.
- *
- * @param file - file to be considered for conversion
- * @return - {@code true} if the given implementation can convert the file
- */
- boolean isAccepted(File file)
-
- /**
- * Converts the given {@link File} to its {@link Contract} representation
- *
- * @param file - file to convert
- * @return - {@link Contract} representation of the file
- */
- Collection<Contract> convertFrom(File file)
-
- /**
+interface ContractConverter<T> {
+
+
+
+
/**
+ * Should this file be accepted by the converter. Can use the file extension
+ * to check if the conversion is possible.
+ *
+ * @param file - file to be considered for conversion
+ * @return - {@code true} if the given implementation can convert the file
+ */
+boolean isAccepted(File file)
+
+
+
+
+
/**
+ * Converts the given {@link File} to its {@link Contract} representation
+ *
+ * @param file - file to convert
+ * @return - {@link Contract} representation of the file
+ */
+Collection<Contract> convertFrom(File file)
+
+
+
+
+
/**
* Converts the given {@link Contract} to a {@link T} representation
*
* @param contract - the parsed contract
* @return - {@link T} the type to which we do the conversion
*/
T convertTo(Collection<Contract> contract)
-}
+}
-
-
you can register your own implementation of a contract structure converter.
+
+
+
you can register your own implementation of a contract structure converter.
Your implementation needs to state the condition on which it should start the
-conversion. Also you have to define how to perform that conversion in both ways.
-
-
-
-
-|
- Important
- |
-
-Once you create your implementation you have to create a /META-INF/spring.factories
-file in which you provide the fully qualified name of your implementation.
- |
-
-
-
-
-
Example of a spring.factories file
-
-
-
-
# Converters
-org.springframework.cloud.contract.spec.ContractConverter=\
-org.springframework.cloud.contract.verifier.converter.YamlContractConverter
-
-
-
-
and the YAML implementation
-
-
-
-
package org.springframework.cloud.contract.verifier.converter
+conversion. Also you have to define how to perform that conversion in both ways.
-import groovy.transform.CompileStatic
+IMPORTANT: Once you create your implementation you have to create a `/META-INF/spring.factories`
+file in which you provide the fully qualified name of your implementation.
+
+Example of a `spring.factories` file
+
+[source]
+
+
+
+
+
+
+
+Converters
+
+
org.springframework.cloud.contract.spec.ContractConverter=\
+org.springframework.cloud.contract.verifier.converter.YamlContractConverter
+
+
+
+
and the YAML implementation
+
+[source,groovy]
+
+
+
+
package org.springframework.cloud.contract.verifier.converter
+
+
+
import groovy.transform.CompileStatic
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.ContractConverter
import org.springframework.cloud.contract.spec.internal.Headers
-import org.yaml.snakeyaml.Yaml
-
-/**
+import org.yaml.snakeyaml.Yaml
+
+
+
/**
* Simple converter from and to a {@link YamlContract} to a collection of {@link Contract}
*/
@CompileStatic
-class YamlContractConverter implements ContractConverter<List<YamlContract>> {
-
- @Override
- public boolean isAccepted(File file) {
- String name = file.getName()
- return name.endsWith(".yml") || name.endsWith(".yaml")
- }
-
- @Override
- public Collection<Contract> convertFrom(File file) {
- try {
- YamlContract yamlContract = new Yaml().loadAs(new FileInputStream(file), YamlContract.class)
- return [Contract.make {
- request {
- method(yamlContract?.request?.method)
- url(yamlContract?.request?.url)
- headers {
- yamlContract?.request?.headers?.each { String key, Object value ->
- header(key, value)
- }
+class YamlContractConverter implements ContractConverter<List<YamlContract>> {
+
+
+
+
@Override
+public boolean isAccepted(File file) {
+ String name = file.getName()
+ return name.endsWith(".yml") || name.endsWith(".yaml")
+}
+
+
+
+
+
@Override
+public Collection<Contract> convertFrom(File file) {
+ try {
+ YamlContract yamlContract = new Yaml().loadAs(new FileInputStream(file), YamlContract.class)
+ return [Contract.make {
+ request {
+ method(yamlContract?.request?.method)
+ url(yamlContract?.request?.url)
+ headers {
+ yamlContract?.request?.headers?.each { String key, Object value ->
+ header(key, value)
}
- body(yamlContract?.request?.body)
}
- response {
- status(yamlContract?.response?.status)
- headers {
- yamlContract?.response?.headers?.each { String key, Object value ->
- header(key, value)
- }
+ body(yamlContract?.request?.body)
+ }
+ response {
+ status(yamlContract?.response?.status)
+ headers {
+ yamlContract?.response?.headers?.each { String key, Object value ->
+ header(key, value)
}
- body(yamlContract?.response?.body)
}
- }]
- }
- catch (FileNotFoundException e) {
- throw new IllegalStateException(e)
- }
+ body(yamlContract?.response?.body)
+ }
+ }]
}
-
- @Override
+ catch (FileNotFoundException e) {
+ throw new IllegalStateException(e)
+ }
+}
+
+
+
+
+
@Override
public List<YamlContract> convertTo(Collection<Contract> contracts) {
return contracts.collect { Contract contract ->
YamlContract yamlContract = new YamlContract()
@@ -7526,25 +7498,27 @@ class YamlContractConverter implements ContractConverter<List<YamlContract
return yamlContract
}
}
-}
+}
-
-
Pact converter
-
-
Spring Cloud Contract comes with an out of the box support for Pact representation of contracts.
-In other words instead of using the Groovy DSL you can use Pact files. In this section
-we will present how to add such a support for your project.
-
-
-
Pact contract
-
-
We will be working on the following example of a Pact contract. We’ve placed this file under
-the src/test/resources/contracts folder.
-
-
{
+===== Pact converter
+
+Spring Cloud Contract comes with an out of the box support for https://docs.pact.io/[Pact] representation of contracts.
+In other words instead of using the Groovy DSL you can use Pact files. In this section
+we will present how to add such a support for your project.
+
+====== Pact contract
+
+We will be working on the following example of a Pact contract. We've placed this file under
+the `src/test/resources/contracts` folder.
+
+[source,javascript,indent=0]
+
+
+
+
{
"provider": {
"name": "Provider"
},
@@ -7597,21 +7571,22 @@ the src/test/resources/contracts folder.
"version": "2.4.18"
}
}
-}
+}
-
-
-
-
Pact for producers
-
-
On the producer side you have add to your plugin configuration two additional dependencies.
-One is the Spring Cloud Contract Pact support and the other represents the current
-Pact version that you’re using.
-
-
-
Maven
+
-
<plugin>
+====== Pact for producers
+
+On the producer side you have add to your plugin configuration two additional dependencies.
+One is the Spring Cloud Contract Pact support and the other represents the current
+Pact version that you're using.
+
+[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
+.Maven
+
+
+
+
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>${spring-cloud-contract.version}</version>
@@ -7631,33 +7606,45 @@ Pact version that you’re using.
<version>2.4.18</version>
</dependency>
</dependencies>
-</plugin>
-
-
-
-
Gradle
-
-
classpath "org.springframework.cloud:spring-cloud-contract-spec-pact:${findProperty('verifierVersion') ?: verifierVersion}"
-classpath 'au.com.dius:pact-jvm-model:2.4.18'
-
-
-
-
When you execute the build of your application a test, looking more or less like this, will be generated
+</plugin>
-
@Test
+[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
+.Gradle
+
+
+
+
classpath "org.springframework.cloud:spring-cloud-contract-spec-pact:${findProperty('verifierVersion') ?: verifierVersion}"
+classpath 'au.com.dius:pact-jvm-model:2.4.18'
+
+
+
+
When you execute the build of your application a test, looking more or less like this, will be generated
+
+[source,java,indent=0]
+
+
+
+
+
@Test
public void validate_shouldMarkClientAsFraud() throws Exception {
// given:
MockMvcRequestSpecification request = given()
.header("Content-Type", "application/vnd.fraud.v1+json")
- .body("{\"clientId\":\"1234567890\",\"loanAmount\":99999}");
-
- // when:
- ResponseOptions response = given().spec(request)
- .put("/fraudcheck");
-
- // then:
+ .body("{\"clientId\":\"1234567890\",\"loanAmount\":99999}");
+
+
+
+
+
// when:
+ ResponseOptions response = given().spec(request)
+ .put("/fraudcheck");
+
+
+
+
+
// then:
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.header("Content-Type")).isEqualTo("application/vnd.fraud.v1+json;charset=UTF-8");
// and:
@@ -7665,15 +7652,18 @@ public void validate_shouldMarkClientAsFraud() throws Exception {
assertThatJson(parsedJson).field("rejectionReason").isEqualTo("Amount too high");
// and:
assertThat(parsedJson.read("$.fraudCheckStatus", String.class)).matches("FRAUD");
-}
+}
-
-
and the stub looking like this
-
-
{
+and the stub looking like this
+
+[source,javascript,indent=0]
+
+
+
+
{
"uuid" : "996ae5ae-6834-4db6-8fac-358ca187ab62",
"request" : {
"url" : "/fraudcheck",
@@ -7696,21 +7686,22 @@ public void validate_shouldMarkClientAsFraud() throws Exception {
"Content-Type" : "application/vnd.fraud.v1+json;charset=UTF-8"
}
}
-}
+}
-
-
-
-
Pact for consumers
-
-
On the producer side you have add to your project dependencies two additional dependencies.
-One is the Spring Cloud Contract Pact support and the other represents the current
-Pact version that you’re using.
-
-
-
Maven
+
-
<dependency>
+====== Pact for consumers
+
+On the producer side you have add to your project dependencies two additional dependencies.
+One is the Spring Cloud Contract Pact support and the other represents the current
+Pact version that you're using.
+
+[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
+.Maven
+
+
+
+
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-spec-pact</artifactId>
<scope>test</scope>
@@ -7720,46 +7711,51 @@ Pact version that you’re using.
<artifactId>pact-jvm-model</artifactId>
<version>2.4.18</version>
<scope>test</scope>
-</dependency>
-
-
-
-
Gradle
-
-
testCompile "org.springframework.cloud:spring-cloud-contract-spec-pact"
-testCompile 'au.com.dius:pact-jvm-model:2.4.18'
-
-
-
-
-
-
-
Custom test generator
-
-
If you want to generate tests for different languages than Java or you’re
-not happy with the way we’re building Java tests for you then you can register
-your own implementation to do that.
-
-
-
Thanks to the interface
+</dependency>
-
package org.springframework.cloud.contract.verifier.builder
+[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
+.Gradle
+
+
+
+
testCompile "org.springframework.cloud:spring-cloud-contract-spec-pact"
+testCompile 'au.com.dius:pact-jvm-model:2.4.18'
+
+
+
+
==== Custom test generator
-import groovy.transform.CompileStatic
+If you want to generate tests for different languages than Java or you're
+not happy with the way we're building Java tests for you then you can register
+your own implementation to do that.
+
+Thanks to the interface
+
+[source,groovy]
+
+
+
+
package org.springframework.cloud.contract.verifier.builder
+
+
+
import groovy.transform.CompileStatic
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
-import org.springframework.cloud.contract.verifier.file.ContractMetadata
-
-/**
+import org.springframework.cloud.contract.verifier.file.ContractMetadata
+
+
+
/**
* Builds a single test.
*
* @since 1.1.0
*/
@CompileStatic
-interface SingleTestGenerator {
-
- /**
+interface SingleTestGenerator {
+
+
+
+
/**
* Creates contents of a single test class in which all test scenarios from
* the contract metadata should be placed.
*
@@ -7772,54 +7768,68 @@ interface SingleTestGenerator {
*/
String buildClass(ContractVerifierConfigProperties properties, Collection<ContractMetadata> listOfFiles,
String className, String classPackage, String includedDirectoryRelativePath)
-}
+}
-
-
you can register your own implementation that generates a test. Again, it’s enough to provide
-a proper spring.factories file. Example:
-
-
org.springframework.cloud.contract.verifier.builder.SingleTestGenerator=/
-com.example.MyGenerator
-
-
-
-
-
Custom stub generator
-
-
If you want to generate stubs for other stub server than WireMock it’s enough to
- plug in your own implementation of this interface:
-
-
-
-
package org.springframework.cloud.contract.verifier.converter
+you can register your own implementation that generates a test. Again, it's enough to provide
+a proper `spring.factories` file. Example:
-import groovy.transform.CompileStatic
+[source]
+
+
+
+
org.springframework.cloud.contract.verifier.builder.SingleTestGenerator=/
+com.example.MyGenerator
+
+
+
+
==== Custom stub generator
+
+If you want to generate stubs for other stub server than WireMock it's enough to
+ plug in your own implementation of this interface:
+
+[source,groovy]
+
+
+
+
package org.springframework.cloud.contract.verifier.converter
+
+
+
import groovy.transform.CompileStatic
import org.springframework.cloud.contract.spec.Contract
-import org.springframework.cloud.contract.verifier.file.ContractMetadata
-
-/**
+import org.springframework.cloud.contract.verifier.file.ContractMetadata
+
+
+
/**
* Converts contracts into their stub representation.
*
* @since 1.1.0
*/
@CompileStatic
-interface StubGenerator {
-
- /**
- * Returns {@code true} if the converter can handle the file to convert it into a stub.
- */
- boolean canHandleFileName(String fileName)
-
- /**
- * Returns the collection of converted contracts into stubs. One contract can
- * result in multiple stubs.
- */
- Map<Contract, String> convertContents(String rootName, ContractMetadata content)
-
- /**
+interface StubGenerator {
+
+
+
+
/**
+ * Returns {@code true} if the converter can handle the file to convert it into a stub.
+ */
+boolean canHandleFileName(String fileName)
+
+
+
+
+
/**
+ * Returns the collection of converted contracts into stubs. One contract can
+ * result in multiple stubs.
+ */
+Map<Contract, String> convertContents(String rootName, ContractMetadata content)
+
+
+
+
+
/**
* Returns the name of the converted stub file. If you have multiple contracts
* in a single file then a prefix will be added to the generated file. If you
* provide the {@link Contract#name} field then that field will override the
@@ -7830,220 +7840,246 @@ interface StubGenerator {
* converter will create two files {@code 0_foo.json} and {@code 1_foo.json}
*/
String generateOutputFileNameForInput(String inputFileName)
-}
+}
-
-
you can register your own implementation that generate Stubs. Again, it’s enough to provide
-a proper spring.factories file. Example:
-
-
# Stub converters
-org.springframework.cloud.contract.verifier.converter.StubGenerator=\
-org.springframework.cloud.contract.verifier.wiremock.DslToWireMockClientConverter
-
-
-
-
The default implementation is the WireMock stub generation.
-
-
-
-
-|
- Tip
- |
-
-You can provide multiple stub generator implementations. That way for example from a single
-DSL as input you can e.g. produce WireMock stubs and Pact files too!
- |
-
-
-
-
-
-
Custom Stub Runner
-
-
If you decide to have a custom stub generation you also need a custom way of running
-stubs with your different stub provider.
-
-
-
Let us assume that you’re using Moco to build your stubs.
-You wrote a proper stub generator and your stubs got placed in a JAR file.
-
-
-
In order for Stub Runner to know how to run your stubs you have to define a custom
- HTTP Stub server implementation. It can look like this:
-
-
-
-
package org.springframework.cloud.contract.stubrunner.provider.moco
+you can register your own implementation that generate Stubs. Again, it's enough to provide
+a proper `spring.factories` file. Example:
-import com.github.dreamhead.moco.bootstrap.arg.HttpArgs
+[source]
+
+
+
Stub converters
+
+
org.springframework.cloud.contract.verifier.converter.StubGenerator=\
+org.springframework.cloud.contract.verifier.wiremock.DslToWireMockClientConverter
+
+
+
+
The default implementation is the WireMock stub generation.
+
+TIP: You can provide multiple stub generator implementations. That way for example from a single
+DSL as input you can e.g. produce WireMock stubs and Pact files too!
+
+==== Custom Stub Runner
+
+If you decide to have a custom stub generation you also need a custom way of running
+stubs with your different stub provider.
+
+Let us assume that you're using https://github.com/dreamhead/moco[Moco] to build your stubs.
+You wrote a proper stub generator and your stubs got placed in a JAR file.
+
+In order for Stub Runner to know how to run your stubs you have to define a custom
+ HTTP Stub server implementation. It can look like this:
+
+[source,groovy]
+
+
+
+
package org.springframework.cloud.contract.stubrunner.provider.moco
+
+
+
import com.github.dreamhead.moco.bootstrap.arg.HttpArgs
import com.github.dreamhead.moco.runner.JsonRunner
import org.springframework.cloud.contract.stubrunner.HttpServerStub
-import org.springframework.util.SocketUtils
-
-class MocoHttpServerStub implements HttpServerStub {
-
- private boolean started
- private JsonRunner runner
- private int port
-
- @Override
- int port() {
- if (!isRunning()) {
- return -1
- }
- return port
+import org.springframework.util.SocketUtils
+
+
+
class MocoHttpServerStub implements HttpServerStub {
+
+
+
+
private boolean started
+private JsonRunner runner
+private int port
+
+
+
+
+
@Override
+int port() {
+ if (!isRunning()) {
+ return -1
}
-
- @Override
- boolean isRunning() {
- return started
- }
-
- @Override
- HttpServerStub start() {
- return start(SocketUtils.findAvailableTcpPort())
- }
-
- @Override
- HttpServerStub start(int port) {
- this.port = port
+ return port
+}
+
+
+
+
+
@Override
+boolean isRunning() {
+ return started
+}
+
+
+
+
+
@Override
+HttpServerStub start() {
+ return start(SocketUtils.findAvailableTcpPort())
+}
+
+
+
+
+
@Override
+HttpServerStub start(int port) {
+ this.port = port
+ return this
+}
+
+
+
+
+
@Override
+HttpServerStub stop() {
+ if (!isRunning()) {
return this
}
-
- @Override
- HttpServerStub stop() {
- if (!isRunning()) {
- return this
- }
- this.runner.stop()
- return this
- }
-
- @Override
- HttpServerStub registerMappings(Collection<File> stubFiles) {
- List<InputStream> streams = stubFiles.collect { it.newInputStream() }
- this.runner = JsonRunner.newJsonRunnerWithStreams(streams,
- HttpArgs.httpArgs().withPort(this.port).build())
- this.runner.run()
- this.started = true
- return this
- }
-
- @Override
+ this.runner.stop()
+ return this
+}
+
+
+
+
+
@Override
+HttpServerStub registerMappings(Collection<File> stubFiles) {
+ List<InputStream> streams = stubFiles.collect { it.newInputStream() }
+ this.runner = JsonRunner.newJsonRunnerWithStreams(streams,
+ HttpArgs.httpArgs().withPort(this.port).build())
+ this.runner.run()
+ this.started = true
+ return this
+}
+
+
+
+
+
@Override
boolean isAccepted(File file) {
return file.name.endsWith(".json")
}
-}
+}
-
-
and just register it in your spring.factories file
-
-
# Example of a custom HTTP Server Stub
-org.springframework.cloud.contract.stubrunner.HttpServerStub=\
-org.springframework.cloud.contract.stubrunner.provider.moco.MocoHttpServerStub
-
-
-
-
that way you’ll be able to run stubs using Moco.
-
-
-
-
-|
- Important
- |
-
-If you don’t provide any implementation then the default one - WireMock based
-will be picked. If you provide more than one then the first one on the list will be picked.
- |
-
-
-
-
-
-
Custom Stub Downloader
-
-
You can customize the way your stubs are downloaded. If you don’t want to download the JARs
-from Nexus / Artifactory in the way we do by default you can set your own implementation.
-Below you can find an example of a Stub Downloader Provider that takes json files from the test resources
-from classpath, copies them to a temp file and then passes that temporary folder
- as a root for the stubs.
-
-
-
-
package org.springframework.cloud.contract.stubrunner.provider.moco
+and just register it in your `spring.factories` file
-import org.springframework.cloud.contract.stubrunner.StubConfiguration
+[source]
+
+
+
Example of a custom HTTP Server Stub
+
+
org.springframework.cloud.contract.stubrunner.HttpServerStub=\
+org.springframework.cloud.contract.stubrunner.provider.moco.MocoHttpServerStub
+
+
+
+
that way you'll be able to run stubs using Moco.
+
+IMPORTANT: If you don't provide any implementation then the default one - WireMock based
+will be picked. If you provide more than one then the first one on the list will be picked.
+
+==== Custom Stub Downloader
+
+You can customize the way your stubs are downloaded. If you don't want to download the JARs
+from Nexus / Artifactory in the way we do by default you can set your own implementation.
+Below you can find an example of a Stub Downloader Provider that takes `json` files from the test resources
+from classpath, copies them to a temp file and then passes that temporary folder
+ as a root for the stubs.
+
+[source,java]
+
+
+
+
package org.springframework.cloud.contract.stubrunner.provider.moco
+
+
+
import org.springframework.cloud.contract.stubrunner.StubConfiguration
import org.springframework.cloud.contract.stubrunner.StubDownloader
import org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder
import org.springframework.cloud.contract.stubrunner.StubRunnerOptions
import org.springframework.core.io.DefaultResourceLoader
import org.springframework.core.io.Resource
-import org.springframework.core.io.support.PathMatchingResourcePatternResolver
-
-import java.nio.file.Files
-
-/**
- * Poor man's version of taking stubs from classpath. It needs much more
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver
+
+
+
import java.nio.file.Files
+
+
+
/**
+ * Poor man’s version of taking stubs from classpath. It needs much more
* love and attention to go to the main sources.
*
* @author Marcin Grzejszczak
*/
-class ClasspathStubProvider implements StubDownloaderBuilder {
-
- private static final int TEMP_DIR_ATTEMPTS = 10000
-
- @Override
- public StubDownloader build(StubRunnerOptions stubRunnerOptions) {
- final StubConfiguration configuration = stubRunnerOptions.getDependencies().first()
- PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(
- new DefaultResourceLoader())
- try {
- String rootFolder = repoRoot(stubRunnerOptions) ?: "**/" + separatedArtifact(configuration) + "/**/*.json"
- Resource[] resources = resolver.getResources(rootFolder)
- final File tmp = createTempDir()
- tmp.deleteOnExit()
- // you'd have to write an impl to maintain the folder structure
- // this is just for demo
- resources.each { Resource resource ->
- Files.copy(resource.getInputStream(), new File(tmp, resource.getFile().getName()).toPath())
- }
- return new StubDownloader() {
- @Override
- public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
- StubConfiguration stubConfiguration) {
- return new AbstractMap.SimpleEntry(configuration, tmp)
- }
- }
- } catch (IOException e) {
- throw new IllegalStateException(e)
+class ClasspathStubProvider implements StubDownloaderBuilder {
+
+
+
+
private static final int TEMP_DIR_ATTEMPTS = 10000
+
+
+
+
+
@Override
+public StubDownloader build(StubRunnerOptions stubRunnerOptions) {
+ final StubConfiguration configuration = stubRunnerOptions.getDependencies().first()
+ PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(
+ new DefaultResourceLoader())
+ try {
+ String rootFolder = repoRoot(stubRunnerOptions) ?: "**/" + separatedArtifact(configuration) + "/**/*.json"
+ Resource[] resources = resolver.getResources(rootFolder)
+ final File tmp = createTempDir()
+ tmp.deleteOnExit()
+ // you'd have to write an impl to maintain the folder structure
+ // this is just for demo
+ resources.each { Resource resource ->
+ Files.copy(resource.getInputStream(), new File(tmp, resource.getFile().getName()).toPath())
}
- }
-
- private String repoRoot(StubRunnerOptions stubRunnerOptions) {
- switch (stubRunnerOptions.stubRepositoryRoot) {
- case { !it }:
- return ""
- case { String root -> root.endsWith("**/*.json") }:
- return stubRunnerOptions.stubRepositoryRoot
- default:
- return stubRunnerOptions.stubRepositoryRoot + "/**/*.json"
+ return new StubDownloader() {
+ @Override
+ public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
+ StubConfiguration stubConfiguration) {
+ return new AbstractMap.SimpleEntry(configuration, tmp)
+ }
}
+ } catch (IOException e) {
+ throw new IllegalStateException(e)
}
-
- private String separatedArtifact(StubConfiguration configuration) {
- return configuration.getGroupId().replace(".", File.separator) +
- File.separator + configuration.getArtifactId()
+}
+
+
+
+
+
private String repoRoot(StubRunnerOptions stubRunnerOptions) {
+ switch (stubRunnerOptions.stubRepositoryRoot) {
+ case { !it }:
+ return ""
+ case { String root -> root.endsWith("**/*.json") }:
+ return stubRunnerOptions.stubRepositoryRoot
+ default:
+ return stubRunnerOptions.stubRepositoryRoot + "/**/*.json"
}
-
- // Taken from Guava
+}
+
+
+
+
+
private String separatedArtifact(StubConfiguration configuration) {
+ return configuration.getGroupId().replace(".", File.separator) +
+ File.separator + configuration.getArtifactId()
+}
+
+
+
+
+
// Taken from Guava
private File createTempDir() {
File baseDir = new File(System.getProperty("java.io.tmpdir"))
String baseName = System.currentTimeMillis() + "-"
@@ -8057,71 +8093,40 @@ class ClasspathStubProvider implements StubDownloaderBuilder {
"Failed to create directory within " + TEMP_DIR_ATTEMPTS + " attempts (tried " + baseName + "0 to " + baseName + (
TEMP_DIR_ATTEMPTS - 1) + ")")
}
-}
+}
-
-
and just register it in your spring.factories file
-
-
# Example of a custom Stub Downloader Provider
-org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder=\
-org.springframework.cloud.contract.stubrunner.provider.moco.ClasspathStubProvider
+
and just register it in your `spring.factories` file
+
+[source]
+
Example of a custom Stub Downloader Provider
-
that way you’ll be able to pick a folder with the source of your stubs.
+
org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder=\
+org.springframework.cloud.contract.stubrunner.provider.moco.ClasspathStubProvider
-
-
-
-|
- Important
- |
-
-If you don’t provide any implementation then the default one - Aether based that will download stubs from a remote repo
+
+
+ that way you'll be able to pick a folder with the source of your stubs.
+
+IMPORTANT: If you don't provide any implementation then the default one - Aether based that will download stubs from a remote repo
will be picked. If you provide more than one then the first one on the list will be picked.
- |
-
-
-
-
-
-
-
Links
-
-
Here you can find interesting links related to Spring Cloud Contract Verifier:
-
-
-
+
+=== Links
+
+Here you can find interesting links related to Spring Cloud Contract Verifier:
+
+- https://github.com/spring-cloud/spring-cloud-contract/[Spring Cloud Contract Github Repository]
+- https://github.com/spring-cloud-samples/spring-cloud-contract-samples/[Spring Cloud Contract Samples]
+- https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html[Spring Cloud Contract Documentation]
+- https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html/deprecated[Accurest Legacy Documentation]
+- https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html/#spring-cloud-contract-stub-runner[Spring Cloud Contract Stub Runner Documentation]
+- https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html/#stub-runner-for-messaging[Spring Cloud Contract Stub Runner Messaging Documentation]
+- https://gitter.im/spring-cloud/spring-cloud-contract[Spring Cloud Contract Gitter]
+- https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract-maven-plugin/[Spring Cloud Contract Maven Plugin]