+
+
+
As a developer of the Loan Issuance service (a consumer of the Fraud Detection server):
+
+
+
start doing TDD by writing a test to your feature
+
-
==== Step by step guide to CDC
-
-Let's take an example of Fraud Detection and Loan Issuance process. The business scenario is such that we want to issue loans to people but don't want them to steal the money from us. The current implementation of our system grants loans to everybody.
-
-Let's assume that the `Loan Issuance` is a client to the
-`Fraud Detection` server. In the current sprint we are required to develop a new feature - if a client wants to borrow too much money then we mark him as fraud.
-
-Technical remark - Fraud Detection will have artifact id `http-server`, Loan Issuance `http-client` and both have group id `com.example`.
-
-Social remark - both client and server development teams need to communicate directly and discuss changes while
-going through the process. CDC is all about communication.
-
-The https://github.com/spring-cloud/spring-cloud-contract/tree/1.0.x/samples/standalone/dsl/http-server[server side code is available here] and https://github.com/spring-cloud/spring-cloud-contract/tree/1.0.x/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
-
-[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
-.Maven
+
@Test
+public void shouldBeRejectedDueToAbnormalLoanAmount() {
+ // given:
+ LoanApplication application = new LoanApplication(new Client("1234567890"),
+ 99999);
+ // when:
+ LoanApplicationResult loanApplication = service.loanApplication(application);
+ // then:
+ assertThat(loanApplication.getLoanApplicationStatus())
+ .isEqualTo(LoanApplicationStatus.LOAN_APPLICATION_REJECTED);
+ assertThat(loanApplication.getRejectionReason()).isEqualTo("Amount too high");
+}
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-server/pom.xml[tags=repos,indent=0]
+
We’ve just written a test of our new feature. If a loan application for a big amount is received we should reject that loan application with some description.
+
+
+
write the missing implementation
+
+
+
At some point in time you need to send a request to the Fraud Detection service. Let’s assume that we’d like to send the request containing the id of the client and the amount he wants to borrow from us. We’d like to send it to the /fraudcheck url via the PUT method.
-
[source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
-.Gradle
+
ResponseEntity<FraudServiceResponse> response =
+ restTemplate.exchange("http://localhost:" + port + "/fraudcheck", HttpMethod.PUT,
+ new HttpEntity<>(request, httpHeaders),
+ FraudServiceResponse.class);
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-server/build.gradle[tags=deps_repos,indent=0]
+
For simplicity we’ve hardcoded the port of the Fraud Detection service at 8080 and our application is running on 8090.
+
+
+
If we’d start the written test it would obviously break since we have no service running on port 8080.
+
+
+
clone the Fraud Detection service repository locally
+
+
+
We’ll start playing around with the server side contract. That’s why we need to first clone it.
-
===== Consumer side (Loan Issuance)
-
-As a developer of the Loan Issuance service (a consumer of the Fraud Detection server):
-
-*start doing TDD by writing a test to your feature*
-
-[source,groovy,indent=0]
+
git clone https://your-git-server.com/server-side.git local-http-server-repo
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java[tags=client_tdd,indent=0]
+
define the contract locally in the repo of Fraud Detection service
+
+
+
As consumers we need to define what exactly we want to achieve. We need to formulate our expectations. That’s why we write the following contract.
+
+
+
+
+|
+
+ |
+
+We’re placing the contract under src/test/resources/contracts/fraud folder. The fraud folder
+is important cause we’ll reference that folder in the producer’s test base class name.
+ |
+
+
-
We've just written a test of our new feature. If a loan application for a big amount is received we should reject that loan application with some description.
+package contracts
-*write the missing implementation*
+org.springframework.cloud.contract.spec.Contract.make {
+ request { // (1)
+ method 'PUT' // (2)
+ url '/fraudcheck' // (3)
+ body([ // (4)
+ "client.id": $(regex('[0-9]{10}')),
+ loanAmount: 99999
+ ])
+ headers { // (5)
+ contentType('application/json')
+ }
+ }
+ response { // (6)
+ status 200 // (7)
+ body([ // (8)
+ fraudCheckStatus: "FRAUD",
+ "rejection.reason": "Amount too high"
+ ])
+ headers { // (9)
+ contentType('application/json')
+ }
+ }
+}
-At some point in time you need to send a request to the Fraud Detection service. Let's assume that we'd like to send the request containing the id of the client and the amount he wants to borrow from us. We'd like to send it to the `/fraudcheck` url via the `PUT` method.
+/*
+From the Consumer perspective, when shooting a request in the integration test:
-[source,groovy,indent=0]
+(1) - If the consumer sends a request
+(2) - With the "PUT" method
+(3) - to the URL "/fraudcheck"
+(4) - with the JSON body that
+ * has a field `clientId` that matches a regular expression `[0-9]{10}`
+ * has a field `loanAmount` that is equal to `99999`
+(5) - with header `Content-Type` equal to `application/json`
+(6) - then the response will be sent with
+(7) - status equal `200`
+(8) - and JSON body equal to
+ { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
+(9) - with header `Content-Type` equal to `application/json`
+
+From the Producer perspective, in the autogenerated producer-side test:
+
+(1) - A request will be sent to the producer
+(2) - With the "PUT" method
+(3) - to the URL "/fraudcheck"
+(4) - with the JSON body that
+ * has a field `clientId` that will have a generated value that matches a regular expression `[0-9]{10}`
+ * has a field `loanAmount` that is equal to `99999`
+(5) - with header `Content-Type` equal to `application/json`
+(6) - then the test will assert if the response has been sent with
+(7) - status equal `200`
+(8) - and JSON body equal to
+ { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
+(9) - with header `Content-Type` matching `application/json.*`
+ */
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-client/src/main/java/com/example/loan/LoanApplicationService.java[tags=client_call_server,indent=0]
-
-
-
-
For simplicity we've hardcoded the port of the Fraud Detection service at `8080` and our application is running on `8090`.
-
-If we'd start the written test it would obviously break since we have no service running on port `8080`.
-
-*clone the Fraud Detection service repository locally*
-
-We'll start playing around with the server side contract. That's why we need to first clone it.
-
-[source,bash,indent=0]
-
-
-
-
-
-
*define the contract locally in the repo of Fraud Detection service*
-
-As consumers we need to define what exactly we want to achieve. We need to formulate our expectations. That's why we write the following contract.
-
-IMPORTANT: We're placing the contract under `src/test/resources/contracts/fraud` folder. The `fraud` folder
-is important cause we'll reference that folder in the producer's test base class name.
-
-[source,groovy,indent=0]
-
-
-
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.groovy[]
-
-
The Contract is written using a statically typed Groovy DSL. You might be wondering what are those
value(client(…), server(…)) parts. By using this notation Spring Cloud Contract allows you to
define parts of a JSON / URL / etc. which are dynamic. In case of an identifier or a timestamp you
@@ -703,7 +941,17 @@ It’s really important that you understand the map notation to set up contr
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-server/pom.xml[tags=contract_bom,indent=0]
+
<dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-dependencies</artifactId>
+ <version>${spring-cloud-dependencies.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+</dependencyManagement>
@@ -711,7 +959,15 @@ It’s really important that you understand the map notation to set up contr
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-server/pom.xml[tags=contract_maven_plugin,indent=0]
+
<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.fraud</packageWithBaseClasses>
+ </configuration>
+</plugin>
@@ -777,7 +1033,17 @@ It’s really important that you understand the map notation to set up contr
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-client/pom.xml[tags=contract_bom,indent=0]
+
<dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-dependencies</artifactId>
+ <version>${spring-cloud-dependencies.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+</dependencyManagement>
@@ -785,7 +1051,11 @@ It’s really important that you understand the map notation to set up contr
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-client/pom.xml[tags=stub_runner,indent=0]
+
<dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
+ <scope>test</scope>
+</dependency>
@@ -793,7 +1063,11 @@ It’s really important that you understand the map notation to set up contr
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java[tags=autoconfigure_stubrunner,indent=0]
+
@RunWith(SpringRunner.class)
+@SpringBootTest(webEnvironment=WebEnvironment.NONE)
+@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, workOffline = true)
+@DirtiesContext
+public class LoanApplicationServiceTests {
@@ -836,8 +1110,9 @@ It’s really important that you understand the map notation to set up contr
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudDetectionController.java[tags=server_api,indent=0]
-Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudDetectionController.java[tags=initial_impl,indent=0]
+@RequestMapping(value = "/fraudcheck", method = PUT)
+public FraudCheckResult fraudCheck(@RequestBody FraudCheck fraudCheck) {
+return new FraudCheckResult(FraudCheckStatus.OK, NO_REASON);
}
@@ -855,7 +1130,11 @@ git pull https://your-git-server.com/server-side-fork.git contract-change-pr
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-server/pom.xml[tags=verifier_test_dependencies,indent=0]
+
<dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-starter-contract-verifier</artifactId>
+ <scope>test</scope>
+</dependency>
@@ -863,7 +1142,15 @@ git pull https://your-git-server.com/server-side-fork.git contract-change-pr
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-server/pom.xml[tags=contract_maven_plugin,indent=0]
+
<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.fraud</packageWithBaseClasses>
+ </configuration>
+</plugin>
-
-
Tests in error:
- ContractVerifierTest.validate_shouldMarkClientAsFraud:32 » IllegalState Parsed…
+
Now, if you run the ./mvnw clean install you would get sth like this:
-
That's because you have a new contract from which a test was generated and it failed since you haven't implemented the feature. The autogenerated test would look like this:
+Results :
-[source,java,indent=0]
+Tests in error:
+ ContractVerifierTest.validate_shouldMarkClientAsFraud:32 » IllegalState Parsed...
-
@Test
+
That’s because you have a new contract from which a test was generated and it failed since you haven’t implemented the feature. The autogenerated test would look like this:
+
+
+
+
@Test
public void validate_shouldMarkClientAsFraud() throws Exception {
// given:
MockMvcRequestSpecification request = given()
.header("Content-Type", "application/vnd.fraud.v1+json")
- .body("{\"client.id\":\"1234567890\",\"loanAmount\":99999}");
-
-
-
-
// when:
- ResponseOptions response = given().spec(request)
- .put("/fraudcheck");
-
-
-
-
-
// then:
+ .body("{\"client.id\":\"1234567890\",\"loanAmount\":99999}");
+
+ // when:
+ ResponseOptions response = given().spec(request)
+ .put("/fraudcheck");
+
+ // then:
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.header("Content-Type")).matches("application/vnd.fraud.v1.json.*");
// and:
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field("['fraudCheckStatus']").matches("[A-Z]{5}");
assertThatJson(parsedJson).field("['rejection.reason']").isEqualTo("Amount too high");
-}
-
-
-
-
-
As you can see all the `producer()` parts of the Contract that were present in the `value(consumer(...), producer(...))` blocks got injected into the test.
-
-What's important here to note is that on the producer side we also are doing TDD. We have expectations in form of a test. This test is shooting a request to our own application to an URL, headers and body defined in the contract. It also is expecting very precisely defined values in the response. In other words you have is your `red` part of `red`, `green` and `refactor`. Time to convert the `red` into the `green`.
-
-*write the missing implementation*
-
-Now since we now what is the expected input and expected output let's write the missing implementation.
-
-[source,java,indent=0]
+}
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudDetectionController.java[tags=server_api,indent=0]
-Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudDetectionController.java[tags=new_impl,indent=0]
-Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/dsl/http-server/src/main/java/com/example/fraud/FraudDetectionController.java[tags=initial_impl,indent=0]
-}
+
As you can see all the producer() parts of the Contract that were present in the value(consumer(…), producer(…)) blocks got injected into the test.
+
+
+
What’s important here to note is that on the producer side we also are doing TDD. We have expectations in form of a test. This test is shooting a request to our own application to an URL, headers and body defined in the contract. It also is expecting very precisely defined values in the response. In other words you have is your red part of red, green and refactor. Time to convert the red into the green.
+
+
+
write the missing implementation
+
+
+
Now since we now what is the expected input and expected output let’s write the missing implementation.
-
If we execute `./mvnw clean install` again the tests will pass. Since the `Spring Cloud Contract Verifier` plugin adds the tests to the `generated-test-sources` you can actually run those tests from your IDE.
-
-*deploy your app*
-
-Once you've finished your work it's time to deploy your change. First merge the branch
-
-[source,bash,indent=0]
+
@RequestMapping(value = "/fraudcheck", method = PUT)
+public FraudCheckResult fraudCheck(@RequestBody FraudCheck fraudCheck) {
+if (amountGreaterThanThreshold(fraudCheck)) {
+ return new FraudCheckResult(FraudCheckStatus.FRAUD, AMOUNT_TOO_HIGH);
+}
+return new FraudCheckResult(FraudCheckStatus.OK, NO_REASON);
+}
-
git checkout master
+
If we execute ./mvnw clean install again the tests will pass. Since the Spring Cloud Contract Verifier plugin adds the tests to the generated-test-sources you can actually run those tests from your IDE.
+
+
+
+
Once you’ve finished your work it’s time to deploy your change. First merge the branch
+
+
+
+
git checkout master
git merge --no-ff contract-change-pr
-git push origin master
-
-
-
-
Then we assume that your CI would run sth like `./mvnw clean deploy` which would publish both the application and the stub artifcats.
-
-===== Consumer side (Loan Issuance) final step
-
-As a developer of the Loan Issuance service (a consumer of the Fraud Detection server):
-
-*merge branch to master*
-
-[source,bash,indent=0]
+git push origin master
-
git checkout master
-git merge --no-ff contract-change-pr
+
Then we assume that your CI would run sth like ./mvnw clean deploy which would publish both the application and the stub artifcats.
+
+
+
+
+
+
As a developer of the Loan Issuance service (a consumer of the Fraud Detection server):
+
+
-
*work online*
-
-Now you can disable the offline work for Spring Cloud Contract Stub Runner and provide where the repository with your stubs is placed. At this moment the stubs of the server side will be automatically downloaded from Nexus / Artifactory.
-You can switch off the value of the `workOffline` parameter in your annotation. Below you can see an
-example of achieving the same by changing the properties.
-
-[source,yaml,indent=0]
+
git checkout master
+git merge --no-ff contract-change-pr
-
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/resources/application-test-repo.yaml[]
+
work online
+
+
+
Now you can disable the offline work for Spring Cloud Contract Stub Runner and provide where the repository with your stubs is placed. At this moment the stubs of the server side will be automatically downloaded from Nexus / Artifactory.
+You can switch off the value of the workOffline parameter in your annotation. Below you can see an
+example of achieving the same by changing the properties.
+
+
+
+
stubrunner:
+ ids: 'com.example:http-server-dsl:+:stubs:8080'
+ repositoryRoot: http://repo.spring.io/libs-snapshot
+
And that’s it!
@@ -1008,7 +1321,7 @@ example of achieving the same by changing the properties.
-
+
The best way to add the dependencies is to just use the proper starter dependency.
@@ -1018,7 +1331,7 @@ example of achieving the same by changing the properties.
-
+
Below you can find some resources related to Spring Cloud Contract Verifier and Stub Runner. Note that some can be outdated since the Spring Cloud Contract Verifier project
is under constant development.
@@ -1055,7 +1368,7 @@ is under constant development.
-
+
@@ -1416,129 +1729,346 @@ one to one to the contents of the repo.
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/contracts/com/example/server/pom.xml[indent=0]
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
-As you can see there are no dependencies other than the Spring Cloud Contract Maven Plugin.
-Those poms are necessary for the consumer side to run `mvn clean install -DskipTests` to locally install
- stubs of the producer project.
+ <groupId>com.example</groupId>
+ <artifactId>server</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
-The `pom.xml` in the root folder can look like this:
+ <name>Server Stubs</name>
+ <description>POM used to install locally stubs for consumer side</description>
-[source,xml,indent=0]
+ <parent>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-parent</artifactId>
+ <version>1.5.4.RELEASE</version>
+ <relativePath />
+ </parent>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <java.version>1.8</java.version>
+ <spring-cloud-contract.version>1.2.0.BUILD-SNAPSHOT</spring-cloud-contract.version>
+ <spring-cloud-dependencies.version>Edgware.BUILD-SNAPSHOT</spring-cloud-dependencies.version>
+ <excludeBuildFolders>true</excludeBuildFolders>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-dependencies</artifactId>
+ <version>${spring-cloud-dependencies.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-contract-maven-plugin</artifactId>
+ <version>${spring-cloud-contract.version}</version>
+ <extensions>true</extensions>
+ <configuration>
+ <!-- By default it would search under src/test/resources/ -->
+ <contractsDirectory>${project.basedir}</contractsDirectory>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <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>
+
+</project>
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/samples/standalone/contracts/pom.xml[indent=0]
+
As you can see there are no dependencies other than the Spring Cloud Contract 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:
+
+
+
+
<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>com.example.standalone</groupId>
+ <artifactId>contracts</artifactId>
+ <version>0.0.1-SNAPSHOT</version>
+
+ <name>Contracts</name>
+ <description>Contains all the Spring Cloud Contracts, well, contracts. JAR used by the producers to generate tests and stubs</description>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>contracts</id>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <attach>true</attach>
+ <descriptor>${basedir}/src/assembly/contracts.xml</descriptor>
+ <!-- If you want an explicit classifier remove the following line -->
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
+
It’s using the assembly plugin in order to build the JAR with all the contracts. Example of such setup is here:
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/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]
+
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
+ <id>project</id>
+ <formats>
+ <format>jar</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+ <fileSets>
+ <fileSet>
+ <directory>${project.basedir}</directory>
+ <outputDirectory>/</outputDirectory>
+ <useDefaultExcludes>true</useDefaultExcludes>
+ <excludes>
+ <exclude>**/${project.build.directory}/**</exclude>
+ <exclude>mvnw</exclude>
+ <exclude>mvnw.cmd</exclude>
+ <exclude>.mvn/**</exclude>
+ <exclude>src/**</exclude>
+ </excludes>
+ </fileSet>
+ </fileSets>
+</assembly>
+
+
+
-
Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic-remote-contracts/pom-with-repo.xml[tags=remote_config,indent=0]
+
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.
+
+
+
+
+
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.
+
+
+
+
+
+
+
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:
-
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
+<plugin>
+ <groupId>org.springframework.cloud</groupId>
+ <artifactId>spring-cloud-contract-maven-plugin</artifactId>
+ <configuration>
+ <contractsRepositoryUrl>http://link/to/your/nexus/or/artifactory/or/sth</contractsRepositoryUrl>
+ <contractDependency>
+ <groupId>com.example.standalone</groupId>
+ <artifactId>contracts</artifactId>
+ </contractDependency>
+ </configuration>
+</plugin>
+
+
+
+
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.
-
-==== Can I have multiple base classes for tests?
-
-Yes! Check out the https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html#_different_base_classes_for_contracts[Different base classes for contracts] sections
-of either Gradle or Maven plugins.
-
-==== How can I debug the request/response being sent by the generated tests client?
-
-The generated tests all boil down to RestAssured in some form or fashion which relies on https://hc.apache.org/httpcomponents-client-ga/[Apache HttpClient]. HttpClient has a facility called https://hc.apache.org/httpcomponents-client-ga/logging.html#Wire_Logging[wire logging] which logs the entire request and response to HttpClient. Spring Boot has a logging https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html[common application property] for doing this sort of thing, just add this to your application properties
-[source,properties,indent=0]
-
+when some incompatible changes are done.
-
logging.level.org.apache.http.wire=DEBUG
+
The rest of the flow looks the same.
+
+
+