[[how-to-common-repo-with-contracts]]
= How Can I use a Common Repository with Contracts Instead of Storing Them with the Producer?

include::partial$_attributes.adoc[]

Another way of storing contracts, rather than having them with the producer, is to keep
them in a common place. This situation can be related to security issues (where the
consumers cannot clone the producer's code). Also, if you keep contracts in a single place,
then you, as a producer, know how many consumers you have and which consumer you may break
with your local changes.

[[how-to-repo-structure]]
== Repo Structure

Assume that we have a producer with coordinates of `com.example:server` and three
consumers: `client1`, `client2`, and `client3`. Then, in the repository with common
contracts, you could have the following setup (which you can check out in the
https://github.com/spring-cloud/spring-cloud-contract/[Spring Cloud Contract's repository] `samples/standalone/contracts` subfolder).
The following listing shows such a structure:

[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
----

Under the slash-delimited `groupid/artifact id` folder (`com/example/server`), you have
expectations of the three 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 repository.

The following example shows a `pom.xml` file inside the `server` folder:

{samples_code}/standalone/dsl/contracts/com/example/server/pom.xml[Click here to see the code]

There are no dependencies other than the Spring Cloud Contract Maven Plugin.
Those `pom.xml` files are necessary for the consumer side to run `mvn clean install -DskipTests` to locally install
the stubs of the producer project.

The `pom.xml` file in the root folder can look like the following:

{samples_code}/standalone/dsl/contracts/pom.xml[Click here to see the code]

It uses the assembly plugin to build the JAR with all the contracts. The following example
shows such a setup:

{samples_code}/standalone/dsl/contracts/src/assembly/contracts.xml[Click here to see the code]

[[how-to-workflow]]
== Workflow

The workflow assumes that Spring Cloud Contract is set up both on the consumer and on the
producer side. There is also the proper plugin setup in the common repository with
contracts. The CI jobs are set for a common repository to build an artifact of all
contracts and upload it to Nexus or Artifactory. The following image shows the UML for this
workflow:

[plantuml, how-to-common-repo, png]
----
"API Consumer"->"Common repo": create a folder \nfor producer [API Producer]
"API Consumer"->"Common repo": under [API Producer] create a folder \nfor consumer \n[API Consumer]
"API Consumer"->"Common repo": define contracts under \n[API Consumer] folder
"API Consumer"->"Common repo": install stubs of [API Producer]\nin local storage
"Common repo"->"Common Repo\nSCC Plugin": install stubs \nin local storage. \nDon't generate tests.
"Common Repo\nSCC Plugin"->"Local storage": install stubs
"Local storage"->"Common Repo\nSCC Plugin": stubs installed
"API Consumer"->"API Consumer": write a SCC Stub Runner \nbased contract test
"API Consumer"->"API Consumer\nSCC Stub Runner": fetch the stubs\n of [API Producer] \nfrom local storage
"API Consumer\nSCC Stub Runner"->"Local storage": test asks for [API Producer] stubs
"Local storage"->"API Consumer\nSCC Stub Runner": [API Producer] stubs found
"API Consumer\nSCC Stub Runner"->"API Consumer\nSCC Stub Runner": run in memory\n HTTP server stubs
"API Consumer\nSCC Stub Runner"->"API Consumer": HTTP server stubs running,\n ready for tests
"API Consumer"->"API Consumer\nSCC Stub Runner": send a request \nto the HTTP server stub
"API Consumer\nSCC Stub Runner"->"API Consumer": communication is correct. \nTests are passing
"API Consumer"->"Common repo": file pull request \nwith contracts
"API Producer"->"Common repo": take over \nthe pull request
"API Producer"->"Common repo": install the JAR \nwith all contracts\n in local storage
"Common repo"->"Local storage": install the JAR
"Local storage"->"Common repo": contracts JAR installed
"API Producer"->"Producer Build": run the build \nand fetch contracts from \nlocal storage
"Producer Build"->"Producer\nSCC Plugin": generate \ntests, stubs and stubs \nartifact (e.g. stubs-jar)
"Producer\nSCC Plugin"->"Local storage": fetch contract definitions for [API Prodcer]
"Local storage"->"Producer\nSCC Plugin": contracts fetched
"Producer\nSCC Plugin"->"Producer Build": tests and stubs created
"Producer Build"->"Nexus / Artifactory": upload contracts \nand stubs and the project arifact
"Producer Build"->"API Producer": Build successful
"API Producer"->"Common repo": merge the pull request
"Common repo"->"Nexus / Artifactory": upload the fresh JAR \nwith contract definitions
"API Producer"->"API Producer": start fetching contract definitions \nfrom Nexus / Artifactory
----

[[how-to-workflow-consumer]]
== 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 (for example, `com/example/server`) and runs `mvn clean install -DskipTests` to
locally install the stubs converted from the contracts.

TIP: You need to have https://maven.apache.org/download.cgi[Maven installed locally].

[[how-to-workflow-producer]]
== Producer

As a producer, you can alter the Spring Cloud Contract Verifier to provide the URL and
the dependency of the JAR that contains the contracts, as follows:

[source,xml,indent=0]
----
include::{tools_path}/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 a `groupid` of `com.example.standalone` and an `artifactid` of
`contracts` is downloaded from `https://link/to/your/nexus/or/artifactory/or/sth`. It is
then unpacked in a local temporary folder, and the contracts present in
`com/example/server` are picked as the ones used to generate the tests and the stubs. Due
to this convention, the producer team can know which consumer teams are broken when
some incompatible changes are made.

The rest of the flow looks the same.

[[how-to-define-messaging-contracts-per-topic]]
== How Can I Define Messaging Contracts per Topic Rather than per Producer?

To avoid messaging contracts duplication in the common repository, when a few producers write messages to one topic,
we could create a structure in which the REST contracts are placed in a folder per producer and messaging
contracts are placed in the folder per topic.

[[how-to-define-messaging-contracts-per-topic-maven]]
=== For Maven Projects

To make it possible to work on the producer side, we should specify an inclusion pattern for
filtering common repository jar files by messaging topics we are interested in. The
`includedFiles` property of the Maven Spring Cloud Contract plugin
lets us do so. Also, `contractsPath` need to be specified, since the default path would be
the common repository `groupid/artifactid`. The following example shows a Maven
plugin for Spring Cloud Contract:

[source,xml,indent=0]
----
<plugin>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-contract-maven-plugin</artifactId>
   <version>${spring-cloud-contract.version}</version>
   <configuration>
      <contractsMode>REMOTE</contractsMode>
      <contractsRepositoryUrl>https://link/to/your/nexus/or/artifactory/or/sth</contractsRepositoryUrl>
      <contractDependency>
         <groupId>com.example</groupId>
         <artifactId>common-repo-with-contracts</artifactId>
         <version>+</version>
      </contractDependency>
      <contractsPath>/</contractsPath>
      <baseClassMappings>
         <baseClassMapping>
            <contractPackageRegex>.*messaging.*</contractPackageRegex>
            <baseClassFQN>com.example.services.MessagingBase</baseClassFQN>
         </baseClassMapping>
         <baseClassMapping>
            <contractPackageRegex>.*rest.*</contractPackageRegex>
            <baseClassFQN>com.example.services.TestBase</baseClassFQN>
         </baseClassMapping>
      </baseClassMappings>
      <includedFiles>
         <includedFile>**/${project.artifactId}/**</includedFile>
         <includedFile>**/${first-topic}/**</includedFile>
         <includedFile>**/${second-topic}/**</includedFile>
      </includedFiles>
   </configuration>
</plugin>
----

NOTE: Many of the values in the preceding Maven plugin can be changed. We included it for
illustration purposes rather than trying to provide a "`typical`" example.

[[how-to-define-messaging-contracts-per-topic-gradle]]
=== For Gradle Projects

To work with a Gradle project:

. Add a custom configuration for the common repository dependency, as follows:
+
[source,groovy,indent=0]
----
ext {
    contractsGroupId = "com.example"
    contractsArtifactId = "common-repo"
    contractsVersion = "1.2.3"
}

configurations {
    contracts {
        transitive = false
    }
}
----

. Add the common repository dependency to your classpath, as follows:
+
[source,groovy,indent=0]
----
dependencies {
    contracts "${contractsGroupId}:${contractsArtifactId}:${contractsVersion}"
    testCompile "${contractsGroupId}:${contractsArtifactId}:${contractsVersion}"
}
----

. Download the dependency to an appropriate folder, as follows:
+
[source,groovy,indent=0]
----
task getContracts(type: Copy) {
    from configurations.contracts
    into new File(project.buildDir, "downloadedContracts")
}
----

. Unzip the JAR, as follows:
+
[source,groovy,indent=0]
----
task unzipContracts(type: Copy) {
    def zipFile = new File(project.buildDir, "downloadedContracts/${contractsArtifactId}-${contractsVersion}.jar")
    def outputDir = file("${buildDir}/unpackedContracts")

    from zipTree(zipFile)
    into outputDir
}
----

. Cleanup unused contracts, as follows:
+
[source,groovy,indent=0]
----
task deleteUnwantedContracts(type: Delete) {
    delete fileTree(dir: "${buildDir}/unpackedContracts",
        include: "**/*",
        excludes: [
            "**/${project.name}/**"",
            "**/${first-topic}/**",
            "**/${second-topic}/**"])
}
----

. Create task dependencies, as follows:
+
[source,groovy,indent=0]
----
unzipContracts.dependsOn("getContracts")
deleteUnwantedContracts.dependsOn("unzipContracts")
build.dependsOn("deleteUnwantedContracts")
----

. Configure the plugin by specifying the directory that contains the contracts, by setting
the `contractsDslDir` property, as follows:
+
[source,groovy,indent=0]
----
contracts {
    contractsDslDir = new File("${buildDir}/unpackedContracts")
}
----

