Consumer Contracts (#83)

With this functionality you can have one centralized repository containing all contracts. This repo will have to produce a JAR containing all contracts. The layout of the repository can be arbitrary but some sensible defaults are assumed. The producer will be able to then download that JAR and produce tests and stubs from it.

fixes #38
This commit is contained in:
Marcin Grzejszczak
2016-09-23 10:16:51 +02:00
committed by GitHub
parent 93782cb049
commit 01f4ad76be
50 changed files with 1995 additions and 168 deletions

View File

@@ -120,6 +120,9 @@ going through the process. CDC is all about communication.
The https://github.com/spring-cloud/spring-cloud-contract/tree/master/samples/standalone/dsl/http-server[server side code is available here] and https://github.com/spring-cloud/spring-cloud-contract/tree/master/samples/standalone/dsl/http-client[the client side code here].
TIP: In this case the ownership of the contracts lays on the producer side. It means that physically
all the contract are present in the producer's repository
===== Technical note
If using the *SNAPSHOT* / *Milestone* / *Release Candidate* versions please add the following section to your
@@ -720,4 +723,99 @@ Example of tests using production version of stubs
@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:prod-stubs:8080"})
----
You can pass those values also via properties from your deployment pipeline.
You can pass those values also via properties from your deployment pipeline.
===== Common repo with contracts
Another way of storing contracts other than having them with the producer is keeping them in a common place.
It can be related to security issues where the consumers can't clone the producer's code. Also if you keep
contracts in a single place then you, as a producer, will know how many consumers you have and which
consumer will you break with your local changes.
* Repo structure *
Let's assume that we have a producer with coordinates `com.example:server` and 3 consumers: `client1`,
`client2`, `client3`. Then in the repository with common contracts you would have the following setup
(which you can checkout https://github.com/spring-cloud/spring-cloud-contract/tree/master/samples/standalone/contracts[here]:
[source,bash,indent=0]
----
├── com
│   └── example
│   └── server
│   ├── client1
│   │   └── expectation.groovy
│   ├── client2
│   │   └── expectation.groovy
│   ├── client3
│   │   └── expectation.groovy
│   └── pom.xml
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
└── assembly
└── contracts.xml
----
As you can see the under the slash-delimited groupid `/` artifact id folder (`com/example/server`) you have
expectations of the 3 consumers (`client1`, `client2` and `client3`). Expectations are the standard Groovy DSL
contract files as described throughout this documentation. This repository has to produce a JAR file that maps
one to one to the contents of the repo.
Example of a `pom.xml` inside the `server` folder.
[source,xml,indent=0]
----
include::{introduction_url}/samples/standalone/contracts/com/example/server/pom.xml[indent=0]
----
As you can see there are no dependencies other than the Spring Cloud Contract Verifier Maven plugin.
Those poms are necessary for the consumer side to run `mvn clean install -DskipTests` to locally install
stubs of the producer project.
The `pom.xml` in the root folder can look like this:
[source,xml,indent=0]
----
include::{introduction_url}/samples/standalone/contracts/pom.xml[indent=0]
----
It's using the assembly plugin in order to build the JAR with all the contracts. Example of such setup is here:
[source,xml,indent=0]
----
include::{introduction_url}/samples/standalone/contracts/src/assembly/contracts.xml[indent=0]
----
*Workflow*
The workflow would look similar to the one presented in the `Step by step guide to CDC`. The only difference
is that the producer doesn't own the contracts anymore. So the consumer and the producer have to work on
common contracts in a common repository.
_Consumer_
When the *consumer* wants to work on the contracts offline, instead of cloning the producer code, the
consumer team clones the common repository, goes to the required producer's folder (e.g. `com/example/server`)
and runs `mvn clean install -DskipTests` to install locally the stubs converted from the contracts.
TIP: You need to have http://maven.apache.org/download.cgi[Maven installed locally]
_Producer_
As a *producer* it's enough to alter the Spring Cloud Contract Verifier to provide the URL and the dependency
of the JAR containing the contracts:
[source,xml,indent=0]
----
include::{introduction_url}/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic-remote-contracts/pom-with-repo.xml[tags=remote_config,indent=0]
----
With this setup the JAR with groupid `com.example.standalone` and artifactid `contracts` will be downloaded
from `http://link/to/your/nexus/or/artifactory/or/sth`. It will be then unpacked in a local temporary folder
and contracts present under the `com/example/server` will be picked as the ones used to generate the
tests and the stubs. Due to this convention the producer team will know which consumer teams will be broken
when some incompatible changes are done.
The rest of the flow looks the same.

View File

@@ -129,6 +129,11 @@ contracts {
contractsDslDir = "${project.rootDir}/src/test/resources/contracts"
basePackageForTests = 'org.springframework.cloud.verifier.tests'
stubsOutputDir = project.file("${project.buildDir}/stubs")
// the following properties are used when you want to provide where the JAR with contract lays
contractDependency = new org.springframework.cloud.contract.verifier.plugin.ContractVerifierExtension.Dependency()
contractsPath = ''
contractsWorkOffline = false
}
tasks.create(type: Jar, name: 'verifierStubsJar', dependsOn: 'generateWireMockClientStubs') {
@@ -185,6 +190,12 @@ contracts {
- **stubsOutputDir** - dir where the generated WireMock stubs from Groovy DSL should be placed
- **targetFramework** - the target test framework to be used; currently Spock and JUnit are supported with JUnit being the default framework
The following properties are used when you want to provide where the JAR with contract lays
- **contractDependency** - the Dependency that provides `groupid:artifactid:version:classifier` coordinates. You can use the `contractDependency` closure to set it up
- **contractsPath** - if contract deps are downloaded will default to `groupid/artifactid` where `groupid` will be slash separated. Otherwise will scan contracts under provided directory
- **contractsWorkOffline** - in order not to download the dependencies each time you can download them once and work offline afterwards (reuse local Maven repo)
====== Base class for tests
When using Spring Cloud Contract Verifier in default MockMvc you need to create a base specification for all generated acceptance tests. In this class you need to point to endpoint which should be verified.
@@ -331,6 +342,13 @@ To change default configuration just add `configuration` section to plugin defin
- **contractsDir** - directory containing contracts written using the GroovyDSL. By default `/src/test/resources/contracts`.
- **testFramework** - the target test framework to be used; currently Spock and JUnit are supported with JUnit being the default framework
If you want to download your contract definitions from a Maven repository you can use
- **contractsRepositoryUrl** - URL to a repo with the artifacts with contracts, if not provided should use the current Maven ones
- **contractDependency** - the contract dependency that contains all the packaged contracts
- **contractsPath** - path to concrete contracts in the JAR with packaged contracts. Defaults to `groupid/artifactid` where `gropuid` is slash separated.
- **contractsWorkOffline** - if the dependencies should be downloaded or local Maven only should be reused
For complete information take a look at https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract-maven-plugin/plugin-info.html[Plugin Documentation]
====== Base class for tests