Merge branch 'master' into 2.0.x
This commit is contained in:
@@ -124,6 +124,7 @@ contracts {
|
||||
}
|
||||
contractsPath = ''
|
||||
contractsWorkOffline = false
|
||||
cacheDownloadedContracts = true
|
||||
}
|
||||
|
||||
tasks.create(type: Jar, name: 'verifierStubsJar', dependsOn: 'generateClientStubs') {
|
||||
@@ -443,8 +444,9 @@ If you want to download your contract definitions from a Maven repository you ca
|
||||
- **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]
|
||||
- **cacheDownloadedContracts** - if you want to reuse download JARs that contain contract definitions.
|
||||
We cache only non-snapshot, explicitly provided versions (e.g. `+` or `1.0.0.BUILD-SNAPSHOT` won't get cached).
|
||||
By default this feature is turned on.
|
||||
|
||||
====== Single base class for all tests
|
||||
|
||||
|
||||
@@ -86,6 +86,10 @@ public class StubConfiguration {
|
||||
return StringUtils.hasText(this.groupId) && StringUtils.hasText(this.artifactId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a colon separated representation of the stub configuration
|
||||
* (e.g. groupid:artifactid:version:classifier)
|
||||
*/
|
||||
public String toColonSeparatedDependencyNotation() {
|
||||
if (!isDefined()) {
|
||||
return "";
|
||||
@@ -102,6 +106,13 @@ public class StubConfiguration {
|
||||
return StringUtils.hasText(value) ? value : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if ivy notation matches group and artifact ids
|
||||
*
|
||||
* @param ivyNotationAsString - e.g. group:artifact:version:classifier
|
||||
* @return {@code true} if artifact id matches and there's no group id. Or if
|
||||
* both group id and artifact id are present and matching
|
||||
*/
|
||||
public boolean groupIdAndArtifactMatches(String ivyNotationAsString) {
|
||||
String[] parts = ivyNotationFrom(ivyNotationAsString);
|
||||
String groupId = parts[0];
|
||||
@@ -112,6 +123,14 @@ public class StubConfiguration {
|
||||
return this.groupId.equals(groupId) && this.artifactId.equals(artifactId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} for a snapshot or a LATEST (+) version
|
||||
*/
|
||||
public boolean isVersionChanging() {
|
||||
return DEFAULT_VERSION.equals(this.version) ||
|
||||
this.version.toLowerCase().contains("snapshot");
|
||||
}
|
||||
|
||||
public String getGroupId() {
|
||||
return this.groupId;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
package org.springframework.cloud.contract.stubrunner
|
||||
|
||||
import io.specto.hoverfly.junit.HoverflyRule
|
||||
import org.eclipse.aether.RepositorySystemSession;
|
||||
import org.eclipse.aether.RepositorySystemSession
|
||||
import org.junit.Rule
|
||||
import org.springframework.util.ResourceUtils
|
||||
import spock.lang.IgnoreIf
|
||||
import spock.lang.Specification
|
||||
import spock.util.environment.RestoreSystemProperties
|
||||
|
||||
|
||||
class AetherStubDownloaderSpec extends Specification {
|
||||
|
||||
@Rule
|
||||
HoverflyRule hoverflyRule = HoverflyRule.inSimulationMode("simulation.json")
|
||||
|
||||
// CI tools sometimes can't reach the `test.jfrog.io` address
|
||||
@IgnoreIf({ Boolean.valueOf(env['CI']) })
|
||||
def 'Should be able to download from a repository using username and password authentication'() {
|
||||
given:
|
||||
StubRunnerOptions stubRunnerOptions = new StubRunnerOptionsBuilder()
|
||||
|
||||
@@ -45,4 +45,19 @@ class StubConfigurationSpec extends Specification {
|
||||
|
||||
}
|
||||
|
||||
@Unroll
|
||||
def 'should resolve [#ivy] as a changing version [#result]'() {
|
||||
given:
|
||||
StubConfiguration stubConfiguration = new StubConfiguration(ivy)
|
||||
expect:
|
||||
result == stubConfiguration.isVersionChanging()
|
||||
where:
|
||||
ivy || result
|
||||
'group:artifact:1.0.0.RELEASE:classifier' || false
|
||||
'group:artifact:1.0.0.BUILD-SNAPSHOT:' || true
|
||||
'group:artifact:1.0.0.SNAPSHOT' || true
|
||||
'group:artifact:+:' || true
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -141,6 +141,12 @@ class ContractVerifierExtension {
|
||||
*/
|
||||
boolean excludeBuildFolders
|
||||
|
||||
/**
|
||||
* If set to true then will cache the folder where non snapshot contract artifacts
|
||||
* got downloaded.
|
||||
*/
|
||||
boolean cacheDownloadedContracts = true
|
||||
|
||||
void contractDependency(@DelegatesTo(Dependency) Closure closure) {
|
||||
closure.delegate = contractDependency
|
||||
closure.call()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.PackageScope
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
|
||||
@@ -7,6 +8,7 @@ import org.springframework.cloud.contract.verifier.config.ContractVerifierConfig
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@PackageScope
|
||||
@CompileStatic
|
||||
class ExtensionToProperties {
|
||||
|
||||
protected static ContractVerifierConfigProperties fromExtension(ContractVerifierExtension extension) {
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
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.StubDownloader
|
||||
import org.springframework.cloud.contract.stubrunner.StubDownloaderBuilderProvider
|
||||
import org.springframework.cloud.contract.stubrunner.StubRunnerOptionsBuilder
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
import org.springframework.util.StringUtils
|
||||
@@ -15,13 +18,14 @@ import java.util.concurrent.ConcurrentHashMap
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@PackageScope
|
||||
@CompileStatic
|
||||
class GradleContractsDownloader {
|
||||
|
||||
private static final String LATEST_VERSION = '+'
|
||||
|
||||
private final Project project
|
||||
private final Logger log
|
||||
private static final Map<StubConfiguration, File> downloadedContract = new ConcurrentHashMap<>()
|
||||
protected static final Map<StubConfiguration, File> downloadedContract = new ConcurrentHashMap<>()
|
||||
|
||||
GradleContractsDownloader(Project project, Logger log) {
|
||||
this.project = project
|
||||
@@ -31,24 +35,29 @@ class GradleContractsDownloader {
|
||||
File downloadAndUnpackContractsIfRequired(ContractVerifierExtension extension,
|
||||
ContractVerifierConfigProperties config) {
|
||||
File defaultContractsDir = extension.contractsDslDir
|
||||
this.log.info("Project has group id [${this.project.group}], artifact id [${this.project.name}]")
|
||||
this.log.info("Project has group id [{}], artifact id [{}]", this.project.group, this.project.name)
|
||||
// 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")
|
||||
this.log.info("Contract dependency [{}]", extension.contractDependency)
|
||||
StubConfiguration configuration = stubConfiguration(extension.contractDependency)
|
||||
this.log.info("Got the following contract dependency to download [{}]", configuration)
|
||||
File cachedFolder = downloadedContract.get(configuration)
|
||||
if (cachedFolder) {
|
||||
this.log.info("For project [${this.project.name}] Returning the cached location of the contracts")
|
||||
contractDownloader(extension, configuration).updatePropertiesWithInclusion(cachedFolder, config)
|
||||
return cachedFolder
|
||||
this.log.info("The contract dependency is a changing one [{}] and cache download switch is set to [{}]",
|
||||
configuration.isVersionChanging(), extension.cacheDownloadedContracts)
|
||||
if (!configuration.isVersionChanging() && extension.cacheDownloadedContracts) {
|
||||
this.log.info("Resolved a non changing version - will try to return the folder from a cache")
|
||||
File cachedFolder = downloadedContract.get(configuration)
|
||||
if (cachedFolder) {
|
||||
this.log.info("For project [{}] returning the cached location of the contracts", this.project.name)
|
||||
contractDownloader(extension, configuration).updatePropertiesWithInclusion(cachedFolder, config)
|
||||
return cachedFolder
|
||||
}
|
||||
}
|
||||
File downloadedContracts = contractDownloader(extension, configuration).unpackedDownloadedContracts(config)
|
||||
downloadedContract.put(configuration, downloadedContracts)
|
||||
return downloadedContracts
|
||||
}
|
||||
this.log.info("For project [${this.project.name}] will use contracts provided in the folder [" + defaultContractsDir + "]")
|
||||
this.log.info("For project [{}] will use contracts provided in the folder [{}]", this.project.name, defaultContractsDir)
|
||||
return defaultContractsDir
|
||||
}
|
||||
|
||||
@@ -58,13 +67,14 @@ class GradleContractsDownloader {
|
||||
StringUtils.hasText(extension.contractDependency.stringNotation))
|
||||
}
|
||||
|
||||
private ContractDownloader contractDownloader(ContractVerifierExtension extension, StubConfiguration configuration) {
|
||||
protected 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(
|
||||
protected StubDownloader stubDownloader(ContractVerifierExtension extension) {
|
||||
StubDownloaderBuilderProvider provider = new StubDownloaderBuilderProvider()
|
||||
return provider.getOrDefaultDownloader(
|
||||
new StubRunnerOptionsBuilder()
|
||||
.withStubRepositoryRoot(extension.contractsRepositoryUrl)
|
||||
.withWorkOffline(extension.contractsWorkOffline)
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
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.verifier.config.ContractVerifierConfigProperties
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
@@ -8,6 +13,9 @@ import spock.lang.Specification
|
||||
*/
|
||||
class GradleContractsDownloaderSpec extends Specification {
|
||||
|
||||
Project project = Stub(Project)
|
||||
Logger logger = Stub(Logger)
|
||||
|
||||
def "should parse dependency via string notation"() {
|
||||
given:
|
||||
String stringNotation = "com.example:foo:1.0.0:stubs"
|
||||
@@ -37,6 +45,7 @@ class GradleContractsDownloaderSpec extends Specification {
|
||||
stubConfig.version == "1.0.0"
|
||||
stubConfig.classifier == "stubs"
|
||||
}
|
||||
|
||||
def "should parse dependency via string notation with methods"() {
|
||||
given:
|
||||
String stringNotation = "com.example:foo:1.0.0:stubs"
|
||||
@@ -66,4 +75,122 @@ class GradleContractsDownloaderSpec extends Specification {
|
||||
stubConfig.version == "1.0.0"
|
||||
stubConfig.classifier == "stubs"
|
||||
}
|
||||
|
||||
def "should pick dependency from cache for a non snapshot contract dependency"() {
|
||||
given:
|
||||
ContractVerifierExtension ext = new ContractVerifierExtension()
|
||||
ext.with {
|
||||
contractDependency {
|
||||
groupId("com.example")
|
||||
artifactId("foo")
|
||||
version("1.0.0")
|
||||
classifier("stubs")
|
||||
}
|
||||
contractsRepositoryUrl = "foo"
|
||||
}
|
||||
and:
|
||||
final AetherStubDownloader downloader = Mock(AetherStubDownloader)
|
||||
final ContractDownloader contractDownloader = Mock(ContractDownloader)
|
||||
and:
|
||||
def gradleDownloader = stubbedContractDownloader(downloader, contractDownloader)
|
||||
and:
|
||||
StubConfiguration expectedStubConfig = new StubConfiguration("com.example:foo:1.0.0:stubs")
|
||||
File expectedFileFromCache = new File("foo/bar")
|
||||
GradleContractsDownloader.downloadedContract.put(expectedStubConfig, expectedFileFromCache)
|
||||
when:
|
||||
File file = gradleDownloader.downloadAndUnpackContractsIfRequired(ext, new ContractVerifierConfigProperties())
|
||||
then:
|
||||
file == expectedFileFromCache
|
||||
}
|
||||
|
||||
def "should not pick dependency from cache for a non snapshot contract dependency with cache switch off"() {
|
||||
given:
|
||||
ContractVerifierExtension ext = new ContractVerifierExtension()
|
||||
ext.with {
|
||||
contractDependency {
|
||||
groupId("com.example")
|
||||
artifactId("foo")
|
||||
version("1.0.0")
|
||||
classifier("stubs")
|
||||
}
|
||||
contractsRepositoryUrl = "foo"
|
||||
cacheDownloadedContracts = false
|
||||
}
|
||||
and:
|
||||
final AetherStubDownloader downloader = Mock(AetherStubDownloader)
|
||||
final ContractDownloader contractDownloader = Mock(ContractDownloader)
|
||||
and:
|
||||
def gradleDownloader = stubbedContractDownloader(downloader, contractDownloader)
|
||||
and:
|
||||
StubConfiguration expectedStubConfig = new StubConfiguration("com.example:foo:1.0.0:stubs")
|
||||
File expectedFileFromCache = new File("foo/bar")
|
||||
GradleContractsDownloader.downloadedContract.put(expectedStubConfig, expectedFileFromCache)
|
||||
and:
|
||||
File expectedFileNotFromCache = new File("foo/bar/baz")
|
||||
contractDownloader.unpackedDownloadedContracts(_) >> expectedFileNotFromCache
|
||||
when:
|
||||
File file = gradleDownloader.downloadAndUnpackContractsIfRequired(ext, new ContractVerifierConfigProperties())
|
||||
then:
|
||||
file == expectedFileNotFromCache
|
||||
}
|
||||
|
||||
def "should not pick dependency from cache for snapshot contract dependency"() {
|
||||
given:
|
||||
ContractVerifierExtension ext = new ContractVerifierExtension()
|
||||
ext.with {
|
||||
contractDependency {
|
||||
groupId("com.example")
|
||||
artifactId("foo")
|
||||
version("1.0.0.BUILD-SNAPSHOT")
|
||||
classifier("stubs")
|
||||
}
|
||||
contractsRepositoryUrl = "foo"
|
||||
}
|
||||
and:
|
||||
final AetherStubDownloader downloader = Mock(AetherStubDownloader)
|
||||
final ContractDownloader contractDownloader = Mock(ContractDownloader)
|
||||
File expectedFileNotFromCache = new File("foo/bar/baz")
|
||||
contractDownloader.unpackedDownloadedContracts(_) >> expectedFileNotFromCache
|
||||
and:
|
||||
def gradleDownloader = stubbedContractDownloader(downloader, contractDownloader)
|
||||
when:
|
||||
File file = gradleDownloader.downloadAndUnpackContractsIfRequired(ext, new ContractVerifierConfigProperties())
|
||||
then:
|
||||
file == expectedFileNotFromCache
|
||||
}
|
||||
|
||||
private GradleContractsDownloader stubbedContractDownloader(downloader, contractDownloader) {
|
||||
new GradleContractsDownloader(project, logger) {
|
||||
@Override
|
||||
protected AetherStubDownloader stubDownloader(ContractVerifierExtension extension) {
|
||||
return downloader
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ContractDownloader contractDownloader(ContractVerifierExtension extension, StubConfiguration configuration) {
|
||||
return contractDownloader
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "should pick contract directory location from extension"() {
|
||||
given:
|
||||
ContractVerifierExtension ext = new ContractVerifierExtension()
|
||||
ext.with {
|
||||
contractsDslDir = new File("/foo/bar/baz")
|
||||
}
|
||||
and:
|
||||
final AetherStubDownloader downloader = Mock(AetherStubDownloader)
|
||||
final ContractDownloader contractDownloader = Mock(ContractDownloader)
|
||||
and:
|
||||
def gradleDownloader = stubbedContractDownloader(downloader, contractDownloader)
|
||||
and:
|
||||
StubConfiguration expectedStubConfig = new StubConfiguration("com.example:foo:1.0.0:stubs")
|
||||
GradleContractsDownloader.downloadedContract.put(expectedStubConfig, new File("foo/bar"))
|
||||
when:
|
||||
File file = gradleDownloader.downloadAndUnpackContractsIfRequired(ext, new ContractVerifierConfigProperties())
|
||||
then:
|
||||
file == new File("/foo/bar/baz")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user