diff --git a/README.adoc b/README.adoc
index 59a9673b21..4d492e96b2 100644
--- a/README.adoc
+++ b/README.adoc
@@ -414,6 +414,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
@@ -1231,6 +1234,98 @@ Example of tests using production version of stubs
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]
+----
+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]
+
+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]
+----
+Unresolved directive in verifier/introduction.adoc - include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/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]
+----
+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]
+----
+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]
+----
+
+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.
+
=== Links
Here you can find interesting links related to Spring Cloud Contract Verifier:
diff --git a/docs/src/main/asciidoc/verifier/introduction.adoc b/docs/src/main/asciidoc/verifier/introduction.adoc
index 4900d9d157..7a299bbeaf 100644
--- a/docs/src/main/asciidoc/verifier/introduction.adoc
+++ b/docs/src/main/asciidoc/verifier/introduction.adoc
@@ -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.
\ No newline at end of file
+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.
\ No newline at end of file
diff --git a/docs/src/main/asciidoc/verifier/rest.adoc b/docs/src/main/asciidoc/verifier/rest.adoc
index ad6f94cffc..84c8692553 100644
--- a/docs/src/main/asciidoc/verifier/rest.adoc
+++ b/docs/src/main/asciidoc/verifier/rest.adoc
@@ -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
diff --git a/pom.xml b/pom.xml
index 883984b6ae..2f5c65b4c2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,6 @@
Brooklyn.BUILD-SNAPSHOT
1.2.0.BUILD-SNAPSHOT
1.1.0.BUILD-SNAPSHOT
- 1.1.3.BUILD-SNAPSHOT
@@ -147,13 +146,6 @@
pom
import
-
- org.springframework.cloud
- spring-cloud-commons-dependencies
- ${spring-cloud-commons.version}
- pom
- import
-
diff --git a/samples/standalone/contracts/.mvn/wrapper/maven-wrapper.jar b/samples/standalone/contracts/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000000..c6feb8bb6f
Binary files /dev/null and b/samples/standalone/contracts/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/samples/standalone/contracts/.mvn/wrapper/maven-wrapper.properties b/samples/standalone/contracts/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000000..6637cedb28
--- /dev/null
+++ b/samples/standalone/contracts/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1 @@
+distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip
\ No newline at end of file
diff --git a/samples/standalone/contracts/com/example/server/client1/expectation.groovy b/samples/standalone/contracts/com/example/server/client1/expectation.groovy
new file mode 100644
index 0000000000..47b9ba6662
--- /dev/null
+++ b/samples/standalone/contracts/com/example/server/client1/expectation.groovy
@@ -0,0 +1,67 @@
+package contracts
+
+org.springframework.cloud.contract.spec.Contract.make {
+ request { // (1)
+ method 'PUT' // (2)
+ url '/fraudcheck1' // (3)
+ body([ // (4)
+ clientId: value(consumer(regex('[0-9]{10}'))),
+ loanAmount: 99999
+ ])
+ headers { // (5)
+ header('Content-Type', 'application/vnd.fraud.v1+json')
+ }
+ }
+ response { // (6)
+ status 200 // (7)
+ body([ // (8)
+ fraudCheckStatus: "FRAUD",
+ rejectionReason: "Amount too high"
+ ])
+ headers { // (9)
+ header('Content-Type': value(
+ producer(regex('application/vnd.fraud.v1.json.*')),
+ consumer('application/vnd.fraud.v1+json'))
+ )
+ }
+ }
+}
+
+/*
+Since we don't want to force on the user to hardcode values of fields that are dynamic
+(timestamps, database ids etc.), one can provide parametrize those entries by using the
+`value(consumer(...), producer(...))` method. That way what's present in the `consumer`
+section will end up in the produced stub. What's there in the `producer` will end up in the
+autogenerated test. If you provide only the regular expression side without the concrete
+value then Spring Cloud Contract will generate one for you.
+
+From the Consumer perspective, when shooting a request in the integration test:
+
+(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/vnd.fraud.v1+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/vnd.fraud.v1+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/vnd.fraud.v1+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/vnd.fraud.v1+json.*`
+ */
\ No newline at end of file
diff --git a/samples/standalone/contracts/com/example/server/client2/expectation.groovy b/samples/standalone/contracts/com/example/server/client2/expectation.groovy
new file mode 100644
index 0000000000..c8eb2adff4
--- /dev/null
+++ b/samples/standalone/contracts/com/example/server/client2/expectation.groovy
@@ -0,0 +1,67 @@
+package contracts
+
+org.springframework.cloud.contract.spec.Contract.make {
+ request { // (1)
+ method 'PUT' // (2)
+ url '/fraudcheck2' // (3)
+ body([ // (4)
+ clientId: value(consumer(regex('[0-9]{10}'))),
+ loanAmount: 99999
+ ])
+ headers { // (5)
+ header('Content-Type', 'application/vnd.fraud.v1+json')
+ }
+ }
+ response { // (6)
+ status 200 // (7)
+ body([ // (8)
+ fraudCheckStatus: "FRAUD",
+ rejectionReason: "Amount too high"
+ ])
+ headers { // (9)
+ header('Content-Type': value(
+ producer(regex('application/vnd.fraud.v1.json.*')),
+ consumer('application/vnd.fraud.v1+json'))
+ )
+ }
+ }
+}
+
+/*
+Since we don't want to force on the user to hardcode values of fields that are dynamic
+(timestamps, database ids etc.), one can provide parametrize those entries by using the
+`value(consumer(...), producer(...))` method. That way what's present in the `consumer`
+section will end up in the produced stub. What's there in the `producer` will end up in the
+autogenerated test. If you provide only the regular expression side without the concrete
+value then Spring Cloud Contract will generate one for you.
+
+From the Consumer perspective, when shooting a request in the integration test:
+
+(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/vnd.fraud.v1+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/vnd.fraud.v1+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/vnd.fraud.v1+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/vnd.fraud.v1+json.*`
+ */
\ No newline at end of file
diff --git a/samples/standalone/contracts/com/example/server/client3/expectation.groovy b/samples/standalone/contracts/com/example/server/client3/expectation.groovy
new file mode 100644
index 0000000000..47958d8ae9
--- /dev/null
+++ b/samples/standalone/contracts/com/example/server/client3/expectation.groovy
@@ -0,0 +1,67 @@
+package contracts
+
+org.springframework.cloud.contract.spec.Contract.make {
+ request { // (1)
+ method 'PUT' // (2)
+ url '/fraudcheck3' // (3)
+ body([ // (4)
+ clientId: value(consumer(regex('[0-9]{10}'))),
+ loanAmount: 99999
+ ])
+ headers { // (5)
+ header('Content-Type', 'application/vnd.fraud.v1+json')
+ }
+ }
+ response { // (6)
+ status 200 // (7)
+ body([ // (8)
+ fraudCheckStatus: "FRAUD",
+ rejectionReason: "Amount too high"
+ ])
+ headers { // (9)
+ header('Content-Type': value(
+ producer(regex('application/vnd.fraud.v1.json.*')),
+ consumer('application/vnd.fraud.v1+json'))
+ )
+ }
+ }
+}
+
+/*
+Since we don't want to force on the user to hardcode values of fields that are dynamic
+(timestamps, database ids etc.), one can provide parametrize those entries by using the
+`value(consumer(...), producer(...))` method. That way what's present in the `consumer`
+section will end up in the produced stub. What's there in the `producer` will end up in the
+autogenerated test. If you provide only the regular expression side without the concrete
+value then Spring Cloud Contract will generate one for you.
+
+From the Consumer perspective, when shooting a request in the integration test:
+
+(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/vnd.fraud.v1+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/vnd.fraud.v1+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/vnd.fraud.v1+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/vnd.fraud.v1+json.*`
+ */
\ No newline at end of file
diff --git a/samples/standalone/contracts/com/example/server/pom.xml b/samples/standalone/contracts/com/example/server/pom.xml
new file mode 100644
index 0000000000..ad18558a2d
--- /dev/null
+++ b/samples/standalone/contracts/com/example/server/pom.xml
@@ -0,0 +1,107 @@
+
+
+ 4.0.0
+
+ com.example
+ server
+ 0.0.1-SNAPSHOT
+
+ Server Stubs
+ POM used to install locally stubs for consumer side
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 1.4.0.BUILD-SNAPSHOT
+
+
+
+
+ UTF-8
+ 1.8
+ 1.0.0.BUILD-SNAPSHOT
+ Camden.BUILD-SNAPSHOT
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud-dependencies.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-contract-maven-plugin
+ ${spring-cloud-contract.version}
+ true
+
+
+ ${project.basedir}
+
+
+
+
+
+
+
+ spring-snapshots
+ Spring Snapshots
+ https://repo.spring.io/snapshot
+
+ true
+
+
+
+ spring-milestones
+ Spring Milestones
+ https://repo.spring.io/milestone
+
+ false
+
+
+
+ spring-releases
+ Spring Releases
+ https://repo.spring.io/release
+
+ false
+
+
+
+
+
+ spring-snapshots
+ Spring Snapshots
+ https://repo.spring.io/snapshot
+
+ true
+
+
+
+ spring-milestones
+ Spring Milestones
+ https://repo.spring.io/milestone
+
+ false
+
+
+
+ spring-releases
+ Spring Releases
+ https://repo.spring.io/release
+
+ false
+
+
+
+
+
diff --git a/samples/standalone/contracts/mvnw b/samples/standalone/contracts/mvnw
new file mode 100755
index 0000000000..fc7efd17d0
--- /dev/null
+++ b/samples/standalone/contracts/mvnw
@@ -0,0 +1,234 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ #
+ # Look for the Apple JDKs first to preserve the existing behaviour, and then look
+ # for the new JDKs provided by Oracle.
+ #
+ if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
+ #
+ # Apple JDKs
+ #
+ export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
+ fi
+
+ if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
+ #
+ # Apple JDKs
+ #
+ export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
+ fi
+
+ if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
+ #
+ # Oracle JDKs
+ #
+ export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
+ fi
+
+ if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
+ #
+ # Apple JDKs
+ #
+ export JAVA_HOME=`/usr/libexec/java_home`
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Migwn, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+ # TODO classpath?
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+fi
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+ local basedir=$(pwd)
+ local wdir=$(pwd)
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ wdir=$(cd "$wdir/.."; pwd)
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CMD_LINE_ARGS
+
diff --git a/samples/standalone/contracts/mvnw.cmd b/samples/standalone/contracts/mvnw.cmd
new file mode 100644
index 0000000000..001048081d
--- /dev/null
+++ b/samples/standalone/contracts/mvnw.cmd
@@ -0,0 +1,145 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %*
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+
+set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar""
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/samples/standalone/contracts/pom.xml b/samples/standalone/contracts/pom.xml
new file mode 100644
index 0000000000..33fcad34dc
--- /dev/null
+++ b/samples/standalone/contracts/pom.xml
@@ -0,0 +1,41 @@
+
+
+ 4.0.0
+
+ com.example.standalone
+ contracts
+ 0.0.1-SNAPSHOT
+
+ Spring Cloud Contract Verifier Http Server Sample
+ Spring Cloud Contract Verifier Http Server Sample
+
+
+ UTF-8
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ contracts
+ prepare-package
+
+ single
+
+
+ true
+ ${basedir}/src/assembly/contracts.xml
+
+ false
+
+
+
+
+
+
+
+
diff --git a/samples/standalone/contracts/src/assembly/contracts.xml b/samples/standalone/contracts/src/assembly/contracts.xml
new file mode 100644
index 0000000000..89fb589f6c
--- /dev/null
+++ b/samples/standalone/contracts/src/assembly/contracts.xml
@@ -0,0 +1,23 @@
+
+ project
+
+ jar
+
+ false
+
+
+ ${project.basedir}
+ /
+ true
+
+ **/${project.build.directory}/**
+ mvnw
+ mvnw.cmd
+ .mvn/**
+ src/**
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-contract-dependencies/pom.xml b/spring-cloud-contract-dependencies/pom.xml
index 5552ffb5c1..6a848d3c49 100644
--- a/spring-cloud-contract-dependencies/pom.xml
+++ b/spring-cloud-contract-dependencies/pom.xml
@@ -17,6 +17,7 @@
2.1.7
0.4.7
1.0.2.v20150114
+ 1.1.3.BUILD-SNAPSHOT
@@ -60,6 +61,13 @@
spring-cloud-starter-contract-stub-runner-jetty
${project.version}
+
+ org.springframework.cloud
+ spring-cloud-commons-dependencies
+ ${spring-cloud-commons.version}
+ pom
+ import
+
com.github.tomakehurst
wiremock
diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ContractDownloader.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ContractDownloader.java
new file mode 100644
index 0000000000..e37b8c2ba4
--- /dev/null
+++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ContractDownloader.java
@@ -0,0 +1,91 @@
+package org.springframework.cloud.contract.stubrunner;
+
+import java.io.File;
+import java.lang.invoke.MethodHandles;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties;
+import org.springframework.util.StringUtils;
+
+/**
+ * Downloads a JAR with contracts and sets up the plugin configuration with proper
+ * inclusion patterns
+ *
+ * @author Marcin Grzejszczak
+ *
+ * @since 1.0.0
+ */
+public class ContractDownloader {
+
+ private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
+
+ private final StubDownloader stubDownloader;
+ private final StubConfiguration contractsJarStubConfiguration;
+ private final String contractsPath;
+ private final String projectGroupId;
+ private final String projectArtifactId;
+
+ public ContractDownloader(StubDownloader stubDownloader,
+ StubConfiguration contractsJarStubConfiguration,
+ String contractsPath, String projectGroupId, String projectArtifactId) {
+ this.stubDownloader = stubDownloader;
+ this.contractsJarStubConfiguration = contractsJarStubConfiguration;
+ this.contractsPath = contractsPath;
+ this.projectGroupId = projectGroupId;
+ this.projectArtifactId = projectArtifactId;
+ }
+
+ /**
+ * Downloads JAR containing all the contracts. Plugin configuration gets updated with
+ * the inclusion pattern for the downloaded contracts. The JAR with the contracts contains all
+ * the contracts for all the projects. We're interested only in its subset.
+ *
+ * @param config - Plugin configuration that will get updated with the inclusion pattern
+ * @return location of the unpacked downloaded stubs
+ */
+ public File unpackedDownloadedContracts(ContractVerifierConfigProperties config) {
+ File contractsDirectory = unpackAndDownloadContracts();
+ updatePropertiesWithInclusion(contractsDirectory, config);
+ return contractsDirectory;
+ }
+
+ public ContractVerifierConfigProperties updatePropertiesWithInclusion(File contractsDirectory,
+ ContractVerifierConfigProperties config) {
+ String pattern = StringUtils.hasText(this.contractsPath) ? patternFromProperty(contractsDirectory) :
+ groupArtifactToPattern(contractsDirectory);
+ log.info("Pattern to pick contracts equals [" + pattern + "]");
+ config.setIncludedContracts(pattern);
+ return config;
+ }
+
+ private String patternFromProperty(File contractsDirectory) {
+ return "^" + contractsDirectory.getAbsolutePath() + contractsPath() + ".*$";
+ }
+
+ private String contractsPath() {
+ return this.contractsPath.startsWith(File.separator) ? this.contractsPath : File.separator + this.contractsPath;
+ }
+
+ private File unpackAndDownloadContracts() {
+ log.info("Will download contracts for [" + this.contractsJarStubConfiguration + "]");
+ Map.Entry unpackedContractStubs = this.stubDownloader
+ .downloadAndUnpackStubJar(null, this.contractsJarStubConfiguration);
+ if (unpackedContractStubs == null) {
+ throw new IllegalStateException("The contracts failed to be downloaded!");
+ }
+ return unpackedContractStubs.getValue();
+ }
+
+ private String groupArtifactToPattern(File contractsDirectory) {
+ return "^" +
+ contractsDirectory.getAbsolutePath() +
+ File.separator +
+ this.projectGroupId.replace(".", File.separator) +
+ File.separator +
+ this.projectArtifactId
+ + File.separator +
+ ".*$";
+ }
+}
diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubConfiguration.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubConfiguration.java
index 51b2417c8c..23a6caccfd 100644
--- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubConfiguration.java
+++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubConfiguration.java
@@ -91,10 +91,17 @@ public class StubConfiguration {
return "";
}
return StringUtils.arrayToDelimitedString(
- new String[] { this.groupId, this.artifactId, this.version, this.classifier },
+ new String[] { nullCheck(this.groupId),
+ nullCheck(this.artifactId),
+ nullCheck(this.version),
+ nullCheck(this.classifier) },
STUB_COLON_DELIMITER);
}
+ private String nullCheck(String value) {
+ return StringUtils.hasText(value) ? value : "";
+ }
+
public boolean groupIdAndArtifactMatches(String ivyNotationAsString) {
String[] parts = ivyNotationFrom(ivyNotationAsString);
String groupId = parts[0];
@@ -117,6 +124,10 @@ public class StubConfiguration {
return this.classifier;
}
+ public String getVersion() {
+ return this.version;
+ }
+
@Override
public int hashCode() {
final int prime = 31;
diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/ContractDownloaderSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/ContractDownloaderSpec.groovy
new file mode 100644
index 0000000000..89ee9e369a
--- /dev/null
+++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/ContractDownloaderSpec.groovy
@@ -0,0 +1,41 @@
+package org.springframework.cloud.contract.stubrunner
+
+import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
+import spock.lang.Specification
+/**
+ * @author Marcin Grzejszczak
+ */
+class ContractDownloaderSpec extends Specification {
+
+ StubDownloader stubDownloader = Stub()
+ StubConfiguration stubConfiguration = new StubConfiguration('')
+ File file = new File('/some/path/to/somewhere')
+
+ def 'should set inclusion pattern on config when path pattern was explicitly provided with a separator at the beginning'() {
+ given:
+ String contractPath = '/a/b/c/d'
+ ContractDownloader contractDownloader = new ContractDownloader(stubDownloader,
+ stubConfiguration, contractPath, '', '')
+ ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
+ and:
+ stubDownloader.downloadAndUnpackStubJar(_, _) >> new AbstractMap.SimpleEntry(stubConfiguration, file)
+ when:
+ contractDownloader.unpackedDownloadedContracts(properties)
+ then:
+ properties.includedContracts == '^/some/path/to/somewhere/a/b/c/d.*$'
+ }
+
+ def 'should set inclusion pattern on config when path pattern was explicitly provided without a separator at the beginning'() {
+ given:
+ String contractPath = 'a/b/c/d'
+ ContractDownloader contractDownloader = new ContractDownloader(stubDownloader,
+ stubConfiguration, contractPath, '', '')
+ ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
+ and:
+ stubDownloader.downloadAndUnpackStubJar(_, _) >> new AbstractMap.SimpleEntry(stubConfiguration, file)
+ when:
+ contractDownloader.unpackedDownloadedContracts(properties)
+ then:
+ properties.includedContracts == '^/some/path/to/somewhere/a/b/c/d.*$'
+ }
+}
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml
index 3c2f456076..0569637047 100644
--- a/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml
+++ b/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml
@@ -14,6 +14,10 @@
Spring Cloud Contract Converters
1.8
+
+ org.springframework
+ spring-context
+
org.springframework.cloud
spring-cloud-contract-verifier
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/wiremock/RecursiveFilesConverter.groovy b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/wiremock/RecursiveFilesConverter.groovy
index 5be7dec7ab..8c8d5d6ddc 100644
--- a/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/wiremock/RecursiveFilesConverter.groovy
+++ b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/wiremock/RecursiveFilesConverter.groovy
@@ -54,7 +54,8 @@ class RecursiveFilesConverter {
}
void processFiles() {
- ContractFileScanner scanner = new ContractFileScanner(properties.contractsDslDir, properties.excludedFiles as Set, [] as Set)
+ ContractFileScanner scanner = new ContractFileScanner(properties.contractsDslDir,
+ properties.excludedFiles as Set, [] as Set, properties.includedContracts)
ListMultimap contracts = scanner.findContracts()
if (log.isDebugEnabled()) {
log.debug("Found the following contracts $contracts")
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierExtension.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierExtension.groovy
new file mode 100644
index 0000000000..01afc18dc5
--- /dev/null
+++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierExtension.groovy
@@ -0,0 +1,127 @@
+package org.springframework.cloud.contract.verifier.plugin
+
+import groovy.transform.ToString
+import org.springframework.cloud.contract.verifier.config.TestFramework
+import org.springframework.cloud.contract.verifier.config.TestMode
+
+/**
+ * @author Marcin Grzejszczak
+ */
+@ToString
+class ContractVerifierExtension {
+ /**
+ * For which unit test library tests should be generated
+ */
+ TestFramework targetFramework = TestFramework.JUNIT
+
+ /**
+ * Which mechanism should be used to invoke REST calls during tests
+ */
+ TestMode testMode = TestMode.MOCKMVC
+
+ /**
+ * Base package for generated tests
+ */
+ String basePackageForTests
+
+ /**
+ * Class which all generated tests should extend
+ */
+ String baseClassForTests
+
+ /**
+ * Suffix for generated test classes, like Spec or Test
+ */
+ String nameSuffixForTests
+
+ /**
+ * Rule class that should be added to generated tests
+ */
+ String ruleClassForTests
+
+ /**
+ * Patterns that should not be taken into account for processing
+ */
+ List excludedFiles = []
+
+ /**
+ * Patterns for which generated tests should be @Ignored
+ */
+ List ignoredFiles = []
+
+ /**
+ * Imports that should be added to generated tests
+ */
+ String[] imports = []
+
+ /**
+ * Static imports that should be added to generated tests
+ */
+ String[] staticImports = []
+
+ /**
+ * Directory containing contracts written using the GroovyDSL
+ */
+ File contractsDslDir
+
+ /**
+ * Test source directory where tests generated from Groovy DSL should be placed
+ */
+ File generatedTestSourcesDir
+
+ /**
+ * Dir where the generated WireMock stubs from Groovy DSL should be placed.
+ * You can then mention them in your packaging task to create jar with stubs
+ */
+ File stubsOutputDir
+
+ /**
+ * Suffix for the generated Stubs Jar task
+ */
+ String stubsSuffix = 'stubs'
+
+ /**
+ * Incubating feature. You can check the size of JSON arrays. If not turned on
+ * explicitly will be disabled.
+ */
+ Boolean assertJsonSize = false
+
+ /**
+ * The URL from which a JAR containing the contracts should get downloaded. If not provided
+ * but artifactid / coordinates notation was provided then the current Maven's build repositories will be
+ * taken into consideration
+ */
+ String contractsRepositoryUrl
+
+ /**
+ * Dependency that contains packaged contracts
+ */
+ Dependency contractDependency = new Dependency()
+
+ /**
+ * The path in the JAR with all the contracts where contracts for this particular service lay.
+ * If not provided will be resolved to {@code groupid/artifactid}. Example:
+ *
+ * If {@code groupid} is {@code com.example} and {@code artifactid} is {@code service} then the resolved path will be
+ * {@code /com/example/artifactid}
+ */
+ String contractsPath
+
+ /**
+ * If {@code true} then JAR with contracts will be taken from local maven repository
+ */
+ boolean contractsWorkOffline
+
+ void contractDependency(@DelegatesTo(Dependency) Closure closure) {
+ closure.delegate = contractDependency
+ closure.call()
+ }
+
+ static class Dependency {
+ String groupId
+ String artifactId
+ String classifier
+ String version
+ String stringNotation
+ }
+}
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ExtensionToProperties.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ExtensionToProperties.groovy
new file mode 100644
index 0000000000..6e0262ebfe
--- /dev/null
+++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/ExtensionToProperties.groovy
@@ -0,0 +1,31 @@
+package org.springframework.cloud.contract.verifier.plugin
+
+import groovy.transform.PackageScope
+import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
+
+/**
+ * @author Marcin Grzejszczak
+ */
+@PackageScope
+class ExtensionToProperties {
+
+ protected static ContractVerifierConfigProperties fromExtension(ContractVerifierExtension extension) {
+ return new ContractVerifierConfigProperties(
+ targetFramework: extension.targetFramework,
+ testMode: extension.testMode,
+ basePackageForTests: extension.basePackageForTests,
+ baseClassForTests: extension.baseClassForTests,
+ nameSuffixForTests: extension.nameSuffixForTests,
+ ruleClassForTests: extension.ruleClassForTests,
+ excludedFiles: extension.excludedFiles,
+ ignoredFiles: extension.ignoredFiles,
+ imports: extension.imports,
+ staticImports: extension.staticImports,
+ contractsDslDir: extension.contractsDslDir,
+ generatedTestSourcesDir: extension.generatedTestSourcesDir,
+ stubsOutputDir: extension.stubsOutputDir,
+ stubsSuffix: extension.stubsSuffix,
+ assertJsonSize: extension.assertJsonSize
+ )
+ }
+}
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.groovy
index ce5301b3fd..055e58ba33 100644
--- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.groovy
+++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateServerTestsTask.groovy
@@ -38,7 +38,7 @@ class GenerateServerTestsTask extends ConventionTask {
File generatedTestSourcesDir
//TODO: How to deal with @Input*, @Output* and that domain object?
- ContractVerifierConfigProperties configProperties
+ ContractVerifierExtension configProperties
@TaskAction
void generate() {
@@ -51,7 +51,9 @@ class GenerateServerTestsTask extends ConventionTask {
try {
//TODO: What with that? How to pass?
- TestGenerator generator = new TestGenerator(getConfigProperties())
+ ContractVerifierConfigProperties props = ExtensionToProperties.fromExtension(getConfigProperties())
+ props.contractsDslDir = getContractsDslDir()
+ TestGenerator generator = new TestGenerator(props)
int generatedClasses = generator.generate()
project.logger.info("Generated {} test classes", generatedClasses)
} catch (ContractVerifierException e) {
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateWireMockClientStubsFromDslTask.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateWireMockClientStubsFromDslTask.groovy
index a3693e28c8..eed1d46cc6 100644
--- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateWireMockClientStubsFromDslTask.groovy
+++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GenerateWireMockClientStubsFromDslTask.groovy
@@ -38,18 +38,19 @@ class GenerateWireMockClientStubsFromDslTask extends ConventionTask {
@OutputDirectory
File stubsOutputDir
- ContractVerifierConfigProperties configProperties
+ ContractVerifierExtension configProperties
@TaskAction
void generate() {
logger.info("Spring Cloud Contract Verifier Plugin: Invoking DSL to WireMock client stubs conversion")
logger.debug("From '${getContractsDslDir()}' to '${getStubsOutputDir()}'")
- ContractVerifierConfigProperties props = getConfigProperties()
+ ContractVerifierConfigProperties props = ExtensionToProperties.fromExtension(getConfigProperties())
+ props.contractsDslDir = getContractsDslDir()
File outMappingsDir = props.stubsOutputDir != null ? new File(props.stubsOutputDir, DEFAULT_MAPPINGS_FOLDER)
: new File(project.buildDir, "stubs/$DEFAULT_MAPPINGS_FOLDER")
RecursiveFilesConverter converter = new RecursiveFilesConverter(
new DslToWireMockClientConverter(),
- getConfigProperties(), outMappingsDir)
+ props, outMappingsDir)
converter.processFiles()
}
}
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloader.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloader.groovy
new file mode 100644
index 0000000000..8ee920a0e3
--- /dev/null
+++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/GradleContractsDownloader.groovy
@@ -0,0 +1,82 @@
+package org.springframework.cloud.contract.verifier.plugin
+
+import groovy.transform.PackageScope
+import org.gradle.api.Project
+import org.gradle.api.logging.Logger
+import org.springframework.cloud.contract.stubrunner.AetherStubDownloader
+import org.springframework.cloud.contract.stubrunner.ContractDownloader
+import org.springframework.cloud.contract.stubrunner.StubConfiguration
+import org.springframework.cloud.contract.stubrunner.StubRunnerOptionsBuilder
+import org.springframework.util.StringUtils
+
+import java.util.concurrent.ConcurrentHashMap
+/**
+ * @author Marcin Grzejszczak
+ */
+@PackageScope
+class GradleContractsDownloader {
+
+ private static final String LATEST_VERSION = '+'
+
+ private final Project project
+ private final Logger log
+ private static final Map downloadedContract = new ConcurrentHashMap<>()
+
+ GradleContractsDownloader(Project project, Logger log) {
+ this.project = project
+ this.log = log
+ }
+
+ File downloadAndUnpackContractsIfRequired(ContractVerifierExtension extension) {
+ File defaultContractsDir = extension.contractsDslDir
+ // download contracts, unzip them and pass as output directory
+ if (shouldDownloadContracts(extension)) {
+ this.log.info("For project [${this.project.name}] Download dependency is provided - will download contract jars")
+ StubConfiguration configuration = stubConfiguration(extension.contractDependency)
+ if (downloadedContract.get(configuration)) {
+ this.log.info("For project [${this.project.name}] Returning the cached location of the contracts")
+ return downloadedContract.get(configuration)
+ }
+ File downloadedContracts = contractDownloader(extension, configuration).unpackedDownloadedContracts(
+ ExtensionToProperties.fromExtension(extension))
+ downloadedContract.put(configuration, downloadedContracts)
+ return downloadedContracts
+ }
+ this.log.info("For project [${this.project.name}] will use contracts provided in the folder [" + defaultContractsDir + "]")
+ return defaultContractsDir
+ }
+
+ private boolean shouldDownloadContracts(ContractVerifierExtension extension) {
+ return StringUtils.hasText(extension.contractsRepositoryUrl) &&
+ (StringUtils.hasText(extension.contractDependency.artifactId) ||
+ StringUtils.hasText(extension.contractDependency.stringNotation))
+ }
+
+ private ContractDownloader contractDownloader(ContractVerifierExtension extension, StubConfiguration configuration) {
+ return new ContractDownloader(stubDownloader(extension), configuration,
+ extension.contractsPath, this.project.group as String, this.project.name)
+ }
+
+ private AetherStubDownloader stubDownloader(ContractVerifierExtension extension) {
+ return new AetherStubDownloader(
+ new StubRunnerOptionsBuilder()
+ .withStubRepositoryRoot(extension.contractsRepositoryUrl)
+ .withWorkOffline(extension.contractsWorkOffline)
+ .build())
+ }
+
+ private StubConfiguration stubConfiguration(ContractVerifierExtension.Dependency contractDependency) {
+ String groupId = contractDependency.groupId
+ String artifactId = contractDependency.artifactId
+ String version = StringUtils.hasText(contractDependency.version) ?
+ contractDependency.version : LATEST_VERSION
+ String classifier = contractDependency.classifier
+ String stringNotation = contractDependency.stringNotation
+ if (StringUtils.hasText(stringNotation)) {
+ StubConfiguration stubConfiguration = new StubConfiguration(stringNotation)
+ return new StubConfiguration(stubConfiguration.groupId, stubConfiguration.artifactId,
+ stubConfiguration.version, contractDependency.classifier)
+ }
+ return new StubConfiguration(groupId, artifactId, version, classifier)
+ }
+}
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.groovy
index d8edc0944a..d228a6374c 100644
--- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.groovy
+++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/main/groovy/org/springframework/cloud/contract/verifier/plugin/SpringCloudContractVerifierGradlePlugin.groovy
@@ -24,7 +24,6 @@ import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
import org.gradle.api.tasks.Copy
import org.gradle.jvm.tasks.Jar
-import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
/**
* Gradle plugin for Spring Cloud Contract Verifier that from the DSL contract can
*
@@ -62,14 +61,15 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin {
void apply(Project project) {
this.project = project
project.plugins.apply(GroovyPlugin)
- ContractVerifierConfigProperties extension = project.extensions.create(EXTENSION_NAME, ContractVerifierConfigProperties)
+ ContractVerifierExtension extension = project.extensions.create(EXTENSION_NAME, ContractVerifierExtension)
+ GradleContractsDownloader downloader = new GradleContractsDownloader(this.project, this.project.logger)
project.check.dependsOn(GENERATE_SERVER_TESTS_TASK_NAME)
setConfigurationDefaults(extension)
- createGenerateTestsTask(extension)
- createAndConfigureGenerateWireMockClientStubsFromDslTask(extension)
+ createGenerateTestsTask(downloader, extension)
+ createAndConfigureGenerateWireMockClientStubsFromDslTask(downloader, extension)
Task stubsJar = createAndConfigureStubsJarTasks(extension)
- createAndConfigureCopyContractsTask(stubsJar, extension)
- createAndConfigureMavenPublishPlugin(stubsJar, extension)
+ createAndConfigureCopyContractsTask(stubsJar, downloader, extension)
+ createAndConfigureMavenPublishPlugin(stubsJar)
addProjectDependencies(project)
addIdeaTestSources(project, extension)
}
@@ -94,11 +94,11 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin {
project.dependencies.add("testCompile", "org.assertj:assertj-core:2.3.0")
}
- private void setConfigurationDefaults(ContractVerifierConfigProperties extension) {
+ private void setConfigurationDefaults(ContractVerifierExtension extension) {
extension.with {
- generatedTestSourcesDir = project.file("${project.buildDir}/generated-test-sources/contracts")
- contractsDslDir = defaultContractsDir() //TODO: Use sourceset
- basePackageForTests = 'org.springframework.cloud.contract.verifier.tests'
+ generatedTestSourcesDir = generatedTestSourcesDir ?: project.file("${project.buildDir}/generated-test-sources/contracts")
+ contractsDslDir = contractsDslDir ?: defaultContractsDir() //TODO: Use sourceset
+ basePackageForTests = basePackageForTests ?: 'org.springframework.cloud.contract.verifier.tests'
stubsOutputDir = stubsOutputDir ?: project.file("${project.buildDir}/stubs")
}
}
@@ -107,29 +107,31 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin {
return project.file("${project.rootDir}/src/test/resources/contracts")
}
- private void createGenerateTestsTask(ContractVerifierConfigProperties extension) {
+ private void createGenerateTestsTask(GradleContractsDownloader downloader,
+ ContractVerifierExtension extension) {
Task task = project.tasks.create(GENERATE_SERVER_TESTS_TASK_NAME, GenerateServerTestsTask)
task.description = "Generate server tests from the contracts"
task.group = GROUP_NAME
task.conventionMapping.with {
- contractsDslDir = { extension.contractsDslDir }
+ contractsDslDir = { downloader.downloadAndUnpackContractsIfRequired(extension) }
generatedTestSourcesDir = { extension.generatedTestSourcesDir }
configProperties = { extension }
}
}
- private void createAndConfigureGenerateWireMockClientStubsFromDslTask(ContractVerifierConfigProperties extension) {
+ private void createAndConfigureGenerateWireMockClientStubsFromDslTask(
+ GradleContractsDownloader downloader, ContractVerifierExtension extension) {
Task task = project.tasks.create(DSL_TO_WIREMOCK_CLIENT_TASK_NAME, GenerateWireMockClientStubsFromDslTask)
task.description = "Generate WireMock client stubs from the contracts"
task.group = GROUP_NAME
task.conventionMapping.with {
- contractsDslDir = { extension.contractsDslDir }
+ contractsDslDir = { downloader.downloadAndUnpackContractsIfRequired(extension) }
stubsOutputDir = { extension.stubsOutputDir }
configProperties = { extension }
}
}
- private Task createAndConfigureStubsJarTasks(ContractVerifierConfigProperties extension) {
+ private Task createAndConfigureStubsJarTasks(ContractVerifierExtension extension) {
Task task = stubsTask()
if (task) {
project.logger.info("Spring Cloud Contract Verifier Plugin: Stubs jar task was present - won't create one. Remember about adding it to artifacts as an archive!")
@@ -158,9 +160,11 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin {
}
}
- private Task createAndConfigureCopyContractsTask(Task stubs, ContractVerifierConfigProperties extension) {
+ private Task createAndConfigureCopyContractsTask(Task stubs,
+ GradleContractsDownloader downloader,
+ ContractVerifierExtension extension) {
Task task = project.tasks.create(type: Copy, name: COPY_CONTRACTS_TASK_NAME) {
- from { extension.contractsDslDir }
+ from { downloader.downloadAndUnpackContractsIfRequired(extension) }
into { extension.stubsOutputDir != null ?
project.file("${extension.stubsOutputDir}/contracts") : project.file("${project.buildDir}/stubs/contracts") }
}
@@ -170,7 +174,7 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin {
return task
}
- private void createAndConfigureMavenPublishPlugin(Task stubsTask, ContractVerifierConfigProperties extension) {
+ private void createAndConfigureMavenPublishPlugin(Task stubsTask) {
if (!classIsOnClasspath("org.gradle.api.publish.maven.plugins.MavenPublishPlugin")) {
project.logger.debug("Maven Publish Plugin is not present - won't add default publication")
return
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierIntegrationSpec.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierIntegrationSpec.groovy
index 722be177a3..ced185b132 100644
--- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierIntegrationSpec.groovy
+++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierIntegrationSpec.groovy
@@ -91,7 +91,7 @@ abstract class ContractVerifierIntegrationSpec extends Specification {
}
protected String[] checkAndPublishToMavenLocal() {
- String[] args = ["check", "publishToMavenLocal", "--info"] as String[]
+ String[] args = ["check", "publishToMavenLocal", "--info", "--stacktrace"] as String[]
if (WORK_OFFLINE) args << "--offline"
return args
}
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierSpec.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierSpec.groovy
index 53af60f5b2..e9f6a3f0ad 100644
--- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierSpec.groovy
+++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/groovy/org/springframework/cloud/contract/verifier/plugin/ContractVerifierSpec.groovy
@@ -6,7 +6,6 @@ import org.gradle.api.plugins.GroovyPlugin
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
import org.gradle.testfixtures.ProjectBuilder
-import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import spock.lang.Specification
class ContractVerifierSpec extends Specification {
@@ -32,7 +31,7 @@ class ContractVerifierSpec extends Specification {
project.plugins.apply(SpringCloudContractVerifierGradlePlugin)
expect:
- project.extensions.findByType(ContractVerifierConfigProperties) != null
+ project.extensions.findByType(ContractVerifierExtension) != null
}
def "should create generateContractTests task"() {
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle
index 099e9c5da0..7269ee6a6a 100644
--- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle
+++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle
@@ -52,24 +52,10 @@ subprojects {
configure([project(':fraudDetectionService'), project(':loanApplicationService')]) {
apply plugin: 'spring-boot'
- apply plugin: 'spring-cloud-contract'
apply plugin: 'maven-publish'
- ext {
- contractsDir = file("mappings")
- stubsOutputDirRoot = file("${project.buildDir}/production/${project.name}-stubs/")
- }
ext['jetty.version'] = '9.2.17.v20160517'
- contracts {
- targetFramework = 'Spock'
- testMode = 'JaxRsClient'
- baseClassForTests = 'org.springframework.cloud.MvcSpec'
- contractsDslDir = file("${project.projectDir.absolutePath}/mappings/")
- generatedTestSourcesDir = file("${project.buildDir}/generated-test-sources/")
- stubsOutputDir = stubsOutputDirRoot
- }
-
jar {
version = '0.0.1'
}
@@ -114,16 +100,38 @@ configure([project(':fraudDetectionService'), project(':loanApplicationService')
configure(project(':fraudDetectionService')) {
test.dependsOn('generateWireMockClientStubs')
+
+ apply plugin: 'spring-cloud-contract'
+
+ ext {
+ contractsDir = file("mappings")
+ stubsOutputDirRoot = file("${project.buildDir}/production/${project.name}-stubs/")
+ }
+ ext['jetty.version'] = '9.2.17.v20160517'
+
+ contracts {
+ targetFramework = 'Spock'
+ testMode = 'JaxRsClient'
+ baseClassForTests = 'org.springframework.cloud.MvcSpec'
+ contractsRepositoryUrl = "file://" + file("${project.rootDir.absolutePath}/m2repo/repository").absolutePath
+ contractDependency {
+ stringNotation = "com.example:jersey-contracts"
+ }
+ generatedTestSourcesDir = file("${project.buildDir}/generated-test-sources/")
+ stubsOutputDir = stubsOutputDirRoot
+ }
}
configure(project(':loanApplicationService')) {
task copyCollaboratorStubs(type: Copy) {
File fraudBuildDir = project(':fraudDetectionService').buildDir
- from(new File(fraudBuildDir, "/production/${project(':fraudDetectionService').name}-stubs/"))
- into "src/test/resources/"
+ from(new File(fraudBuildDir, "/production/${project(':fraudDetectionService').name}-stubs/")) {
+ include '**/*.json'
+ }
+ into "src/test/resources/mappings"
}
- generateContractTests.dependsOn('copyCollaboratorStubs')
+ test.dependsOn('copyCollaboratorStubs')
}
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy
deleted file mode 100644
index 6c3106b3f0..0000000000
--- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsFraud.groovy
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2013-2016 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import org.springframework.cloud.contract.spec.Contract
-
-Contract.make {
- request {
- method """PUT"""
- url """/fraudcheck"""
- body("""
- {
- "clientPesel":"${value(consumer(regex('[0-9]{10}')), producer('1234567890'))}",
- "loanAmount":99999}
- """
- )
- headers {
- header("""Content-Type""", """application/vnd.fraud.v1+json""")
- }
-
- }
- response {
- status 200
- body( """{
- "fraudCheckStatus": "${value(consumer('FRAUD'), producer(regex('[A-Z]{5}')))}",
- "rejectionReason": "Amount too high"
-}""")
- headers {
- header('Content-Type': value(
- producer(regex('application/vnd.fraud.v1.json.*')),
- consumer('application/vnd.fraud.v1+json'))
- )
- }
- }
-
-}
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy
deleted file mode 100644
index 00d5236743..0000000000
--- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/fraudDetectionService/mappings/fraudDetectionService/shouldMarkClientAsNotFraud.groovy
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2013-2016 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import org.springframework.cloud.contract.spec.Contract
-
-Contract.make {
- request {
- method 'PUT'
- url '/fraudcheck'
- body("""
- {
- "clientPesel":"${value(consumer(regex('[0-9]{10}')), producer('1234567890'))}",
- "loanAmount":123.123
- }
- """
- )
- headers {
- header('Content-Type', 'application/vnd.fraud.v1+json')
- }
-
- }
- response {
- status 200
- body(
- fraudCheckStatus: "OK",
- rejectionReason: $(consumer(null), producer(execute('assertThatRejectionReasonIsNull($it)')))
- )
- headers {
- header('Content-Type': value(
- producer(regex('application/vnd.fraud.v1.json.*')),
- consumer('application/vnd.fraud.v1+json'))
- )
- }
- }
-
-}
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/m2repo/repository/com/example/jersey-contracts/0.0.1-SNAPSHOT/jersey-contracts-0.0.1-SNAPSHOT.jar b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/m2repo/repository/com/example/jersey-contracts/0.0.1-SNAPSHOT/jersey-contracts-0.0.1-SNAPSHOT.jar
new file mode 100644
index 0000000000..93a77ae409
Binary files /dev/null and b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/m2repo/repository/com/example/jersey-contracts/0.0.1-SNAPSHOT/jersey-contracts-0.0.1-SNAPSHOT.jar differ
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/m2repo/repository/com/example/jersey-contracts/0.0.1-SNAPSHOT/jersey-contracts-0.0.1-SNAPSHOT.pom b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/m2repo/repository/com/example/jersey-contracts/0.0.1-SNAPSHOT/jersey-contracts-0.0.1-SNAPSHOT.pom
new file mode 100644
index 0000000000..79bb66d076
--- /dev/null
+++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/m2repo/repository/com/example/jersey-contracts/0.0.1-SNAPSHOT/jersey-contracts-0.0.1-SNAPSHOT.pom
@@ -0,0 +1,25 @@
+
+
+
+
+ 4.0.0
+ com.example
+ jersey-contracts
+ 0.0.1-SNAPSHOT
+ pom
+
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/m2repo/repository/com/example/jersey-contracts/0.0.1-SNAPSHOT/maven-metadata-local.xml b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/m2repo/repository/com/example/jersey-contracts/0.0.1-SNAPSHOT/maven-metadata-local.xml
new file mode 100644
index 0000000000..05d9ce3299
--- /dev/null
+++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/m2repo/repository/com/example/jersey-contracts/0.0.1-SNAPSHOT/maven-metadata-local.xml
@@ -0,0 +1,24 @@
+
+
+ com.example
+ jersey-contracts
+ 0.0.1-SNAPSHOT
+
+
+ true
+
+ 20160916125313
+
+
+ jar
+ 0.0.1-SNAPSHOT
+ 20160916125313
+
+
+ pom
+ 0.0.1-SNAPSHOT
+ 20160916125313
+
+
+
+
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/m2repo/repository/com/example/jersey-contracts/maven-metadata.xml b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/m2repo/repository/com/example/jersey-contracts/maven-metadata.xml
new file mode 100644
index 0000000000..b3827fc5c5
--- /dev/null
+++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/m2repo/repository/com/example/jersey-contracts/maven-metadata.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ com.example
+ jersey-contracts
+ 0.0.1-SNAPSHOT
+
+
+ 0.0.1-SNAPSHOT
+
+ 20160409062112
+
+
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/ConvertMojo.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/ConvertMojo.java
index bbe5b76027..006e59128c 100644
--- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/ConvertMojo.java
+++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/ConvertMojo.java
@@ -16,8 +16,10 @@
package org.springframework.cloud.contract.maven.verifier;
import java.io.File;
+import javax.inject.Inject;
import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
@@ -27,6 +29,8 @@ import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.filtering.MavenResourcesFiltering;
+import org.eclipse.aether.RepositorySystemSession;
+import org.springframework.cloud.contract.maven.verifier.stubrunner.AetherStubDownloaderFactory;
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties;
import org.springframework.cloud.contract.verifier.wiremock.DslToWireMockClientConverter;
import org.springframework.cloud.contract.verifier.wiremock.RecursiveFilesConverter;
@@ -40,6 +44,9 @@ import org.springframework.cloud.contract.verifier.wiremock.RecursiveFilesConver
defaultPhase = LifecyclePhase.PROCESS_TEST_RESOURCES)
public class ConvertMojo extends AbstractMojo {
+ @Parameter(defaultValue = "${repositorySystemSession}", readonly = true)
+ private RepositorySystemSession repoSession;
+
/**
* Directory containing Spring Cloud Contract Verifier contracts written using the GroovyDSL
*/
@@ -72,9 +79,43 @@ public class ConvertMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", readonly = true) private MavenProject project;
+ /**
+ * The URL from which a JAR containing the contracts should get downloaded. If not provided
+ * but artifactid / coordinates notation was provided then the current Maven's build repositories will be
+ * taken into consideration
+ */
+ @Parameter(property = "contractsRepositoryUrl")
+ private String contractsRepositoryUrl;
+
+ @Parameter(property = "contractDependency")
+ private Dependency contractDependency;
+
+ /**
+ * The path in the JAR with all the contracts where contracts for this particular service lay.
+ * If not provided will be resolved to {@code groupid/artifactid}. Example:
+ *
+ * If {@code groupid} is {@code com.example} and {@code artifactid} is {@code service} then the resolved path will be
+ * {@code /com/example/artifactid}
+ */
+ @Parameter(property = "contractsPath")
+ private String contractsPath;
+
+ /**
+ * If {@code true} then JAR with contracts will be taken from local maven repository
+ */
+ @Parameter(property = "contractsWorkOffline", defaultValue = "false")
+ private boolean contractsWorkOffline;
+
@Component(role = MavenResourcesFiltering.class, hint = "default")
private MavenResourcesFiltering mavenResourcesFiltering;
+ private final AetherStubDownloaderFactory aetherStubDownloaderFactory;
+
+ @Inject
+ public ConvertMojo(AetherStubDownloaderFactory aetherStubDownloaderFactory) {
+ this.aetherStubDownloaderFactory = aetherStubDownloaderFactory;
+ }
+
public void execute() throws MojoExecutionException, MojoFailureException {
if (this.skip) {
@@ -83,12 +124,18 @@ public class ConvertMojo extends AbstractMojo {
this.skip));
return;
}
+ // download contracts, unzip them and pass as output directory
+ ContractVerifierConfigProperties config = new ContractVerifierConfigProperties();
+ File contractsDirectory = new MavenContractsDownloader(this.project, this.contractDependency,
+ this.contractsPath, this.contractsRepositoryUrl, this.contractsWorkOffline, getLog(),
+ this.aetherStubDownloaderFactory, this.repoSession).downloadAndUnpackContractsIfRequired(config, this.contractsDirectory);
+ getLog().info("Directory with contract is present at [" + contractsDirectory + "]");
new CopyContracts(this.project, this.mavenSession, this.mavenResourcesFiltering)
- .copy(this.contractsDirectory, this.outputDirectory);
+ .copy(contractsDirectory, this.outputDirectory);
- final ContractVerifierConfigProperties config = new ContractVerifierConfigProperties();
- config.setContractsDslDir(isInsideProject() ? this.contractsDirectory : this.source);
+
+ config.setContractsDslDir(isInsideProject() ? contractsDirectory : this.source);
config.setStubsOutputDir(
isInsideProject() ? new File(this.outputDirectory, "mappings") : this.destination);
@@ -100,6 +147,7 @@ public class ConvertMojo extends AbstractMojo {
getLog().info(String.format("WireMock stubs mappings directory: %s",
config.getStubsOutputDir()));
+
RecursiveFilesConverter converter = new RecursiveFilesConverter(
new DslToWireMockClientConverter(), config);
converter.processFiles();
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateStubsMojo.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateStubsMojo.java
index 98a14ca83f..88de449039 100644
--- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateStubsMojo.java
+++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateStubsMojo.java
@@ -29,6 +29,9 @@ import org.apache.maven.project.MavenProjectHelper;
import org.codehaus.plexus.archiver.Archiver;
import org.codehaus.plexus.archiver.jar.JarArchiver;
+/**
+ * Picks the converted .json files and creates a jar. Requires convert to be executed first
+ */
@Mojo(name = "generateStubs", defaultPhase = LifecyclePhase.PACKAGE,
requiresProject = true)
public class GenerateStubsMojo extends AbstractMojo {
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateTestsMojo.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateTestsMojo.java
index e43ab91d6b..b041994013 100644
--- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateTestsMojo.java
+++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateTestsMojo.java
@@ -18,6 +18,9 @@ package org.springframework.cloud.contract.maven.verifier;
import java.io.File;
import java.util.List;
+import javax.inject.Inject;
+
+import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
@@ -26,6 +29,8 @@ import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
+import org.eclipse.aether.RepositorySystemSession;
+import org.springframework.cloud.contract.maven.verifier.stubrunner.AetherStubDownloaderFactory;
import org.springframework.cloud.contract.spec.ContractVerifierException;
import org.springframework.cloud.contract.verifier.TestGenerator;
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties;
@@ -36,6 +41,9 @@ import org.springframework.cloud.contract.verifier.config.TestMode;
requiresDependencyResolution = ResolutionScope.TEST)
public class GenerateTestsMojo extends AbstractMojo {
+ @Parameter(defaultValue = "${repositorySystemSession}", readonly = true)
+ private RepositorySystemSession repoSession;
+
@Parameter(property = "spring.cloud.contract.verifier.contractsDirectory",
defaultValue = "${project.basedir}/src/test/resources/contracts")
private File contractsDirectory;
@@ -105,6 +113,40 @@ public class GenerateTestsMojo extends AbstractMojo {
@Parameter(property = "skipTests", defaultValue = "false") private boolean skipTests;
+ /**
+ * The URL from which a JAR containing the contracts should get downloaded. If not provided
+ * but artifactid / coordinates notation was provided then the current Maven's build repositories will be
+ * taken into consideration
+ */
+ @Parameter(property = "contractsRepositoryUrl")
+ private String contractsRepositoryUrl;
+
+ @Parameter(property = "contractDependency")
+ private Dependency contractDependency;
+
+ /**
+ * The path in the JAR with all the contracts where contracts for this particular service lay.
+ * If not provided will be resolved to {@code groupid/artifactid}. Example:
+ *
+ * If {@code groupid} is {@code com.example} and {@code artifactid} is {@code service} then the resolved path will be
+ * {@code /com/example/artifactid}
+ */
+ @Parameter(property = "contractsPath")
+ private String contractsPath;
+
+ /**
+ * If {@code true} then JAR with contracts will be taken from local maven repository
+ */
+ @Parameter(property = "contractsWorkOffline", defaultValue = "false")
+ private boolean contractsWorkOffline;
+
+ private final AetherStubDownloaderFactory aetherStubDownloaderFactory;
+
+ @Inject
+ public GenerateTestsMojo(AetherStubDownloaderFactory aetherStubDownloaderFactory) {
+ this.aetherStubDownloaderFactory = aetherStubDownloaderFactory;
+ }
+
public void execute() throws MojoExecutionException, MojoFailureException {
if (this.skip || this.mavenTestSkip || this.skipTests) {
if (this.skip) getLog().info("Skipping Spring Cloud Contract Verifier execution: spring.cloud.contract.verifier.skip=" + this.skip);
@@ -115,7 +157,12 @@ public class GenerateTestsMojo extends AbstractMojo {
getLog().info(
"Generating server tests source code for Spring Cloud Contract Verifier contract verification");
final ContractVerifierConfigProperties config = new ContractVerifierConfigProperties();
- config.setContractsDslDir(this.contractsDirectory);
+ // download contracts, unzip them and pass as output directory
+ File contractsDirectory = new MavenContractsDownloader(this.project, this.contractDependency,
+ this.contractsPath, this.contractsRepositoryUrl, this.contractsWorkOffline, getLog(),
+ this.aetherStubDownloaderFactory, this.repoSession).downloadAndUnpackContractsIfRequired(config, this.contractsDirectory);
+ getLog().info("Directory with contract is present at [" + contractsDirectory + "]");
+ config.setContractsDslDir(contractsDirectory);
config.setGeneratedTestSourcesDir(this.generatedTestSourcesDir);
config.setTargetFramework(this.testFramework);
config.setTestMode(this.testMode);
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/MavenContractsDownloader.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/MavenContractsDownloader.java
new file mode 100644
index 0000000000..26e0ed88da
--- /dev/null
+++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/MavenContractsDownloader.java
@@ -0,0 +1,102 @@
+package org.springframework.cloud.contract.maven.verifier;
+
+import java.io.File;
+
+import org.apache.maven.model.Dependency;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.aether.RepositorySystemSession;
+import org.springframework.cloud.contract.maven.verifier.stubrunner.AetherStubDownloaderFactory;
+import org.springframework.cloud.contract.stubrunner.AetherStubDownloader;
+import org.springframework.cloud.contract.stubrunner.ContractDownloader;
+import org.springframework.cloud.contract.stubrunner.StubConfiguration;
+import org.springframework.cloud.contract.stubrunner.StubRunnerOptionsBuilder;
+import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties;
+import org.springframework.util.StringUtils;
+
+/**
+ * Downloads JAR with contracts
+ *
+ * @author Marcin Grzejszczak
+ * @since 1.0.0
+ */
+class MavenContractsDownloader {
+
+ private static final String LATEST_VERSION = "+";
+ private static final String CONTRACTS_DIRECTORY_PROP = "CONTRACTS_DIRECTORY";
+
+ private final MavenProject project;
+ private final Dependency contractDependency;
+ private final String contractsPath;
+ private final String contractsRepositoryUrl;
+ private final boolean contractsWorkOffline;
+ private final Log log;
+ private final AetherStubDownloaderFactory aetherStubDownloaderFactory;
+ private final RepositorySystemSession repoSession;
+
+ MavenContractsDownloader(MavenProject project, Dependency contractDependency,
+ String contractsPath, String contractsRepositoryUrl,
+ boolean contractsWorkOffline, Log log,
+ AetherStubDownloaderFactory aetherStubDownloaderFactory,
+ RepositorySystemSession repoSession) {
+ this.project = project;
+ this.contractDependency = contractDependency;
+ this.contractsPath = contractsPath;
+ this.contractsRepositoryUrl = contractsRepositoryUrl;
+ this.contractsWorkOffline = contractsWorkOffline;
+ this.log = log;
+ this.aetherStubDownloaderFactory = aetherStubDownloaderFactory;
+ this.repoSession = repoSession;
+ }
+
+ File downloadAndUnpackContractsIfRequired(ContractVerifierConfigProperties config, File defaultContractsDir) {
+ String contractsDirFromProp = this.project.getProperties().getProperty(CONTRACTS_DIRECTORY_PROP);
+ File downloadedContractsDir = StringUtils.hasText(contractsDirFromProp) ?
+ new File(contractsDirFromProp) : null;
+ // reuse downloaded contracts from another mojo
+ if (downloadedContractsDir != null && downloadedContractsDir.exists()) {
+ this.log.info("Another mojo has downloaded the contracts - will reuse them from [" + downloadedContractsDir + "]");
+ contractDownloader().updatePropertiesWithInclusion(downloadedContractsDir, config);
+ return downloadedContractsDir;
+ } else if (shouldDownloadContracts()) {
+ this.log.info("Download dependency is provided - will download contract jars");
+ File downloadedContracts = contractDownloader().unpackedDownloadedContracts(config);
+ this.project.getProperties().setProperty(CONTRACTS_DIRECTORY_PROP, downloadedContracts.getAbsolutePath());
+ return downloadedContracts;
+ }
+ this.log.info("Will use contracts provided in the folder [" + defaultContractsDir + "]");
+ return defaultContractsDir;
+ }
+
+ private boolean shouldDownloadContracts() {
+ return this.contractDependency != null && StringUtils.hasText(this.contractDependency.getArtifactId());
+ }
+
+ private ContractDownloader contractDownloader() {
+ return new ContractDownloader(stubDownloader(), stubConfiguration(),
+ this.contractsPath, this.project.getGroupId(), this.project.getArtifactId());
+ }
+
+ private AetherStubDownloader stubDownloader() {
+ if (StringUtils.hasText(this.contractsRepositoryUrl) || this.contractsWorkOffline) {
+ this.log.info("Will download contracts from [" + this.contractsRepositoryUrl + "]. "
+ + "Work offline switch equals to [" + this.contractsWorkOffline + "]");
+ return new AetherStubDownloader(
+ new StubRunnerOptionsBuilder()
+ .withStubRepositoryRoot(this.contractsRepositoryUrl)
+ .withWorkOffline(this.contractsWorkOffline)
+ .build());
+ }
+ this.log.info("Will download contracts using current build's Maven repository setup");
+ return this.aetherStubDownloaderFactory.build(this.repoSession);
+ }
+
+ private StubConfiguration stubConfiguration() {
+ String groupId = this.contractDependency.getGroupId();
+ String artifactId = this.contractDependency.getArtifactId();
+ String version = StringUtils.hasText(this.contractDependency.getVersion()) ?
+ this.contractDependency.getVersion() : LATEST_VERSION;
+ String classifier = this.contractDependency.getClassifier();
+ return new StubConfiguration(groupId, artifactId, version, classifier);
+ }
+}
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/java/org/springframework/cloud/contract/maven/verifier/PluginUnitTest.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/java/org/springframework/cloud/contract/maven/verifier/PluginUnitTest.java
index 24a5f96b9e..4ecceb79b4 100644
--- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/java/org/springframework/cloud/contract/maven/verifier/PluginUnitTest.java
+++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/java/org/springframework/cloud/contract/maven/verifier/PluginUnitTest.java
@@ -134,5 +134,32 @@ public class PluginUnitTest {
assertFilesPresent(basedir, "target/sample-project-0.1-foo.jar");
}
+ @Test
+ public void shouldGenerateStubsByDownloadingContractsFromARepo() throws Exception {
+ File basedir = this.resources.getBasedir("basic-remote-contracts");
+ this.maven.executeMojo(basedir, "convert", newParameter("contractsRepositoryUrl", "file://" + PluginUnitTest.class.getClassLoader().getResource("m2repo/repository").getFile()));
+ assertFilesPresent(basedir, "target/stubs/mappings/com/example/server/client1/contracts/shouldMarkClientAsFraud.json");
+ }
+
+ @Test
+ public void shouldGenerateStubsByDownloadingContractsFromARepoWhenCustomPathIsProvided() throws Exception {
+ File basedir = this.resources.getBasedir("complex-remote-contracts");
+ this.maven.executeMojo(basedir, "convert", newParameter("contractsRepositoryUrl", "file://" + PluginUnitTest.class.getClassLoader().getResource("m2repo/repository").getFile()));
+ assertFilesPresent(basedir, "target/stubs/mappings/com/example/server/client1/contracts/shouldMarkClientAsFraud.json");
+ }
+
+ @Test
+ public void shouldGenerateTestsByDownloadingContractsFromARepo() throws Exception {
+ File basedir = this.resources.getBasedir("basic-remote-contracts");
+ this.maven.executeMojo(basedir, "generateTests", newParameter("contractsRepositoryUrl", "file://" + PluginUnitTest.class.getClassLoader().getResource("m2repo/repository").getFile()));
+ assertFilesPresent(basedir, "target/generated-test-sources/contracts/org/springframework/cloud/contract/verifier/tests/com/example/server/client1/ContractsTest.java");
+ }
+
+ @Test
+ public void shouldGenerateTestsByDownloadingContractsFromARepoWhenCustomPathIsProvided() throws Exception {
+ File basedir = this.resources.getBasedir("complex-remote-contracts");
+ this.maven.executeMojo(basedir, "generateTests", newParameter("contractsRepositoryUrl", "file://" + PluginUnitTest.class.getClassLoader().getResource("m2repo/repository").getFile()));
+ assertFilesPresent(basedir, "target/generated-test-sources/contracts/org/springframework/cloud/contract/verifier/tests/com/example/server/client1/ContractsTest.java");
+ }
}
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic-remote-contracts/pom-with-repo.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic-remote-contracts/pom-with-repo.xml
new file mode 100644
index 0000000000..9efa5fd96e
--- /dev/null
+++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic-remote-contracts/pom-with-repo.xml
@@ -0,0 +1,46 @@
+
+
+
+ 4.0.0
+
+ com.example
+ server
+ 0.1.BUILD-SNAPSHOT
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-contract-maven-plugin
+
+ http://link/to/your/nexus/or/artifactory/or/sth
+
+ com.example.standalone
+ contracts
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic-remote-contracts/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic-remote-contracts/pom.xml
new file mode 100644
index 0000000000..02a87e7a01
--- /dev/null
+++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/basic-remote-contracts/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+ 4.0.0
+
+ com.example
+ server
+ 0.1.BUILD-SNAPSHOT
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-contract-maven-plugin
+
+
+ com.example
+ contracts
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-remote-contracts/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-remote-contracts/pom.xml
new file mode 100644
index 0000000000..19b4b2ef8c
--- /dev/null
+++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/complex-remote-contracts/pom.xml
@@ -0,0 +1,46 @@
+
+
+
+ 4.0.0
+
+ com.example
+ someartifact
+ 0.1.BUILD-SNAPSHOT
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-contract-maven-plugin
+ 1.0.0.BUILD-SNAPSHOT
+
+ com/example/server
+
+ com.example
+ contracts
+ +
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/logback.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/logback.xml
new file mode 100644
index 0000000000..df6a2daa72
--- /dev/null
+++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/logback.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/contracts/0.0.1-SNAPSHOT/contracts-0.0.1-SNAPSHOT.jar b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/contracts/0.0.1-SNAPSHOT/contracts-0.0.1-SNAPSHOT.jar
new file mode 100644
index 0000000000..4e3d44862b
Binary files /dev/null and b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/contracts/0.0.1-SNAPSHOT/contracts-0.0.1-SNAPSHOT.jar differ
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/contracts/0.0.1-SNAPSHOT/contracts-0.0.1-SNAPSHOT.pom b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/contracts/0.0.1-SNAPSHOT/contracts-0.0.1-SNAPSHOT.pom
new file mode 100644
index 0000000000..3101d07b79
--- /dev/null
+++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/contracts/0.0.1-SNAPSHOT/contracts-0.0.1-SNAPSHOT.pom
@@ -0,0 +1,25 @@
+
+
+
+
+ 4.0.0
+ com.example
+ contracts
+ 0.0.1-SNAPSHOT
+ pom
+
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/contracts/0.0.1-SNAPSHOT/maven-metadata-local.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/contracts/0.0.1-SNAPSHOT/maven-metadata-local.xml
new file mode 100644
index 0000000000..3b4511d3ba
--- /dev/null
+++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/contracts/0.0.1-SNAPSHOT/maven-metadata-local.xml
@@ -0,0 +1,24 @@
+
+
+ com.example
+ contracts
+ 0.0.1-SNAPSHOT
+
+
+ true
+
+ 20160916125313
+
+
+ jar
+ 0.0.1-SNAPSHOT
+ 20160916125313
+
+
+ pom
+ 0.0.1-SNAPSHOT
+ 20160916125313
+
+
+
+
diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/contracts/maven-metadata.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/contracts/maven-metadata.xml
new file mode 100644
index 0000000000..3d1d3547d5
--- /dev/null
+++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/resources/m2repo/repository/com/example/contracts/maven-metadata.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ com.example
+ contracts
+ 0.0.1-SNAPSHOT
+
+
+ 0.0.1-SNAPSHOT
+
+ 20160409062112
+
+
diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/ContractVerifierConfigProperties.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/ContractVerifierConfigProperties.groovy
index 6e34af897e..9529f50910 100644
--- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/ContractVerifierConfigProperties.groovy
+++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/config/ContractVerifierConfigProperties.groovy
@@ -102,4 +102,11 @@ class ContractVerifierConfigProperties {
*/
Boolean assertJsonSize = false
+ /**
+ * A regular expression that matches contracts. Especially useful when using a single JAR containing
+ * all the contracts in the system. In this case you'd like to take into consideration only some of them.
+ * Defaults to picking all files.
+ */
+ String includedContracts = ".*"
+
}
diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScanner.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScanner.groovy
index e1af75de6a..f7d2b420d1 100755
--- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScanner.groovy
+++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScanner.groovy
@@ -16,19 +16,17 @@
package org.springframework.cloud.contract.verifier.file
+import com.google.common.collect.ArrayListMultimap
+import com.google.common.collect.ListMultimap
import groovy.transform.CompileStatic
+import groovy.util.logging.Slf4j
+import org.apache.commons.lang3.SystemUtils
import java.nio.file.FileSystem
import java.nio.file.FileSystems
import java.nio.file.Path
import java.nio.file.PathMatcher
import java.util.regex.Pattern
-
-import org.apache.commons.lang3.SystemUtils
-
-import com.google.common.collect.ArrayListMultimap
-import com.google.common.collect.ListMultimap
-
/**
* Scans the provided file path for the DSLs. There's a possibility to provide
* inclusion and exclusion filters.
@@ -38,6 +36,7 @@ import com.google.common.collect.ListMultimap
* @since 1.0.0
*/
@CompileStatic
+@Slf4j
class ContractFileScanner {
private static final String MATCH_PREFIX = "glob:"
@@ -45,11 +44,13 @@ class ContractFileScanner {
private final File baseDir
private final Set excludeMatchers
private final Set ignoreMatchers
+ private final String includeMatcher
- ContractFileScanner(File baseDir, Set excluded, Set ignored) {
+ ContractFileScanner(File baseDir, Set excluded, Set ignored, String includeMatcher = "") {
this.baseDir = baseDir
- excludeMatchers = processPatterns(excluded ?: [] as Set)
- ignoreMatchers = processPatterns(ignored ?: [] as Set)
+ this.excludeMatchers = processPatterns(excluded ?: [] as Set)
+ this.ignoreMatchers = processPatterns(ignored ?: [] as Set)
+ this.includeMatcher = includeMatcher
}
private Set processPatterns(Set patterns) {
@@ -79,8 +80,11 @@ class ContractFileScanner {
return;
}
files.sort().eachWithIndex { File file, int index ->
- if (!matchesPattern(file, excludeMatchers)) {
- if (isContractFile(file)) {
+ boolean excluded = matchesPattern(file, excludeMatchers)
+ if (!excluded) {
+ boolean contractFile = isContractFile(file);
+ boolean included = includeMatcher ? file.absolutePath.matches(includeMatcher) : true
+ if (contractFile && included) {
Path path = file.toPath()
Integer order = null
if (hasScenarioFilenamePattern(path)) {
@@ -89,6 +93,13 @@ class ContractFileScanner {
result.put(file.parentFile.toPath(), new ContractMetadata(path, matchesPattern(file, ignoreMatchers), files.size(), order))
} else {
appendRecursively(file, result)
+ if (log.isDebugEnabled()) {
+ log.debug("File [$file] is ignored. Is a contract file? [$contractFile]. Should be included by pattern? [$included]")
+ }
+ }
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug("File [$file] is ignored. Should be excluded? [$excluded]")
}
}
}
@@ -98,8 +109,8 @@ class ContractFileScanner {
return SCENARIO_STEP_FILENAME_PATTERN.matcher(path.fileName.toString()).matches()
}
- private boolean matchesPattern(File file, Set excludeMatchers) {
- for (PathMatcher matcher : excludeMatchers) {
+ private boolean matchesPattern(File file, Set matchers) {
+ for (PathMatcher matcher : matchers) {
if (matcher.matches(file.toPath())) {
return true;
}