Replace custom caching with Gradle's incremental build (#1187)
Replaced custom caching of the downloaded contracts with Gradle's incremental build. After that `InitContractsTask` is not needed any more, as its main purpose was to be a bridge between custom caching and Gradle's one. Simplified code to download contracts is now part of the `ContractsCopyTask`. Also fixed `PublishStubsToScmTask` and removed incremental stuff from there for now. Fixes gh-1133
This commit is contained in:
committed by
Marcin Grzejszczak
parent
cdb93f178b
commit
d4740706f0
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import org.gradle.api.tasks.Internal
|
||||
|
||||
import javax.inject.Inject
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
@@ -517,10 +519,11 @@ class ContractVerifierExtension {
|
||||
@Optional
|
||||
Property<String> proxyHost
|
||||
/**
|
||||
* If set to true then will cache the folder where non snapshot contract artifacts got downloaded.
|
||||
* Not used any more, as we switched to Gradle's incremental build.
|
||||
*/
|
||||
@Input
|
||||
Property<Boolean> cacheDownloadedContracts
|
||||
@Internal
|
||||
@Deprecated
|
||||
boolean cacheDownloadedContracts
|
||||
|
||||
@Inject
|
||||
ContractRepository(ObjectFactory objects) {
|
||||
@@ -529,7 +532,6 @@ class ContractVerifierExtension {
|
||||
this.password = objects.property(String)
|
||||
this.proxyHost = objects.property(String)
|
||||
this.proxyPort = objects.property(Integer)
|
||||
this.cacheDownloadedContracts = objects.property(Boolean).convention(true)
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -540,7 +542,6 @@ class ContractVerifierExtension {
|
||||
", password=" + password.getOrNull() +
|
||||
", proxyPort=" + proxyPort.getOrNull() +
|
||||
", proxyHost=" + proxyHost.getOrNull() +
|
||||
", cacheDownloadedContracts=" + cacheDownloadedContracts.get() +
|
||||
'}'
|
||||
}
|
||||
|
||||
@@ -576,6 +577,10 @@ class ContractVerifierExtension {
|
||||
this.proxyHost.set(proxyHost.toString())
|
||||
}
|
||||
|
||||
void setProxyPort(Integer proxyPort) {
|
||||
this.proxyPort.set(proxyPort)
|
||||
}
|
||||
|
||||
void repositoryUrl(String repositoryUrl) {
|
||||
this.repositoryUrl.set(repositoryUrl)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.Immutable
|
||||
import groovy.transform.ImmutableOptions
|
||||
import groovy.transform.PackageScope
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.DefaultTask
|
||||
@@ -26,18 +28,28 @@ import org.gradle.api.Project
|
||||
import org.gradle.api.file.CopySpec
|
||||
import org.gradle.api.file.Directory
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.gradle.api.provider.MapProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.Nested
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.api.tasks.WorkResult
|
||||
|
||||
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.spring.StubRunnerProperties
|
||||
import org.springframework.cloud.contract.verifier.converter.ToYamlConverter
|
||||
import org.springframework.util.StringUtils
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
// TODO: Convert to incremental task: https://docs.gradle.org/current/userguide/custom_tasks.html#incremental_tasks
|
||||
/**
|
||||
* Task that copies the contracts in order for the jar task to
|
||||
@@ -65,12 +77,54 @@ class ContractsCopyTask extends DefaultTask {
|
||||
Provider<Boolean> excludeBuildFolders
|
||||
@Input
|
||||
Provider<Boolean> failOnNoContracts
|
||||
@Input
|
||||
Provider<String> includedRootFolderAntPattern
|
||||
@InputDirectory
|
||||
Provider<Directory> contractsDirectory
|
||||
// All fields inside `@Nested` one are properly marked as an `@Input` to work with incremental build:
|
||||
@Nested
|
||||
@Optional
|
||||
ContractVerifierExtension.Dependency contractDependency
|
||||
@Nested
|
||||
@Optional
|
||||
ContractVerifierExtension.ContractRepository contractRepository
|
||||
@Input
|
||||
Provider<String> contractsRepository
|
||||
@Optional
|
||||
Property<StubRunnerProperties.StubsMode> contractsMode
|
||||
@Input
|
||||
Property<Boolean> deleteStubsAfterTest
|
||||
@Input
|
||||
MapProperty<String, String> contractsProperties
|
||||
@Input
|
||||
@Optional
|
||||
Property<String> contractsPath
|
||||
@Input
|
||||
@Optional
|
||||
Instant getForceDownloadOfTheLatestContracts() {
|
||||
// If we have `dynamic` version (`+` or `SNAPSHOT`) - we should mark this task as out of date for every run:
|
||||
if (shouldDownloadContracts() && getStubConfiguration().isVersionChanging()) {
|
||||
return Instant.now() // This will trigger re-download of contracts
|
||||
} else {
|
||||
return null // This will not trigger re-download of contracts
|
||||
}
|
||||
}
|
||||
@Optional
|
||||
@InputDirectory
|
||||
Provider<Directory> getContractsDirectory() {
|
||||
if (shouldDownloadContracts()) {
|
||||
return null
|
||||
} else {
|
||||
return contractsDirectory
|
||||
}
|
||||
}
|
||||
|
||||
@Internal
|
||||
boolean shouldDownloadContracts() {
|
||||
return StringUtils.hasText(contractDependency.getArtifactId().getOrNull()) ||
|
||||
StringUtils.hasText(contractDependency.getStringNotation().getOrNull()) ||
|
||||
StringUtils.hasText(contractRepository.repositoryUrl.getOrNull())
|
||||
}
|
||||
@Internal
|
||||
StubConfiguration getStubConfiguration() {
|
||||
return GradleContractsDownloaderHelper.stubConfiguration(contractDependency)
|
||||
}
|
||||
|
||||
@OutputDirectory
|
||||
DirectoryProperty copiedContractsFolder
|
||||
@@ -81,13 +135,23 @@ class ContractsCopyTask extends DefaultTask {
|
||||
|
||||
@TaskAction
|
||||
void sync() {
|
||||
File contractsDirectory = config.contractsDirectory.get().asFile
|
||||
String contractsRepository = config.contractsRepository.get()
|
||||
final DownloadedData downloadedData = downloadContractsIfNeeded()
|
||||
final File contractsDirectory
|
||||
final String antPattern
|
||||
if (downloadedData) {
|
||||
contractsDirectory = downloadedData.downloadedContracts
|
||||
antPattern = "${downloadedData.inclusionProperties.includedRootFolderAntPattern}*.*"
|
||||
} else {
|
||||
contractsDirectory = config.contractsDirectory.get().asFile
|
||||
antPattern = "**/"
|
||||
}
|
||||
logger.info("For project [{}] will use contracts provided in the folder [{}]", project.name, contractsDirectory)
|
||||
final String contractsRepository = config.contractRepository.repositoryUrl.isPresent() ? config.contractRepository.repositoryUrl : ""
|
||||
throwExceptionWhenFailOnNoContracts(contractsDirectory, contractsRepository)
|
||||
String antPattern = "${config.includedRootFolderAntPattern.get()}*.*"
|
||||
String slashSeparatedGroupId = project.group.toString().replace(".", File.separator)
|
||||
String slashSeparatedAntPattern = antPattern.replace(slashSeparatedGroupId, project.group.toString())
|
||||
File output = config.copiedContractsFolder.get().asFile
|
||||
|
||||
final String slashSeparatedGroupId = project.group.toString().replace(".", File.separator)
|
||||
final String slashSeparatedAntPattern = antPattern.replace(slashSeparatedGroupId, project.group.toString())
|
||||
final File output = config.copiedContractsFolder.get().asFile
|
||||
logger.info("Downloading and unpacking files from [${contractsDirectory}] to [$output]. The inclusion ant patterns are [${antPattern}] and [${slashSeparatedAntPattern}]")
|
||||
sync(contractsDirectory, antPattern, slashSeparatedAntPattern, config.excludeBuildFolders.get(), output)
|
||||
if (config.convertToYaml.get()) {
|
||||
@@ -95,16 +159,20 @@ class ContractsCopyTask extends DefaultTask {
|
||||
}
|
||||
}
|
||||
|
||||
static Config fromExtension(ContractVerifierExtension extension, TaskProvider<InitContractsTask> initContractsTask, String root, Project project) {
|
||||
static Config fromExtension(ContractVerifierExtension extension, String root, Project project) {
|
||||
return new Config(
|
||||
convertToYaml: extension.convertToYaml,
|
||||
excludeBuildFolders: extension.excludeBuildFolders,
|
||||
failOnNoContracts: extension.failOnNoContracts,
|
||||
includedRootFolderAntPattern: initContractsTask.flatMap { it.config.includedRootFolderAntPattern },
|
||||
contractsDirectory: initContractsTask.flatMap { it.config.initialisedContractsDirectory },
|
||||
contractsRepository: extension.contractRepository.repositoryUrl.isPresent() ? extension.contractRepository.repositoryUrl : project.provider({ String s -> "" }),
|
||||
contractsDirectory: extension.contractsDslDir,
|
||||
copiedContractsFolder: createTaskOutput(root, extension.stubsOutputDir, ContractsCopyTask.CONTRACTS, project),
|
||||
backupContractsFolder: createTaskOutput(root, extension.stubsOutputDir, ContractsCopyTask.BACKUP, project)
|
||||
backupContractsFolder: createTaskOutput(root, extension.stubsOutputDir, ContractsCopyTask.BACKUP, project),
|
||||
contractDependency: extension.contractDependency,
|
||||
contractRepository: extension.contractRepository,
|
||||
contractsMode: extension.contractsMode,
|
||||
deleteStubsAfterTest: extension.deleteStubsAfterTest,
|
||||
contractsProperties: extension.contractsProperties,
|
||||
contractsPath: extension.contractsPath
|
||||
)
|
||||
}
|
||||
|
||||
@@ -115,8 +183,6 @@ class ContractsCopyTask extends DefaultTask {
|
||||
}
|
||||
|
||||
protected WorkResult sync(File file, String antPattern, String slashSeparatedAntPattern, boolean excludeBuildFolders, File outputContractsFolder) {
|
||||
// TODO: Is there any better way to make it statically compiled, avoiding explicit creation of new Action?
|
||||
// sync will remove files from target if they are removed from source. So using it here instead of copy:
|
||||
return project.sync(new Action<CopySpec>() {
|
||||
@Override
|
||||
void execute(final CopySpec spec) {
|
||||
@@ -135,6 +201,35 @@ class ContractsCopyTask extends DefaultTask {
|
||||
})
|
||||
}
|
||||
|
||||
private DownloadedData downloadContractsIfNeeded() {
|
||||
if (config.shouldDownloadContracts()) {
|
||||
logger.info("Project has group id [{}], artifact id [{}]", project.group, project.name)
|
||||
logger.info("For project [${project.name}] Download dependency is provided - will download contract jars")
|
||||
logger.info("Contract dependency [{}]", config.contractDependency)
|
||||
StubConfiguration configuration = config.getStubConfiguration()
|
||||
logger.info("Got the following contract dependency to download [{}]", configuration)
|
||||
logger.info("The contract dependency is a changing one [{}]", configuration.isVersionChanging())
|
||||
|
||||
final StubDownloader downloader = new StubDownloaderBuilderProvider().get(
|
||||
StubRunnerOptionsFactory.createStubRunnerOptions(config.contractRepository,
|
||||
config.contractsMode.getOrNull(), config.deleteStubsAfterTest.get(),
|
||||
config.contractsProperties.get(), config.failOnNoContracts.get()))
|
||||
final ContractDownloader contractDownloader = new ContractDownloader(downloader, configuration,
|
||||
config.contractsPath.getOrNull(), project.group as String, project.name, project.version as String)
|
||||
final File downloadedContracts = contractDownloader.unpackAndDownloadContracts();
|
||||
final ContractDownloader.InclusionProperties inclusionProperties =
|
||||
contractDownloader.createNewInclusionProperties(downloadedContracts)
|
||||
|
||||
// TODO: inclusionProperties.includedContracts is never used eventually. Review this:
|
||||
return new DownloadedData(
|
||||
downloadedContracts: contractsSubDirIfPresent(downloadedContracts, logger),
|
||||
inclusionProperties: inclusionProperties
|
||||
)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private static DirectoryProperty createTaskOutput(String root, DirectoryProperty stubsOutputDir, String suffix, Project project) {
|
||||
Provider<Directory> provider = stubsOutputDir.flatMap {
|
||||
Directory dir = it
|
||||
@@ -162,4 +257,22 @@ class ContractsCopyTask extends DefaultTask {
|
||||
+ "] .\nPlease make sure that the contracts were defined, or set the [failOnNoContracts] flag to [false]")
|
||||
}
|
||||
}
|
||||
|
||||
private static File contractsSubDirIfPresent(File contractsDirectory, Logger logger) {
|
||||
File contracts = new File(contractsDirectory, "contracts")
|
||||
if (contracts.exists()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Contracts folder found [" + contracts + "]")
|
||||
}
|
||||
contractsDirectory = contracts
|
||||
}
|
||||
return contractsDirectory
|
||||
}
|
||||
|
||||
@ImmutableOptions(knownImmutableClasses = [File, ContractDownloader.InclusionProperties])
|
||||
@Immutable
|
||||
private static class DownloadedData {
|
||||
final File downloadedContracts
|
||||
final ContractDownloader.InclusionProperties inclusionProperties
|
||||
}
|
||||
}
|
||||
@@ -54,8 +54,6 @@ class GenerateServerTestsTask extends DefaultTask {
|
||||
@InputDirectory
|
||||
Provider<Directory> contractsDslDir
|
||||
@Input
|
||||
Provider<String> includedContracts
|
||||
@Input
|
||||
@Optional
|
||||
Provider<String> nameSuffixForTests
|
||||
@Input
|
||||
@@ -101,12 +99,6 @@ class GenerateServerTestsTask extends DefaultTask {
|
||||
logger.info("Generated test sources dir [${generatedTestSources}]")
|
||||
logger.info("Generated test resources dir [${generatedTestResources}]")
|
||||
File contractsDslDir = config.contractsDslDir.get().asFile
|
||||
// There used to be some bug in old code, which was always setting `includedContracts` to ".*" instead of
|
||||
// getting it from the config (which is coming from contracts downloader). In fact, that `correct` behaviour
|
||||
// doesn't even make sense here as contracts are already copied, so that inclusion filter will not include
|
||||
// anything. So for now just restoring this old behaviour, but it must be reviewed. Probably that
|
||||
// `includedContracts` should be used in the `copyContracts` task instead?
|
||||
// String includedContracts = config.includedContracts.get()
|
||||
String includedContracts = ".*"
|
||||
project.logger.info("Spring Cloud Contract Verifier Plugin: Invoking test sources generation")
|
||||
project.logger.info("Contracts are unpacked to [${contractsDslDir}]")
|
||||
@@ -145,11 +137,9 @@ class GenerateServerTestsTask extends DefaultTask {
|
||||
}
|
||||
}
|
||||
|
||||
static Config fromExtension(ContractVerifierExtension extension, TaskProvider<InitContractsTask> initContractsTask,
|
||||
TaskProvider<ContractsCopyTask> copyContractsTask) {
|
||||
static Config fromExtension(ContractVerifierExtension extension, TaskProvider<ContractsCopyTask> copyContractsTask) {
|
||||
return new Config(
|
||||
contractsDslDir: copyContractsTask.flatMap { it.config.copiedContractsFolder },
|
||||
includedContracts: initContractsTask.flatMap { it.config.includedContracts },
|
||||
nameSuffixForTests: extension.nameSuffixForTests,
|
||||
basePackageForTests: extension.basePackageForTests,
|
||||
baseClassForTests: extension.baseClassForTests,
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.Immutable
|
||||
import groovy.transform.ImmutableOptions
|
||||
import groovy.transform.PackageScope
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.logging.Logger
|
||||
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.spring.StubRunnerProperties
|
||||
import org.springframework.util.StringUtils
|
||||
|
||||
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 logger
|
||||
protected static final Map<StubConfiguration, File> downloadedContract = new ConcurrentHashMap<>()
|
||||
|
||||
GradleContractsDownloader(Project project, Logger logger) {
|
||||
this.project = project
|
||||
this.logger = logger
|
||||
}
|
||||
|
||||
DownloadedData downloadAndUnpackContractsIfRequired(ContractVerifierExtension.Dependency contractDependency,
|
||||
ContractVerifierExtension.ContractRepository contractRepository, String contractsPath,
|
||||
StubRunnerProperties.StubsMode contractsMode, boolean deleteStubsAfterTest,
|
||||
Map<String, String> contractsProperties, boolean failOnNoContracts) {
|
||||
if (!shouldDownloadContracts(contractDependency, contractRepository)) {
|
||||
return null
|
||||
}
|
||||
logger.info("Project has group id [{}], artifact id [{}]", project.group, project.name)
|
||||
// download contracts, unzip them and pass as output directory
|
||||
|
||||
logger.info("For project [${project.name}] Download dependency is provided - will download contract jars")
|
||||
logger.info("Contract dependency [{}]", contractDependency)
|
||||
StubConfiguration configuration = stubConfiguration(contractDependency)
|
||||
logger.info("Got the following contract dependency to download [{}]", configuration)
|
||||
logger.info("The contract dependency is a changing one [{}] and cache download switch is set to [{}]",
|
||||
configuration.isVersionChanging(), contractRepository.cacheDownloadedContracts.get())
|
||||
if (!configuration.isVersionChanging() && contractRepository.cacheDownloadedContracts.get()) {
|
||||
logger.info("Resolved a non changing version - will try to return the folder from a cache")
|
||||
File cachedFolder = downloadedContract.get(configuration)
|
||||
if (cachedFolder) {
|
||||
logger.info("For project [{}] returning the cached location of the contracts", project.name)
|
||||
final ContractDownloader.InclusionProperties inclusionProperties =
|
||||
contractDownloader(configuration, contractRepository, contractsPath, contractsMode,
|
||||
deleteStubsAfterTest, contractsProperties, failOnNoContracts).createNewInclusionProperties(cachedFolder)
|
||||
|
||||
return new DownloadedData(
|
||||
downloadedContracts: contractsSubDirIfPresent(cachedFolder, logger),
|
||||
inclusionProperties: inclusionProperties
|
||||
)
|
||||
}
|
||||
}
|
||||
final ContractDownloader contractDownloader =
|
||||
contractDownloader(configuration, contractRepository, contractsPath, contractsMode, deleteStubsAfterTest,
|
||||
contractsProperties, failOnNoContracts);
|
||||
final File downloadedContracts = contractDownloader.unpackAndDownloadContracts();
|
||||
final ContractDownloader.InclusionProperties inclusionProperties =
|
||||
contractDownloader.createNewInclusionProperties(downloadedContracts)
|
||||
|
||||
downloadedContract.put(configuration, downloadedContracts)
|
||||
|
||||
return new DownloadedData(
|
||||
downloadedContracts: contractsSubDirIfPresent(downloadedContracts, logger),
|
||||
inclusionProperties: inclusionProperties
|
||||
)
|
||||
}
|
||||
|
||||
private static File contractsSubDirIfPresent(File contractsDirectory, Logger logger) {
|
||||
File contracts = new File(contractsDirectory, "contracts")
|
||||
if (contracts.exists()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Contracts folder found [" + contracts + "]")
|
||||
}
|
||||
contractsDirectory = contracts
|
||||
}
|
||||
return contractsDirectory
|
||||
}
|
||||
|
||||
@PackageScope
|
||||
StubConfiguration stubConfiguration(ContractVerifierExtension.Dependency contractDependency) {
|
||||
String groupId = contractDependency.groupId.getOrNull()
|
||||
String artifactId = contractDependency.artifactId.getOrNull()
|
||||
String version = StringUtils.hasText(contractDependency.version.getOrNull()) ?
|
||||
contractDependency.version.getOrNull() : LATEST_VERSION
|
||||
String classifier = contractDependency.classifier.getOrNull()
|
||||
String stringNotation = contractDependency.stringNotation.getOrNull()
|
||||
if (StringUtils.hasText(stringNotation)) {
|
||||
StubConfiguration stubConfiguration = new StubConfiguration(stringNotation)
|
||||
return new StubConfiguration(stubConfiguration.groupId, stubConfiguration.artifactId,
|
||||
stubConfiguration.version, stubConfiguration.classifier)
|
||||
}
|
||||
return new StubConfiguration(groupId, artifactId, version, classifier)
|
||||
}
|
||||
|
||||
protected ContractDownloader contractDownloader(StubConfiguration configuration,
|
||||
ContractVerifierExtension.ContractRepository contractRepository,
|
||||
String contractsPath, StubRunnerProperties.StubsMode contractsMode,
|
||||
boolean deleteStubsAfterTest, Map<String, String> contractsProperties,
|
||||
boolean failOnNoContracts) {
|
||||
return new ContractDownloader(stubDownloader(contractRepository, contractsMode, deleteStubsAfterTest, contractsProperties, failOnNoContracts),
|
||||
configuration, contractsPath, project.group as String, project.name, project.version as String)
|
||||
}
|
||||
|
||||
protected StubDownloader stubDownloader(ContractVerifierExtension.ContractRepository contractRepository,
|
||||
StubRunnerProperties.StubsMode contractsMode, boolean deleteStubsAfterTest,
|
||||
Map<String, String> contractsProperties, boolean failOnNoContracts) {
|
||||
StubDownloaderBuilderProvider provider = new StubDownloaderBuilderProvider()
|
||||
return provider.get(StubRunnerOptionsFactory.createStubRunnerOptions(contractRepository, contractsMode,
|
||||
deleteStubsAfterTest, contractsProperties, failOnNoContracts))
|
||||
}
|
||||
|
||||
private static boolean shouldDownloadContracts(ContractVerifierExtension.Dependency contractDependency,
|
||||
ContractVerifierExtension.ContractRepository contractRepository) {
|
||||
return StringUtils.hasText(contractDependency.getArtifactId().getOrNull()) ||
|
||||
StringUtils.hasText(contractDependency.getStringNotation().getOrNull()) ||
|
||||
StringUtils.hasText(contractRepository.repositoryUrl.getOrNull())
|
||||
}
|
||||
|
||||
@ImmutableOptions(knownImmutableClasses = [File, ContractDownloader.InclusionProperties])
|
||||
@Immutable
|
||||
static class DownloadedData {
|
||||
final File downloadedContracts
|
||||
final ContractDownloader.InclusionProperties inclusionProperties
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.PackageScope
|
||||
import org.springframework.cloud.contract.stubrunner.StubConfiguration
|
||||
import org.springframework.util.StringUtils
|
||||
|
||||
/**
|
||||
* @author Anatoliy Balakirev
|
||||
*/
|
||||
@PackageScope
|
||||
@CompileStatic
|
||||
class GradleContractsDownloaderHelper {
|
||||
|
||||
private static final String LATEST_VERSION = '+'
|
||||
|
||||
@PackageScope
|
||||
static StubConfiguration stubConfiguration(ContractVerifierExtension.Dependency contractDependency) {
|
||||
String groupId = contractDependency.groupId.getOrNull()
|
||||
String artifactId = contractDependency.artifactId.getOrNull()
|
||||
String version = StringUtils.hasText(contractDependency.version.getOrNull()) ?
|
||||
contractDependency.version.getOrNull() : LATEST_VERSION
|
||||
String classifier = contractDependency.classifier.getOrNull()
|
||||
String stringNotation = contractDependency.stringNotation.getOrNull()
|
||||
if (StringUtils.hasText(stringNotation)) {
|
||||
StubConfiguration stubConfiguration = new StubConfiguration(stringNotation)
|
||||
return new StubConfiguration(stubConfiguration.groupId, stubConfiguration.artifactId,
|
||||
stubConfiguration.version, stubConfiguration.classifier)
|
||||
}
|
||||
return new StubConfiguration(groupId, artifactId, version, classifier)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.PackageScope
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.provider.MapProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.Nested
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
|
||||
|
||||
/**
|
||||
* @author Anatoliy Balakirev
|
||||
*/
|
||||
@PackageScope
|
||||
@CompileStatic
|
||||
class InitContractsTask extends DefaultTask {
|
||||
|
||||
static final String TASK_NAME = 'initContracts'
|
||||
protected final GradleContractsDownloader contractDownloader = new GradleContractsDownloader(project, logger)
|
||||
@Nested
|
||||
Config config
|
||||
|
||||
static class Config {
|
||||
// All fields inside `@Nested` one are properly marked as an `@Input` to work with incremental build:
|
||||
@Nested
|
||||
@Optional
|
||||
ContractVerifierExtension.Dependency contractDependency
|
||||
@Nested
|
||||
@Optional
|
||||
ContractVerifierExtension.ContractRepository contractRepository
|
||||
@Input
|
||||
@Optional
|
||||
Property<StubRunnerProperties.StubsMode> contractsMode
|
||||
@Input
|
||||
Property<Boolean> deleteStubsAfterTest
|
||||
@Input
|
||||
Property<Boolean> failOnNoContracts
|
||||
@Input
|
||||
MapProperty<String, String> contractsProperties
|
||||
@Input
|
||||
@Optional
|
||||
Property<String> contractsPath
|
||||
|
||||
@Internal
|
||||
Property<String> includedContracts
|
||||
@Internal
|
||||
Property<String> includedRootFolderAntPattern
|
||||
|
||||
// TODO: Enable it when all caching from `GradleContractsDownloader` is replaced with Gradle's one here:
|
||||
// @OutputDirectory
|
||||
DirectoryProperty initialisedContractsDirectory
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void initContracts() {
|
||||
GradleContractsDownloader.DownloadedData downloaded =
|
||||
contractDownloader.downloadAndUnpackContractsIfRequired(config.contractDependency,
|
||||
config.contractRepository, config.contractsPath.getOrNull(), config.contractsMode.getOrNull(),
|
||||
config.deleteStubsAfterTest.get(), config.contractsProperties.get(), config.failOnNoContracts.get())
|
||||
if (downloaded) {
|
||||
config.includedContracts.set(downloaded.inclusionProperties.includedContracts)
|
||||
config.includedRootFolderAntPattern.set(downloaded.inclusionProperties.includedRootFolderAntPattern)
|
||||
config.initialisedContractsDirectory.set(downloaded.downloadedContracts)
|
||||
}
|
||||
logger.info("For project [{}] will use contracts provided in the folder [{}]", project.name,
|
||||
config.initialisedContractsDirectory.get())
|
||||
}
|
||||
|
||||
static Config fromExtension(ContractVerifierExtension extension, Project project) {
|
||||
DirectoryProperty initialisedContractsDirectory = project.objects.directoryProperty()
|
||||
initialisedContractsDirectory.set(extension.contractsDslDir)
|
||||
return new Config(
|
||||
contractDependency: extension.contractDependency,
|
||||
contractRepository: extension.contractRepository,
|
||||
contractsMode: extension.contractsMode,
|
||||
deleteStubsAfterTest: extension.deleteStubsAfterTest,
|
||||
failOnNoContracts: extension.failOnNoContracts,
|
||||
contractsProperties: extension.contractsProperties,
|
||||
contractsPath: extension.contractsPath,
|
||||
includedContracts: project.objects.property(String).convention(".*"),
|
||||
includedRootFolderAntPattern: project.objects.property(String).convention("**/"),
|
||||
initialisedContractsDirectory: initialisedContractsDirectory
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -17,14 +17,12 @@
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.PackageScope
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.MapProperty
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.Nested
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.springframework.cloud.contract.stubrunner.ContractProjectUpdater
|
||||
import org.springframework.cloud.contract.stubrunner.ScmStubDownloaderBuilder
|
||||
@@ -44,30 +42,95 @@ import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
|
||||
class PublishStubsToScmTask extends DefaultTask {
|
||||
|
||||
static final String TASK_NAME = 'publishStubsToScm'
|
||||
@Nested
|
||||
Config config
|
||||
private Closure customizationClosure = Closure.IDENTITY
|
||||
|
||||
static class Config {
|
||||
// All fields inside `@Nested` one are properly marked as an `@Input` to work with incremental build:
|
||||
@Nested
|
||||
@Optional
|
||||
private ObjectFactory objects
|
||||
ContractVerifierExtension.ContractRepository contractRepository
|
||||
@Input
|
||||
@Optional
|
||||
Provider<StubRunnerProperties.StubsMode> contractsMode
|
||||
@Input
|
||||
Provider<Boolean> deleteStubsAfterTest
|
||||
@Input
|
||||
Provider<Boolean> failOnNoContracts
|
||||
@Input
|
||||
Property<StubRunnerProperties.StubsMode> contractsMode
|
||||
Property<Boolean> deleteStubsAfterTest
|
||||
Property<Boolean> failOnNoContracts
|
||||
MapProperty<String, String> contractsProperties
|
||||
|
||||
@OutputDirectory
|
||||
DirectoryProperty stubsOutputDir
|
||||
|
||||
void setContractsMode(Property<StubRunnerProperties.StubsMode> contractsMode) {
|
||||
this.contractsMode = contractsMode
|
||||
}
|
||||
|
||||
void setContractsMode(String contractsMode) {
|
||||
if (contractsMode) {
|
||||
this.contractsMode = objects.property(StubRunnerProperties.StubsMode).convention(StubRunnerProperties.StubsMode.valueOf(contractsMode.toUpperCase()))
|
||||
}
|
||||
}
|
||||
|
||||
void setDeleteStubsAfterTest(Property<Boolean> deleteStubsAfterTest) {
|
||||
this.deleteStubsAfterTest = deleteStubsAfterTest
|
||||
}
|
||||
|
||||
void setDeleteStubsAfterTest(Boolean deleteStubsAfterTest) {
|
||||
if (deleteStubsAfterTest != null) {
|
||||
this.deleteStubsAfterTest = objects.property(Boolean).convention(deleteStubsAfterTest)
|
||||
}
|
||||
}
|
||||
|
||||
void setFailOnNoContracts(Property<Boolean> failOnNoContracts) {
|
||||
this.failOnNoContracts = failOnNoContracts
|
||||
}
|
||||
|
||||
void setFailOnNoContracts(Boolean failOnNoContracts) {
|
||||
if (failOnNoContracts != null) {
|
||||
this.failOnNoContracts = objects.property(Boolean).convention(failOnNoContracts)
|
||||
}
|
||||
}
|
||||
|
||||
void setContractsProperties(MapProperty<String, String> contractsProperties) {
|
||||
this.contractsProperties = contractsProperties
|
||||
}
|
||||
|
||||
void setContractsProperties(Map<String, String> contractsProperties) {
|
||||
if (contractsProperties) {
|
||||
this.contractsProperties = objects.mapProperty(String, String).convention(contractsProperties)
|
||||
}
|
||||
}
|
||||
|
||||
void setStubsOutputDir(DirectoryProperty stubsOutputDir) {
|
||||
this.stubsOutputDir = stubsOutputDir
|
||||
}
|
||||
|
||||
void setStubsOutputDir(String stubsOutputDir) {
|
||||
if (stubsOutputDir) {
|
||||
this.stubsOutputDir = objects.directoryProperty()
|
||||
this.stubsOutputDir.set(new File(stubsOutputDir))
|
||||
}
|
||||
}
|
||||
|
||||
void setStubsOutputDir(GString stubsOutputDir) {
|
||||
if (stubsOutputDir) {
|
||||
this.stubsOutputDir = objects.directoryProperty()
|
||||
this.stubsOutputDir.set(new File(stubsOutputDir.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
void contractRepository(@DelegatesTo(ContractVerifierExtension.ContractRepository) Closure closure) {
|
||||
closure.delegate = contractRepository
|
||||
closure.call()
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
void contractDependency(Closure closure) {
|
||||
// Here only to preserve backward compatibility. Was never used here.
|
||||
}
|
||||
|
||||
@PackageScope
|
||||
void setObjects(ObjectFactory objects) {
|
||||
this.objects = objects
|
||||
}
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void publishStubsToScm() {
|
||||
applyConfigCustomizations()
|
||||
if (!shouldRun()) {
|
||||
return
|
||||
}
|
||||
@@ -79,15 +142,15 @@ class PublishStubsToScmTask extends DefaultTask {
|
||||
new ContractProjectUpdater(stubRunnerOptions).updateContractProject(projectName, config.stubsOutputDir.get().asFile.toPath())
|
||||
}
|
||||
|
||||
static Config fromExtension(ContractVerifierExtension extension) {
|
||||
static Config fromExtension(ContractVerifierExtension extension, ObjectFactory objects) {
|
||||
return new Config(
|
||||
contractRepository: extension.contractRepository,
|
||||
contractsMode: extension.contractsMode,
|
||||
failOnNoContracts: extension.failOnNoContracts,
|
||||
deleteStubsAfterTest: extension.deleteStubsAfterTest,
|
||||
contractsProperties: extension.contractsProperties,
|
||||
|
||||
stubsOutputDir: extension.stubsOutputDir
|
||||
stubsOutputDir: extension.stubsOutputDir,
|
||||
objects: objects
|
||||
)
|
||||
}
|
||||
|
||||
@@ -99,4 +162,38 @@ class PublishStubsToScmTask extends DefaultTask {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Some builds might want to customize this task only, overriding settings from plugin.
|
||||
*/
|
||||
void customize(@DelegatesTo(Config) Closure closure) {
|
||||
customizationClosure = closure
|
||||
}
|
||||
|
||||
private void applyConfigCustomizations() {
|
||||
// Needs to be copied, otherwise properties won't be updated:
|
||||
config.contractRepository = copy(config.contractRepository, project.objects)
|
||||
customizationClosure.delegate = config
|
||||
customizationClosure.call()
|
||||
}
|
||||
|
||||
private static ContractVerifierExtension.ContractRepository copy(ContractVerifierExtension.ContractRepository original, ObjectFactory objects) {
|
||||
ContractVerifierExtension.ContractRepository copied = new ContractVerifierExtension.ContractRepository(objects)
|
||||
if (original.repositoryUrl.getOrNull()) {
|
||||
copied.repositoryUrl.set(original.repositoryUrl.get())
|
||||
}
|
||||
if (original.username.getOrNull()) {
|
||||
copied.username.set(original.username.get())
|
||||
}
|
||||
if (original.password.getOrNull()) {
|
||||
copied.password.set(original.password.get())
|
||||
}
|
||||
if (original.proxyPort.getOrNull()) {
|
||||
copied.proxyPort.set(original.proxyPort.get())
|
||||
}
|
||||
if (original.proxyHost.getOrNull()) {
|
||||
copied.proxyHost.set(original.proxyHost.get())
|
||||
}
|
||||
return copied
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,12 +57,11 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin<Project> {
|
||||
ContractVerifierExtension extension = project.extensions.create(EXTENSION_NAME, ContractVerifierExtension)
|
||||
setConfigurationDefaults(extension)
|
||||
|
||||
TaskProvider<InitContractsTask> initContracts = createAndConfigureContractsInitTask(extension)
|
||||
TaskProvider<ContractsCopyTask> copyContracts = createAndConfigureCopyContractsTask(initContracts, extension)
|
||||
TaskProvider<ContractsCopyTask> copyContracts = createAndConfigureCopyContractsTask(extension)
|
||||
TaskProvider<GenerateClientStubsFromDslTask> generateClientStubs = createAndConfigureGenerateClientStubs(extension, copyContracts)
|
||||
|
||||
createAndConfigureStubsJarTasks(extension, copyContracts, generateClientStubs)
|
||||
createGenerateTestsTask(extension, copyContracts, initContracts)
|
||||
createGenerateTestsTask(extension, copyContracts)
|
||||
createAndConfigurePublishStubsToScmTask(extension, generateClientStubs)
|
||||
project.afterEvaluate {
|
||||
addIdeaTestSources(project, extension)
|
||||
@@ -112,14 +111,13 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin<Project> {
|
||||
}
|
||||
|
||||
|
||||
private void createGenerateTestsTask(ContractVerifierExtension extension,
|
||||
TaskProvider<ContractsCopyTask> copyContracts, TaskProvider<InitContractsTask> initContracts) {
|
||||
private void createGenerateTestsTask(ContractVerifierExtension extension, TaskProvider<ContractsCopyTask> copyContracts) {
|
||||
TaskProvider<GenerateServerTestsTask> task = project.tasks.register(GenerateServerTestsTask.TASK_NAME, GenerateServerTestsTask)
|
||||
task.configure {
|
||||
it.description = "Generate server tests from the contracts"
|
||||
it.group = GROUP_NAME
|
||||
it.enabled = !project.gradle.startParameter.excludedTaskNames.contains("test")
|
||||
it.config = GenerateServerTestsTask.fromExtension(extension, initContracts, copyContracts)
|
||||
it.config = GenerateServerTestsTask.fromExtension(extension, copyContracts)
|
||||
|
||||
it.dependsOn copyContracts
|
||||
}
|
||||
@@ -133,7 +131,7 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin<Project> {
|
||||
task.configure {
|
||||
it.description = "The generated stubs get committed to the SCM repo and pushed to origin"
|
||||
it.group = GROUP_NAME
|
||||
it.config = PublishStubsToScmTask.fromExtension(extension)
|
||||
it.config = PublishStubsToScmTask.fromExtension(extension, project.objects)
|
||||
|
||||
it.dependsOn generateClientStubs
|
||||
}
|
||||
@@ -242,25 +240,12 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin<Project> {
|
||||
return task
|
||||
}
|
||||
|
||||
private TaskProvider<ContractsCopyTask> createAndConfigureCopyContractsTask(TaskProvider<InitContractsTask> initContractsTask,
|
||||
ContractVerifierExtension extension) {
|
||||
private TaskProvider<ContractsCopyTask> createAndConfigureCopyContractsTask(ContractVerifierExtension extension) {
|
||||
TaskProvider<ContractsCopyTask> task = project.tasks.register(ContractsCopyTask.TASK_NAME, ContractsCopyTask)
|
||||
task.configure {
|
||||
it.description = "Copies contracts to the output folder"
|
||||
it.group = GROUP_NAME
|
||||
it.config = ContractsCopyTask.fromExtension(extension, initContractsTask, buildRootPath(), project)
|
||||
|
||||
it.dependsOn initContractsTask
|
||||
}
|
||||
return task
|
||||
}
|
||||
|
||||
private TaskProvider<InitContractsTask> createAndConfigureContractsInitTask(ContractVerifierExtension extension) {
|
||||
TaskProvider<InitContractsTask> task = project.tasks.register(InitContractsTask.TASK_NAME, InitContractsTask)
|
||||
task.configure {
|
||||
it.description = "Initialises contracts either downloading or using default provided folder"
|
||||
it.group = GROUP_NAME
|
||||
it.config = InitContractsTask.fromExtension(extension, project)
|
||||
it.config = ContractsCopyTask.fromExtension(extension, buildRootPath(), project)
|
||||
}
|
||||
return task
|
||||
}
|
||||
|
||||
@@ -70,16 +70,6 @@ class ContractVerifierSpec extends Specification {
|
||||
project.tasks.verifierStubsJar.getDependsOn().contains(project.tasks.named("copyContracts"))
|
||||
}
|
||||
|
||||
def "should create initContracts task"() {
|
||||
expect:
|
||||
project.tasks.named("initContracts") != null
|
||||
}
|
||||
|
||||
def "should configure initContracts task as a dependency of the copyContracts task"() {
|
||||
expect:
|
||||
project.tasks.copyContracts.getDependsOn().contains(project.tasks.named("initContracts"))
|
||||
}
|
||||
|
||||
/**
|
||||
* project.evaluate() is used here in order to trigger the evaluation lifecycle of a project.
|
||||
* This method is currently exposed via the internal API and is subject to change, however, Gradle
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import org.gradle.api.internal.provider.DefaultPropertyState
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.Property
|
||||
import org.springframework.cloud.contract.stubrunner.StubConfiguration
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Anatoliy Balakirev
|
||||
*/
|
||||
class GradleContractsDownloaderHelperSpec extends Specification {
|
||||
|
||||
ObjectFactory objectFactory = Mock(ObjectFactory)
|
||||
|
||||
def setup() {
|
||||
// Is there any better way to say that I need a new object on each interaction with mock?
|
||||
objectFactory.property(String) >>> [prop(String), prop(String), prop(String), prop(String), prop(String)]
|
||||
}
|
||||
|
||||
def "should parse dependency via string notation"() {
|
||||
given:
|
||||
String stringNotation = "com.example:foo:1.0.0:stubs"
|
||||
ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory)
|
||||
dep.stringNotation.set(stringNotation)
|
||||
when:
|
||||
StubConfiguration stubConfig = GradleContractsDownloaderHelper.stubConfiguration(dep)
|
||||
then:
|
||||
stubConfig.groupId == "com.example"
|
||||
stubConfig.artifactId == "foo"
|
||||
stubConfig.version == "1.0.0"
|
||||
stubConfig.classifier == "stubs"
|
||||
}
|
||||
|
||||
def "should parse dependency via direct setting"() {
|
||||
given:
|
||||
ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory)
|
||||
dep.groupId.set("com.example")
|
||||
dep.artifactId.set("foo")
|
||||
dep.version.set("1.0.0")
|
||||
dep.classifier.set("stubs")
|
||||
when:
|
||||
StubConfiguration stubConfig = GradleContractsDownloaderHelper.stubConfiguration(dep)
|
||||
then:
|
||||
stubConfig.groupId == "com.example"
|
||||
stubConfig.artifactId == "foo"
|
||||
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"
|
||||
ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory)
|
||||
dep.stringNotation.set(stringNotation)
|
||||
when:
|
||||
StubConfiguration stubConfig = GradleContractsDownloaderHelper.stubConfiguration(dep)
|
||||
then:
|
||||
stubConfig.groupId == "com.example"
|
||||
stubConfig.artifactId == "foo"
|
||||
stubConfig.version == "1.0.0"
|
||||
stubConfig.classifier == "stubs"
|
||||
}
|
||||
|
||||
def "should parse dependency via direct setting with methods"() {
|
||||
given:
|
||||
ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory)
|
||||
dep.groupId.set("com.example")
|
||||
dep.artifactId.set("foo")
|
||||
dep.version.set("1.0.0")
|
||||
dep.classifier.set("stubs")
|
||||
when:
|
||||
StubConfiguration stubConfig = GradleContractsDownloaderHelper.stubConfiguration(dep)
|
||||
then:
|
||||
stubConfig.groupId == "com.example"
|
||||
stubConfig.artifactId == "foo"
|
||||
stubConfig.version == "1.0.0"
|
||||
stubConfig.classifier == "stubs"
|
||||
}
|
||||
|
||||
// Have to use this internal property impl here. Is there some better way?
|
||||
static <T> Property<T> prop(Class<T> aClass) {
|
||||
return new DefaultPropertyState(aClass)
|
||||
}
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.internal.provider.DefaultPropertyState
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.Property
|
||||
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.spring.StubRunnerProperties
|
||||
import spock.lang.Specification
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Anatoliy Balakirev
|
||||
*/
|
||||
class GradleContractsDownloaderSpec extends Specification {
|
||||
|
||||
Project project = Stub(Project)
|
||||
Logger logger = Stub(Logger)
|
||||
ObjectFactory objectFactory = Mock(ObjectFactory)
|
||||
|
||||
def setup() {
|
||||
// Is there any better way to say that I need a new object on each interaction with mock?
|
||||
objectFactory.property(String) >>> [prop(String), prop(String), prop(String), prop(String), prop(String), prop(String), prop(String), prop(String), prop(String)]
|
||||
objectFactory.property(Integer) >> prop(Integer)
|
||||
objectFactory.property(Boolean) >> prop(Boolean)
|
||||
}
|
||||
|
||||
def "should parse dependency via string notation"() {
|
||||
given:
|
||||
String stringNotation = "com.example:foo:1.0.0:stubs"
|
||||
ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory)
|
||||
dep.stringNotation.set(stringNotation)
|
||||
when:
|
||||
StubConfiguration stubConfig = new GradleContractsDownloader(null, null).stubConfiguration(dep)
|
||||
then:
|
||||
stubConfig.groupId == "com.example"
|
||||
stubConfig.artifactId == "foo"
|
||||
stubConfig.version == "1.0.0"
|
||||
stubConfig.classifier == "stubs"
|
||||
}
|
||||
|
||||
def "should parse dependency via direct setting"() {
|
||||
given:
|
||||
ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory)
|
||||
dep.groupId.set("com.example")
|
||||
dep.artifactId.set("foo")
|
||||
dep.version.set("1.0.0")
|
||||
dep.classifier.set("stubs")
|
||||
when:
|
||||
StubConfiguration stubConfig = new GradleContractsDownloader(null, null).stubConfiguration(dep)
|
||||
then:
|
||||
stubConfig.groupId == "com.example"
|
||||
stubConfig.artifactId == "foo"
|
||||
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"
|
||||
ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory)
|
||||
dep.stringNotation.set(stringNotation)
|
||||
when:
|
||||
StubConfiguration stubConfig = new GradleContractsDownloader(null, null).stubConfiguration(dep)
|
||||
then:
|
||||
stubConfig.groupId == "com.example"
|
||||
stubConfig.artifactId == "foo"
|
||||
stubConfig.version == "1.0.0"
|
||||
stubConfig.classifier == "stubs"
|
||||
}
|
||||
|
||||
def "should parse dependency via direct setting with methods"() {
|
||||
given:
|
||||
ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory)
|
||||
dep.groupId.set("com.example")
|
||||
dep.artifactId.set("foo")
|
||||
dep.version.set("1.0.0")
|
||||
dep.classifier.set("stubs")
|
||||
when:
|
||||
StubConfiguration stubConfig = new GradleContractsDownloader(null, null).stubConfiguration(dep)
|
||||
then:
|
||||
stubConfig.groupId == "com.example"
|
||||
stubConfig.artifactId == "foo"
|
||||
stubConfig.version == "1.0.0"
|
||||
stubConfig.classifier == "stubs"
|
||||
}
|
||||
|
||||
def "should pick dependency from cache for a non snapshot contract dependency with new property"() {
|
||||
given:
|
||||
ContractVerifierExtension.Dependency contractDependency = new ContractVerifierExtension.Dependency(objectFactory)
|
||||
contractDependency.groupId.set("com.example")
|
||||
contractDependency.artifactId.set("foo")
|
||||
contractDependency.version.set("1.0.0")
|
||||
contractDependency.classifier.set("stubs")
|
||||
ContractVerifierExtension.ContractRepository contractRepository = new ContractVerifierExtension.ContractRepository(objectFactory)
|
||||
contractRepository.repositoryUrl.set("foo")
|
||||
and:
|
||||
final AetherStubDownloader downloader = Mock(AetherStubDownloader)
|
||||
final ContractDownloader contractDownloader = Mock(ContractDownloader)
|
||||
and:
|
||||
GradleContractsDownloader 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:
|
||||
GradleContractsDownloader.DownloadedData downloaded = gradleDownloader.downloadAndUnpackContractsIfRequired(contractDependency, contractRepository, null, StubRunnerProperties.StubsMode.REMOTE, true, [:], true)
|
||||
then:
|
||||
downloaded.downloadedContracts == expectedFileFromCache
|
||||
}
|
||||
|
||||
def "should not pick dependency from cache for a non snapshot contract dependency with cache switch off"() {
|
||||
given:
|
||||
ContractVerifierExtension.Dependency contractDependency = new ContractVerifierExtension.Dependency(objectFactory)
|
||||
contractDependency.groupId.set("com.example")
|
||||
contractDependency.artifactId.set("foo")
|
||||
contractDependency.version.set("1.0.0")
|
||||
contractDependency.classifier.set("stubs")
|
||||
ContractVerifierExtension.ContractRepository contractRepository = new ContractVerifierExtension.ContractRepository(objectFactory)
|
||||
contractRepository.repositoryUrl.set("foo")
|
||||
contractRepository.cacheDownloadedContracts.set(false)
|
||||
and:
|
||||
final AetherStubDownloader downloader = Mock(AetherStubDownloader)
|
||||
final ContractDownloader contractDownloader = Mock(ContractDownloader)
|
||||
and:
|
||||
GradleContractsDownloader 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.unpackAndDownloadContracts() >> expectedFileNotFromCache
|
||||
when:
|
||||
GradleContractsDownloader.DownloadedData downloaded = gradleDownloader.downloadAndUnpackContractsIfRequired(contractDependency, contractRepository, null, StubRunnerProperties.StubsMode.REMOTE, true, [:], true)
|
||||
then:
|
||||
downloaded.downloadedContracts == expectedFileNotFromCache
|
||||
}
|
||||
|
||||
def "should not pick dependency from cache for snapshot contract dependency"() {
|
||||
given:
|
||||
ContractVerifierExtension.Dependency contractDependency = new ContractVerifierExtension.Dependency(objectFactory)
|
||||
contractDependency.groupId.set("com.example")
|
||||
contractDependency.artifactId.set("foo")
|
||||
contractDependency.version.set("1.0.0.BUILD-SNAPSHOT")
|
||||
contractDependency.classifier.set("stubs")
|
||||
ContractVerifierExtension.ContractRepository contractRepository = new ContractVerifierExtension.ContractRepository(objectFactory)
|
||||
contractRepository.repositoryUrl.set("foo")
|
||||
and:
|
||||
final AetherStubDownloader downloader = Mock(AetherStubDownloader)
|
||||
final ContractDownloader contractDownloader = Mock(ContractDownloader)
|
||||
File expectedFileNotFromCache = new File("foo/bar/baz")
|
||||
contractDownloader.unpackAndDownloadContracts() >> expectedFileNotFromCache
|
||||
and:
|
||||
GradleContractsDownloader gradleDownloader = stubbedContractDownloader(downloader, contractDownloader)
|
||||
when:
|
||||
GradleContractsDownloader.DownloadedData downloaded = gradleDownloader.downloadAndUnpackContractsIfRequired(contractDependency, contractRepository, null, StubRunnerProperties.StubsMode.REMOTE, true, [:], true)
|
||||
then:
|
||||
downloaded.downloadedContracts == expectedFileNotFromCache
|
||||
}
|
||||
|
||||
private GradleContractsDownloader stubbedContractDownloader(downloader, contractDownloader) {
|
||||
new GradleContractsDownloader(project, logger) {
|
||||
@Override
|
||||
protected AetherStubDownloader stubDownloader(ContractVerifierExtension.ContractRepository contractRepository,
|
||||
StubRunnerProperties.StubsMode contractsMode, boolean deleteStubsAfterTest,
|
||||
Map<String, String> contractsProperties, boolean failOnNoContracts) {
|
||||
return downloader
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ContractDownloader contractDownloader(StubConfiguration configuration,
|
||||
ContractVerifierExtension.ContractRepository contractRepository,
|
||||
String contractsPath, StubRunnerProperties.StubsMode contractsMode,
|
||||
boolean deleteStubsAfterTest, Map<String, String> contractsProperties,
|
||||
boolean failOnNoContracts) {
|
||||
return contractDownloader
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "should not start downloading"() {
|
||||
given:
|
||||
ContractVerifierExtension.Dependency contractDependency = new ContractVerifierExtension.Dependency(objectFactory)
|
||||
ContractVerifierExtension.ContractRepository contractRepository = new ContractVerifierExtension.ContractRepository(objectFactory)
|
||||
and:
|
||||
final AetherStubDownloader downloader = Mock(AetherStubDownloader)
|
||||
final ContractDownloader contractDownloader = Mock(ContractDownloader)
|
||||
and:
|
||||
GradleContractsDownloader 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:
|
||||
GradleContractsDownloader.DownloadedData downloaded = gradleDownloader.downloadAndUnpackContractsIfRequired(contractDependency, contractRepository, null, StubRunnerProperties.StubsMode.CLASSPATH, true, [:], true)
|
||||
then:
|
||||
downloaded == null
|
||||
}
|
||||
|
||||
def "should pass contract dependency properties as a parameter to the builder"() {
|
||||
given:
|
||||
ContractVerifierExtension.Dependency contractDependency = new ContractVerifierExtension.Dependency(objectFactory)
|
||||
contractDependency.groupId.set("com.example")
|
||||
contractDependency.artifactId.set("foo")
|
||||
contractDependency.version.set("1.0.0.BUILD-SNAPSHOT")
|
||||
contractDependency.classifier.set("stubs")
|
||||
ContractVerifierExtension.ContractRepository contractRepository = new ContractVerifierExtension.ContractRepository(objectFactory)
|
||||
contractRepository.repositoryUrl.set("foo")
|
||||
contractRepository.username.set("foo1")
|
||||
contractRepository.password.set("foo2")
|
||||
contractRepository.proxyHost.set("foo3")
|
||||
contractRepository.proxyPort.set(12)
|
||||
and:
|
||||
final AetherStubDownloader downloader = Mock(AetherStubDownloader)
|
||||
final ContractDownloader contractDownloader = Mock(ContractDownloader)
|
||||
File expectedFileNotFromCache = new File("foo/bar/baz")
|
||||
contractDownloader.unpackAndDownloadContracts() >> expectedFileNotFromCache
|
||||
and:
|
||||
GradleContractsDownloader gradleDownloader = assertingContractDownloader(downloader, contractDownloader)
|
||||
when:
|
||||
gradleDownloader.downloadAndUnpackContractsIfRequired(contractDependency, contractRepository, null, StubRunnerProperties.StubsMode.CLASSPATH, true, [:], true)
|
||||
then:
|
||||
noExceptionThrown()
|
||||
}
|
||||
|
||||
private GradleContractsDownloader assertingContractDownloader(downloader, contractDownloader) {
|
||||
new GradleContractsDownloader(project, logger) {
|
||||
@Override
|
||||
protected AetherStubDownloader stubDownloader(ContractVerifierExtension.ContractRepository contractRepository,
|
||||
StubRunnerProperties.StubsMode contractsMode, boolean deleteStubsAfterTest,
|
||||
Map<String, String> contractsProperties, boolean failOnNoContracts) {
|
||||
assert extension.contractRepository.username == "foo1"
|
||||
assert extension.contractRepository.password == "foo2"
|
||||
assert extension.contractRepository.proxyHost == "foo3"
|
||||
assert extension.contractRepository.proxyPort == 12
|
||||
return downloader
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ContractDownloader contractDownloader(StubConfiguration configuration,
|
||||
ContractVerifierExtension.ContractRepository contractRepository,
|
||||
String contractsPath, StubRunnerProperties.StubsMode contractsMode,
|
||||
boolean deleteStubsAfterTest, Map<String, String> contractsProperties,
|
||||
boolean failOnNoContracts) {
|
||||
return contractDownloader
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Have to use this internal property impl here. Is there some better way?
|
||||
static <T> Property<T> prop(Class<T> aClass) {
|
||||
return new DefaultPropertyState(aClass)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user