Merge branch 'master' into feature/kotlin-contract-support
This commit is contained in:
@@ -624,7 +624,7 @@ You can check out the https://github.com/spring-cloud/spring-cloud-pipelines[Spr
|
||||
project for more information.
|
||||
|
||||
[[features-stub-runner-boot-server]]
|
||||
===== Stub Runner Server
|
||||
==== Stub Runner Server
|
||||
|
||||
To use the Stub Runner Server, add the following dependency:
|
||||
|
||||
@@ -640,7 +640,7 @@ Then annotate a class with `@EnableStubRunnerServer`, build a fat jar, and it is
|
||||
For the properties, see the <<features-stub-runner-rule-spring,Stub Runner Spring>> section.
|
||||
|
||||
[[features-stub-runner-boot-how-fat-jar]]
|
||||
===== Stub Runner Server Fat Jar
|
||||
==== Stub Runner Server Fat Jar
|
||||
|
||||
You can download a standalone JAR from Maven (for example, for version 2.0.1.RELEASE)
|
||||
by running the following commands:
|
||||
@@ -654,7 +654,7 @@ $ java -jar stub-runner.jar --stubrunner.ids=... --stubrunner.repositoryRoot=...
|
||||
====
|
||||
|
||||
[[features-stub-runner-boot-how-cli]]
|
||||
===== Spring Cloud CLI
|
||||
==== Spring Cloud CLI
|
||||
|
||||
Starting from the `1.4.0.RELEASE` version of the https://cloud.spring.io/spring-cloud-cli[Spring Cloud CLI]
|
||||
project, you can start Stub Runner Boot by running `spring cloud stubrunner`.
|
||||
@@ -905,7 +905,7 @@ stubsMode = StubRunnerProperties.StubsMode.REMOTE,
|
||||
Contracts and stubs may be stored in a location, where each producer has its own, dedicated folder for contracts and stub mappings. Under that folder each consumer can have its own setup. To make Stub Runner find the dedicated folder from the provided ids one can pass a property `stubs.find-producer=true` or a system property `stubrunner.stubs.find-producer=true` .
|
||||
|
||||
[source,bash,indent=0]
|
||||
====
|
||||
----
|
||||
└── com.example <1>
|
||||
├── some-artifact-id <2>
|
||||
│ └── 0.0.1
|
||||
@@ -919,7 +919,7 @@ Contracts and stubs may be stored in a location, where each producer has its own
|
||||
└── mappings
|
||||
└── shouldReturnStuffForOtherArtifactId.json
|
||||
|
||||
====
|
||||
----
|
||||
<1> group id of the consumers
|
||||
<2> consumer with artifact id [some-artifact-id]
|
||||
<3> contracts for the consumer with artifact id [some-artifact-id]
|
||||
|
||||
@@ -70,14 +70,52 @@ public class ContractDownloader {
|
||||
* pattern
|
||||
* @return location of the unpacked downloaded stubs
|
||||
*/
|
||||
// Use unpackAndDownloadContracts() and createNewInclusionProperties() instead
|
||||
@Deprecated
|
||||
public File unpackedDownloadedContracts(ContractVerifierConfigProperties config) {
|
||||
File contractsDirectory = unpackAndDownloadContracts();
|
||||
updatePropertiesWithInclusion(contractsDirectory, config);
|
||||
return contractsDirectory;
|
||||
}
|
||||
|
||||
// Use createNewInclusionProperties() instead
|
||||
@Deprecated
|
||||
public ContractVerifierConfigProperties updatePropertiesWithInclusion(
|
||||
File contractsDirectory, ContractVerifierConfigProperties config) {
|
||||
final InclusionProperties newInclusionProperties = createNewInclusionProperties(
|
||||
contractsDirectory);
|
||||
config.setIncludedContracts(newInclusionProperties.getIncludedContracts());
|
||||
config.setIncludedRootFolderAntPattern(
|
||||
newInclusionProperties.getIncludedRootFolderAntPattern());
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads JAR containing all the contracts. The JAR with the contracts contains all
|
||||
* the contracts for all the projects. We're interested only in its subset.
|
||||
* @return location of the unpacked downloaded stubs
|
||||
*/
|
||||
public File unpackAndDownloadContracts() {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Will download contracts for [" + this.contractsJarStubConfiguration
|
||||
+ "]");
|
||||
}
|
||||
Map.Entry<StubConfiguration, File> unpackedContractStubs = this.stubDownloader
|
||||
.downloadAndUnpackStubJar(this.contractsJarStubConfiguration);
|
||||
if (unpackedContractStubs == null) {
|
||||
throw new IllegalStateException("The contracts failed to be downloaded!");
|
||||
}
|
||||
return unpackedContractStubs.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* After JAR with all the contracts is downloaded and unpacked - we need to get new
|
||||
* inclusion pattern for those contracts. The JAR with the contracts contains all the
|
||||
* contracts for all the projects. We're interested only in its subset.
|
||||
* @param contractsDirectory - location of the unpacked downloaded stubs.
|
||||
* @return new inclusion properties, calculated for those downloaded contracts.
|
||||
*/
|
||||
public InclusionProperties createNewInclusionProperties(File contractsDirectory) {
|
||||
String pattern;
|
||||
String includedAntPattern;
|
||||
if (StringUtils.hasText(this.contractsPath)) {
|
||||
@@ -107,9 +145,7 @@ public class ContractDownloader {
|
||||
}
|
||||
log.info("Pattern to pick contracts equals [" + pattern + "]");
|
||||
log.info("Ant Pattern to pick files equals [" + includedAntPattern + "]");
|
||||
config.setIncludedContracts(pattern);
|
||||
config.setIncludedRootFolderAntPattern(includedAntPattern);
|
||||
return config;
|
||||
return new InclusionProperties(pattern, includedAntPattern);
|
||||
}
|
||||
|
||||
private File contractsSubDirIfPresent(File contractsDirectory) {
|
||||
@@ -162,19 +198,6 @@ public class ContractDownloader {
|
||||
+ "**/";
|
||||
}
|
||||
|
||||
private File unpackAndDownloadContracts() {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Will download contracts for [" + this.contractsJarStubConfiguration
|
||||
+ "]");
|
||||
}
|
||||
Map.Entry<StubConfiguration, File> unpackedContractStubs = this.stubDownloader
|
||||
.downloadAndUnpackStubJar(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 + ")?"
|
||||
+ ".*" + slashSeparatedGroupId() + File.separator + this.projectArtifactId
|
||||
@@ -189,4 +212,36 @@ public class ContractDownloader {
|
||||
return this.projectGroupId.replace(".", File.separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holder for updated inclusion properties, which are calculated after jar with
|
||||
* contracts was downloaded / unpacked.
|
||||
*/
|
||||
public static class InclusionProperties {
|
||||
|
||||
/**
|
||||
* @see ContractVerifierConfigProperties.includedContracts
|
||||
*/
|
||||
private final String includedContracts;
|
||||
|
||||
/**
|
||||
* @see ContractVerifierConfigProperties.includedRootFolderAntPattern
|
||||
*/
|
||||
private final String includedRootFolderAntPattern;
|
||||
|
||||
InclusionProperties(final String includedContracts,
|
||||
final String includedRootFolderAntPattern) {
|
||||
this.includedContracts = includedContracts;
|
||||
this.includedRootFolderAntPattern = includedRootFolderAntPattern;
|
||||
}
|
||||
|
||||
public String getIncludedContracts() {
|
||||
return includedContracts;
|
||||
}
|
||||
|
||||
public String getIncludedRootFolderAntPattern() {
|
||||
return includedRootFolderAntPattern;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ 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.cloud.contract.verifier.converter.RecursiveFilesConverter;
|
||||
import org.springframework.cloud.contract.verifier.converter.StubGenerator;
|
||||
import org.springframework.cloud.contract.verifier.converter.StubGeneratorProvider;
|
||||
@@ -149,12 +148,10 @@ class StubRunnerFactory {
|
||||
|
||||
private void generateNewMappings(Path path) {
|
||||
File unpackedLocation = path.toFile();
|
||||
ContractVerifierConfigProperties configProperties = new ContractVerifierConfigProperties();
|
||||
configProperties
|
||||
.setContractsDslDir(subfolderIfPresent(unpackedLocation, "contracts"));
|
||||
configProperties
|
||||
.setStubsOutputDir(subfolderIfPresent(unpackedLocation, "mappings"));
|
||||
RecursiveFilesConverter converter = new RecursiveFilesConverter(configProperties);
|
||||
RecursiveFilesConverter converter = new RecursiveFilesConverter(
|
||||
subfolderIfPresent(unpackedLocation, "mappings"),
|
||||
subfolderIfPresent(unpackedLocation, "contracts"), new ArrayList<>(),
|
||||
".*", false);
|
||||
converter.processFiles();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,6 @@ package org.springframework.cloud.contract.stubrunner
|
||||
|
||||
import spock.lang.Specification
|
||||
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@@ -35,16 +33,13 @@ class ContractDownloaderSpec extends Specification {
|
||||
String contractPath = File.separator + ['a', 'b', 'c', 'd'].join(File.separator)
|
||||
ContractDownloader contractDownloader = new ContractDownloader(stubDownloader,
|
||||
stubConfiguration, contractPath, '', '', '')
|
||||
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
|
||||
and:
|
||||
stubDownloader.downloadAndUnpackStubJar(_) >> new AbstractMap.SimpleEntry(stubConfiguration, file)
|
||||
when:
|
||||
contractDownloader.unpackedDownloadedContracts(properties)
|
||||
ContractDownloader.InclusionProperties inclusionProperties = contractDownloader.createNewInclusionProperties(file)
|
||||
then:
|
||||
properties.includedContracts.startsWith('^')
|
||||
properties.includedContracts.endsWith('$')
|
||||
properties.includedContracts.contains(fileSeparated('/some/path/to/somewhere(/)?.*/a/b/c/d/.*'))
|
||||
properties.includedRootFolderAntPattern == "**/a/b/c/d/**/"
|
||||
inclusionProperties.includedContracts.startsWith('^')
|
||||
inclusionProperties.includedContracts.endsWith('$')
|
||||
inclusionProperties.includedContracts.contains(fileSeparated('/some/path/to/somewhere(/)?.*/a/b/c/d/.*'))
|
||||
inclusionProperties.includedRootFolderAntPattern == "**/a/b/c/d/**/"
|
||||
}
|
||||
|
||||
def 'should set inclusion pattern on config when path pattern was explicitly provided without a separator at the beginning'() {
|
||||
@@ -52,16 +47,14 @@ class ContractDownloaderSpec extends Specification {
|
||||
String contractPath = ['a', 'b', 'c', 'd'].join(File.separator)
|
||||
ContractDownloader contractDownloader = new ContractDownloader(stubDownloader,
|
||||
stubConfiguration, contractPath, '', '', '')
|
||||
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
|
||||
and:
|
||||
stubDownloader.downloadAndUnpackStubJar(_) >> new AbstractMap.SimpleEntry(stubConfiguration, file)
|
||||
when:
|
||||
contractDownloader.unpackedDownloadedContracts(properties)
|
||||
ContractDownloader.InclusionProperties inclusionProperties =
|
||||
contractDownloader.createNewInclusionProperties(file)
|
||||
then:
|
||||
properties.includedContracts.startsWith('^')
|
||||
properties.includedContracts.endsWith('$')
|
||||
properties.includedContracts.contains(fileSeparated('/some/path/to/somewhere(/)?.*/a/b/c/d/.*'))
|
||||
properties.includedRootFolderAntPattern == "**/a/b/c/d/**/"
|
||||
inclusionProperties.includedContracts.startsWith('^')
|
||||
inclusionProperties.includedContracts.endsWith('$')
|
||||
inclusionProperties.includedContracts.contains(fileSeparated('/some/path/to/somewhere(/)?.*/a/b/c/d/.*'))
|
||||
inclusionProperties.includedRootFolderAntPattern == "**/a/b/c/d/**/"
|
||||
}
|
||||
|
||||
private static String fileSeparated(String string) {
|
||||
|
||||
@@ -43,28 +43,41 @@ import org.springframework.cloud.contract.verifier.wiremock.DslToWireMockClientC
|
||||
class RecursiveFilesConverter {
|
||||
|
||||
private final StubGeneratorProvider holder
|
||||
private final ContractVerifierConfigProperties props
|
||||
private final File outMappingsDir
|
||||
private final File contractsDslDir
|
||||
private final List<String> excludedFiles
|
||||
private final String includedContracts
|
||||
private final boolean excludeBuildFolders
|
||||
|
||||
// Use constructor without ContractVerifierConfigProperties
|
||||
@Deprecated
|
||||
RecursiveFilesConverter(ContractVerifierConfigProperties props, StubGeneratorProvider holder = null) {
|
||||
this.props = props
|
||||
this.outMappingsDir = props.stubsOutputDir
|
||||
this.holder = holder ?: new StubGeneratorProvider()
|
||||
this(props.stubsOutputDir, props.contractsDslDir, props.excludedFiles, props.includedContracts, props.excludeBuildFolders, holder)
|
||||
}
|
||||
|
||||
RecursiveFilesConverter(ContractVerifierConfigProperties props, File outMappingsDir, StubGeneratorProvider holder = null) {
|
||||
this.props = props
|
||||
this.outMappingsDir = outMappingsDir
|
||||
// Use constructor without ContractVerifierConfigProperties
|
||||
@Deprecated
|
||||
RecursiveFilesConverter(ContractVerifierConfigProperties props, File stubsOutputDir, StubGeneratorProvider holder = null) {
|
||||
this(stubsOutputDir, props.contractsDslDir, props.excludedFiles, props.includedContracts, props.excludeBuildFolders, holder)
|
||||
}
|
||||
|
||||
RecursiveFilesConverter(File stubsOutputDir, File contractsDslDir, List<String> excludedFiles,
|
||||
String includedContracts, boolean excludeBuildFolders, StubGeneratorProvider holder = null) {
|
||||
this.outMappingsDir = stubsOutputDir
|
||||
this.contractsDslDir = contractsDslDir
|
||||
this.excludedFiles = excludedFiles
|
||||
this.includedContracts = includedContracts
|
||||
this.excludeBuildFolders = excludeBuildFolders
|
||||
this.holder = holder ?: new StubGeneratorProvider()
|
||||
}
|
||||
|
||||
void processFiles() {
|
||||
ContractFileScanner scanner = new ContractFileScannerBuilder()
|
||||
.baseDir(props.contractsDslDir)
|
||||
.excluded(props.excludedFiles as Set)
|
||||
.baseDir(contractsDslDir)
|
||||
.excluded(excludedFiles as Set)
|
||||
.ignored([] as Set)
|
||||
.included([] as Set)
|
||||
.includeMatcher(props.includedContracts)
|
||||
.includeMatcher(includedContracts)
|
||||
.build()
|
||||
ListMultimap<Path, ContractMetadata> contracts = scanner.findContracts()
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -81,7 +94,7 @@ class RecursiveFilesConverter {
|
||||
holder.converterForName(sourceFile.name)
|
||||
try {
|
||||
String path = sourceFile.path
|
||||
if (props.isExcludeBuildFolders()
|
||||
if (excludeBuildFolders
|
||||
&& (
|
||||
matchesPath(path, "target") || matchesPath(path, "build"))) {
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -131,7 +144,7 @@ class RecursiveFilesConverter {
|
||||
}
|
||||
|
||||
private Path createAndReturnTargetDirectory(File sourceFile) {
|
||||
Path relativePath = Paths.get(props.contractsDslDir.toURI()).
|
||||
Path relativePath = Paths.get(contractsDslDir.toURI()).
|
||||
relativize(sourceFile.parentFile.toPath())
|
||||
Path absoluteTargetPath = outMappingsDir.toPath().resolve(relativePath)
|
||||
Files.createDirectories(absoluteTargetPath)
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.junit.rules.TemporaryFolder
|
||||
import spock.lang.Specification
|
||||
|
||||
import org.springframework.cloud.contract.spec.Contract
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
import org.springframework.cloud.contract.verifier.file.ContractMetadata
|
||||
import org.springframework.util.FileSystemUtils
|
||||
|
||||
@@ -50,23 +49,22 @@ class RecursiveFilesConverterSpec extends Specification {
|
||||
|
||||
def "should recursively convert all matching files"() {
|
||||
given:
|
||||
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
|
||||
File originalSourceRootDirectory = new File(this.getClass()
|
||||
.getResource("/converter/source").toURI())
|
||||
properties.contractsDslDir = tmpFolder.newFolder("source")
|
||||
properties.stubsOutputDir = tmpFolder.newFolder("target")
|
||||
File contractsDslDir = tmpFolder.newFolder("source")
|
||||
File stubsOutputDir = tmpFolder.newFolder("target")
|
||||
FileSystemUtils
|
||||
.copyRecursively(originalSourceRootDirectory, properties.contractsDslDir)
|
||||
.copyRecursively(originalSourceRootDirectory, contractsDslDir)
|
||||
and:
|
||||
RecursiveFilesConverter recursiveFilesConverter = new RecursiveFilesConverter(properties)
|
||||
RecursiveFilesConverter recursiveFilesConverter = new RecursiveFilesConverter(stubsOutputDir, contractsDslDir, new ArrayList<>(), ".*", false)
|
||||
when:
|
||||
recursiveFilesConverter.processFiles()
|
||||
then:
|
||||
Collection<File> createdFiles = [] as List
|
||||
properties.stubsOutputDir.
|
||||
stubsOutputDir.
|
||||
eachFileRecurse(FileType.FILES) { createdFiles << it }
|
||||
Set<String> relativizedCreatedFiles =
|
||||
getRelativePathsForFilesInDirectory(createdFiles, properties.stubsOutputDir)
|
||||
getRelativePathsForFilesInDirectory(createdFiles, stubsOutputDir)
|
||||
relativizedCreatedFiles == EXPECTED_TARGET_FILES
|
||||
and:
|
||||
createdFiles.each { assert it.text.contains("uuid") }
|
||||
@@ -74,24 +72,23 @@ class RecursiveFilesConverterSpec extends Specification {
|
||||
|
||||
def "should recursively convert matching files with exlusions"() {
|
||||
given:
|
||||
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
|
||||
File originalSourceRootDirectory = new File(this.getClass()
|
||||
.getResource("/converter/source").toURI())
|
||||
properties.contractsDslDir = tmpFolder.newFolder("source")
|
||||
properties.stubsOutputDir = tmpFolder.newFolder("target")
|
||||
properties.excludedFiles = ["dir1/**"]
|
||||
File contractsDslDir = tmpFolder.newFolder("source")
|
||||
File stubsOutputDir = tmpFolder.newFolder("target")
|
||||
List<String> excludedFiles = ["dir1/**"]
|
||||
FileSystemUtils
|
||||
.copyRecursively(originalSourceRootDirectory, properties.contractsDslDir)
|
||||
.copyRecursively(originalSourceRootDirectory, contractsDslDir)
|
||||
and:
|
||||
RecursiveFilesConverter recursiveFilesConverter = new RecursiveFilesConverter(properties)
|
||||
RecursiveFilesConverter recursiveFilesConverter = new RecursiveFilesConverter(stubsOutputDir, contractsDslDir, excludedFiles, ".*", false)
|
||||
when:
|
||||
recursiveFilesConverter.processFiles()
|
||||
then:
|
||||
Collection<File> createdFiles = [] as List
|
||||
properties.stubsOutputDir.
|
||||
stubsOutputDir.
|
||||
eachFileRecurse(FileType.FILES) { createdFiles << it }
|
||||
Set<String> relativizedCreatedFiles =
|
||||
getRelativePathsForFilesInDirectory(createdFiles, properties.stubsOutputDir)
|
||||
getRelativePathsForFilesInDirectory(createdFiles, stubsOutputDir)
|
||||
[Paths.get("dslRoot.json"), Paths.
|
||||
get("dir2/dsl2.json")] as Set == relativizedCreatedFiles as Set
|
||||
and:
|
||||
@@ -100,7 +97,7 @@ class RecursiveFilesConverterSpec extends Specification {
|
||||
|
||||
def "on failure should break processing and throw meaningful exception"() {
|
||||
given:
|
||||
def sourceFile = tmpFolder.newFile("test.groovy")
|
||||
File sourceFile = tmpFolder.newFile("test.groovy")
|
||||
sourceFile.text = """\
|
||||
org.springframework.cloud.contract.spec.Contract.make {
|
||||
request {
|
||||
@@ -112,28 +109,27 @@ org.springframework.cloud.contract.spec.Contract.make {
|
||||
}
|
||||
}"""
|
||||
and:
|
||||
def stubGenerator = Stub(StubGenerator)
|
||||
StubGenerator stubGenerator = Stub(StubGenerator)
|
||||
stubGenerator.canHandleFileName(_) >> { true }
|
||||
stubGenerator.convertContents(_, _) >> {
|
||||
throw new NullPointerException("Test conversion error")
|
||||
}
|
||||
stubGenerator
|
||||
.generateOutputFileNameForInput(_) >> { String inputFileName -> "${inputFileName}2" }
|
||||
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
|
||||
properties.contractsDslDir = tmpFolder.root
|
||||
properties.stubsOutputDir = tmpFolder.root
|
||||
RecursiveFilesConverter recursiveFilesConverter = new RecursiveFilesConverter(properties, new StubGeneratorProvider([stubGenerator]))
|
||||
File contractsDslDir = tmpFolder.root
|
||||
File stubsOutputDir = tmpFolder.root
|
||||
RecursiveFilesConverter recursiveFilesConverter = new RecursiveFilesConverter(stubsOutputDir, contractsDslDir, new ArrayList<>(), ".*", false, new StubGeneratorProvider([stubGenerator]))
|
||||
when:
|
||||
recursiveFilesConverter.processFiles()
|
||||
then:
|
||||
def e = thrown(ConversionContractVerifierException)
|
||||
ConversionContractVerifierException e = thrown(ConversionContractVerifierException)
|
||||
e.message?.contains(sourceFile.name)
|
||||
e.cause?.message == "Test conversion error"
|
||||
}
|
||||
|
||||
def "should convert contract into stub using all possible converters"() {
|
||||
given:
|
||||
def sourceFile = tmpFolder.newFile("test.groovy")
|
||||
File sourceFile = tmpFolder.newFile("test.groovy")
|
||||
sourceFile.text = """
|
||||
org.springframework.cloud.contract.spec.Contract.make {
|
||||
request {
|
||||
@@ -150,11 +146,10 @@ org.springframework.cloud.contract.spec.Contract.make {
|
||||
and:
|
||||
StubGenerator stubGenerator2 = stubGenerator("bar")
|
||||
and:
|
||||
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
|
||||
properties.contractsDslDir = tmpFolder.root
|
||||
properties.stubsOutputDir = tmpFolder.root
|
||||
File contractsDslDir = tmpFolder.root
|
||||
File stubsOutputDir = tmpFolder.root
|
||||
and:
|
||||
RecursiveFilesConverter recursiveFilesConverter = new RecursiveFilesConverter(properties, new StubGeneratorProvider([stubGenerator1, stubGenerator2]))
|
||||
RecursiveFilesConverter recursiveFilesConverter = new RecursiveFilesConverter(stubsOutputDir, contractsDslDir, new ArrayList<>(), ".*", false, new StubGeneratorProvider([stubGenerator1, stubGenerator2]))
|
||||
when:
|
||||
recursiveFilesConverter.processFiles()
|
||||
then:
|
||||
@@ -163,7 +158,7 @@ org.springframework.cloud.contract.spec.Contract.make {
|
||||
|
||||
def "should not create stub file when generated stub is empty"() {
|
||||
given:
|
||||
def sourceFile = tmpFolder.newFile("test.groovy")
|
||||
File sourceFile = tmpFolder.newFile("test.groovy")
|
||||
sourceFile.text = """
|
||||
org.springframework.cloud.contract.spec.Contract.make {
|
||||
request {
|
||||
@@ -178,15 +173,14 @@ org.springframework.cloud.contract.spec.Contract.make {
|
||||
and:
|
||||
StubGenerator stubGenerator = stubGenerator("")
|
||||
and:
|
||||
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
|
||||
properties.contractsDslDir = tmpFolder.root
|
||||
properties.stubsOutputDir = tmpFolder.newFolder("target")
|
||||
File contractsDslDir = tmpFolder.root
|
||||
File stubsOutputDir = tmpFolder.newFolder("target")
|
||||
and:
|
||||
RecursiveFilesConverter recursiveFilesConverter = new RecursiveFilesConverter(properties, new StubGeneratorProvider([stubGenerator]))
|
||||
RecursiveFilesConverter recursiveFilesConverter = new RecursiveFilesConverter(stubsOutputDir, contractsDslDir, new ArrayList<>(), ".*", false, new StubGeneratorProvider([stubGenerator]))
|
||||
when:
|
||||
recursiveFilesConverter.processFiles()
|
||||
then:
|
||||
properties.stubsOutputDir.list().toList().isEmpty()
|
||||
stubsOutputDir.list().toList().isEmpty()
|
||||
}
|
||||
|
||||
private static Set<Path> getRelativePathsForFilesInDirectory(Collection<File> createdFiles, File targetRootDirectory) {
|
||||
|
||||
@@ -17,133 +17,133 @@
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.ToString
|
||||
import org.apache.commons.logging.Log
|
||||
import org.apache.commons.logging.LogFactory
|
||||
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.MapProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
|
||||
import org.springframework.cloud.contract.verifier.config.TestFramework
|
||||
import org.springframework.cloud.contract.verifier.config.TestMode
|
||||
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Anatoliy Balakirev
|
||||
*/
|
||||
@ToString
|
||||
@CompileStatic
|
||||
class ContractVerifierExtension {
|
||||
|
||||
private static final Log log = LogFactory.getLog(ContractVerifierExtension)
|
||||
|
||||
/**
|
||||
* For which unit test library tests should be generated
|
||||
* @deprecated - use {@code testFramework}
|
||||
*/
|
||||
@Deprecated
|
||||
TestFramework targetFramework
|
||||
|
||||
@Deprecated
|
||||
void setTargetFramework(TestFramework targetFramework) {
|
||||
log.warn("Please use the [testFramework] field. This one is deprecated")
|
||||
setTestFramework(targetFramework)
|
||||
this.testFramework.set(targetFramework)
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
TestFramework getTargetFramework() {
|
||||
return getTestFramework()
|
||||
return getTestFramework().get()
|
||||
}
|
||||
|
||||
/**
|
||||
* For which unit test library tests should be generated
|
||||
*/
|
||||
TestFramework testFramework = TestFramework.JUNIT
|
||||
Property<TestFramework> testFramework
|
||||
|
||||
/**
|
||||
* Which mechanism should be used to invoke REST calls during tests
|
||||
*/
|
||||
TestMode testMode = TestMode.MOCKMVC
|
||||
Property<TestMode> testMode
|
||||
|
||||
/**
|
||||
* Base package for generated tests
|
||||
*/
|
||||
String basePackageForTests
|
||||
Property<String> basePackageForTests
|
||||
|
||||
/**
|
||||
* Class which all generated tests should extend
|
||||
*/
|
||||
String baseClassForTests
|
||||
Property<String> baseClassForTests
|
||||
|
||||
/**
|
||||
* Suffix for generated test classes, like Spec or Test
|
||||
*/
|
||||
String nameSuffixForTests
|
||||
Property<String> nameSuffixForTests
|
||||
|
||||
/**
|
||||
* Rule class that should be added to generated tests
|
||||
*/
|
||||
String ruleClassForTests
|
||||
Property<String> ruleClassForTests
|
||||
|
||||
/**
|
||||
* Patterns that should not be taken into account for processing
|
||||
*/
|
||||
List<String> excludedFiles = []
|
||||
ListProperty<String> excludedFiles
|
||||
|
||||
/**
|
||||
* Patterns that should be taken into account for processing
|
||||
*/
|
||||
List<String> includedFiles = []
|
||||
ListProperty<String> includedFiles
|
||||
|
||||
/**
|
||||
* Patterns for which generated tests should be @Ignored
|
||||
*/
|
||||
List<String> ignoredFiles = []
|
||||
ListProperty<String> ignoredFiles
|
||||
|
||||
/**
|
||||
* Imports that should be added to generated tests
|
||||
*/
|
||||
String[] imports = []
|
||||
ListProperty<String> imports
|
||||
|
||||
/**
|
||||
* Static imports that should be added to generated tests
|
||||
*/
|
||||
String[] staticImports = []
|
||||
ListProperty<String> staticImports
|
||||
|
||||
/**
|
||||
* Directory containing contracts written using the GroovyDSL
|
||||
*/
|
||||
File contractsDslDir
|
||||
DirectoryProperty contractsDslDir
|
||||
|
||||
/**
|
||||
* Test source directory where tests generated from Groovy DSL should be placed
|
||||
*/
|
||||
File generatedTestSourcesDir
|
||||
DirectoryProperty generatedTestSourcesDir
|
||||
|
||||
/**
|
||||
* Test resource directory where tests generated from Groovy DSL should be referenced
|
||||
*/
|
||||
File generatedTestResourcesDir
|
||||
DirectoryProperty generatedTestResourcesDir
|
||||
|
||||
/**
|
||||
* Dir where the generated stubs from Groovy DSL should be placed.
|
||||
* You can then mention them in your packaging task to create jar with stubs
|
||||
*/
|
||||
File stubsOutputDir
|
||||
DirectoryProperty stubsOutputDir
|
||||
|
||||
/**
|
||||
* Suffix for the generated Stubs Jar task
|
||||
*/
|
||||
String stubsSuffix = 'stubs'
|
||||
Property<String> stubsSuffix
|
||||
|
||||
/**
|
||||
* Incubating feature. You can check the size of JSON arrays. If not turned on
|
||||
* explicitly will be disabled.
|
||||
*/
|
||||
Boolean assertJsonSize = false
|
||||
Property<Boolean> assertJsonSize
|
||||
|
||||
/**
|
||||
* When enabled, this flag will tell stub runner to throw an exception when no stubs /
|
||||
* contracts were found.
|
||||
*/
|
||||
boolean failOnNoContracts = true
|
||||
Property<Boolean> failOnNoContracts
|
||||
|
||||
/**
|
||||
* If set to true then if any contracts that are in progress are found, will break the
|
||||
@@ -151,14 +151,14 @@ class ContractVerifierExtension {
|
||||
* contracts in progress and take into consideration that you might be causing false
|
||||
* positive test execution results on the consumer side.
|
||||
*/
|
||||
boolean failOnInProgress = true;
|
||||
Property<Boolean> failOnInProgress
|
||||
|
||||
ContractRepository contractRepository = new ContractRepository()
|
||||
ContractRepository contractRepository
|
||||
|
||||
/**
|
||||
* Dependency that contains packaged contracts
|
||||
*/
|
||||
Dependency contractDependency = new Dependency()
|
||||
Dependency contractDependency
|
||||
|
||||
/**
|
||||
* The path in the JAR with all the contracts where contracts for this particular service lay.
|
||||
@@ -167,12 +167,12 @@ class ContractVerifierExtension {
|
||||
* 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
|
||||
Property<String> contractsPath
|
||||
|
||||
/**
|
||||
* Picks the mode in which stubs will be found and registered
|
||||
*/
|
||||
StubRunnerProperties.StubsMode contractsMode = StubRunnerProperties.StubsMode.CLASSPATH
|
||||
Property<StubRunnerProperties.StubsMode> contractsMode
|
||||
|
||||
/**
|
||||
* A package that contains all the base clases for generated tests. If your contract resides in a location
|
||||
@@ -181,7 +181,7 @@ class ContractVerifierExtension {
|
||||
* have the package {@code com.example.contracts.base} and name {@code ExampleV1Base}. As you can see
|
||||
* it will take the two last folders to and attach {@code Base} to its name.
|
||||
*/
|
||||
String packageWithBaseClasses
|
||||
Property<String> packageWithBaseClasses
|
||||
|
||||
/**
|
||||
* A way to override any base class mappings. The keys are regular expressions on the package name
|
||||
@@ -194,14 +194,14 @@ class ContractVerifierExtension {
|
||||
* When a contract's package matches the provided regular expression then extending class will be the one
|
||||
* provided in the map - in this case {@code com.example.SomeBaseClass}
|
||||
*/
|
||||
Map<String, String> baseClassMappings = [:]
|
||||
MapProperty<String, String> baseClassMappings
|
||||
|
||||
/**
|
||||
* If set to true then the {@code target} or {@code build} folders are getting
|
||||
* excluded from any operations. This is used out of the box when working with
|
||||
* common repo with contracts.
|
||||
*/
|
||||
boolean excludeBuildFolders = false
|
||||
Property<Boolean> excludeBuildFolders
|
||||
|
||||
/**
|
||||
* If set to {@code true} will not assert whether the downloaded stubs / contract
|
||||
@@ -210,23 +210,23 @@ class ContractVerifierExtension {
|
||||
* @deprecated - with 2.1.0 this option is redundant
|
||||
*/
|
||||
@Deprecated
|
||||
boolean contractsSnapshotCheckSkip = false
|
||||
Property<Boolean> contractsSnapshotCheckSkip
|
||||
|
||||
/**
|
||||
* If set to {@code false} will NOT delete stubs from a temporary
|
||||
* folder after running tests
|
||||
*/
|
||||
boolean deleteStubsAfterTest = true
|
||||
Property<Boolean> deleteStubsAfterTest
|
||||
|
||||
/**
|
||||
* If {@code true} then will convert contracts to a YAML representation
|
||||
*/
|
||||
boolean convertToYaml = false
|
||||
Property<Boolean> convertToYaml
|
||||
|
||||
/**
|
||||
* Map of properties that can be passed to custom {@link org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder}
|
||||
*/
|
||||
Map<String, String> contractsProperties = [:]
|
||||
MapProperty<String, String> contractsProperties
|
||||
|
||||
void contractDependency(@DelegatesTo(Dependency) Closure closure) {
|
||||
closure.delegate = contractDependency
|
||||
@@ -243,175 +243,167 @@ class ContractVerifierExtension {
|
||||
closure.call()
|
||||
}
|
||||
|
||||
void contractsProperties(Map<String, String> props) {
|
||||
contractsProperties = props
|
||||
}
|
||||
|
||||
/**
|
||||
* Is set to true will not provide the default publication task
|
||||
*/
|
||||
boolean disableStubPublication = false
|
||||
Property<Boolean> disableStubPublication
|
||||
|
||||
void disableStubPublication(boolean disableStubPublication) {
|
||||
this.disableStubPublication = disableStubPublication
|
||||
// Added for backward compatibility only. Use setter of dedicated enum type
|
||||
@Deprecated
|
||||
void setTestMode(String testMode) {
|
||||
if (testMode != null) {
|
||||
this.testMode.set(TestMode.valueOf(testMode.toUpperCase()))
|
||||
}
|
||||
}
|
||||
|
||||
void failOnNoContracts(boolean failOnNoContracts) {
|
||||
this.failOnNoContracts = failOnNoContracts
|
||||
// Added for backward compatibility only. Use setter of dedicated enum type
|
||||
@Deprecated
|
||||
void setTestFramework(String testFramework) {
|
||||
if (testFramework != null) {
|
||||
this.testFramework.set(TestFramework.valueOf(testFramework.toUpperCase()))
|
||||
}
|
||||
}
|
||||
|
||||
void failOnInProgress(boolean failOnInProgress) {
|
||||
this.failOnInProgress = failOnInProgress
|
||||
// Added for backward compatibility only. Use setter of dedicated enum type
|
||||
@Deprecated
|
||||
void setContractsMode(String contractsMode) {
|
||||
if (contractsMode != null) {
|
||||
this.contractsMode.set(StubRunnerProperties.StubsMode.valueOf(contractsMode.toUpperCase()))
|
||||
}
|
||||
}
|
||||
|
||||
ContractVerifierExtension copy() {
|
||||
return new ContractVerifierExtension(
|
||||
testFramework: this.testFramework,
|
||||
testMode: this.testMode,
|
||||
basePackageForTests: this.basePackageForTests,
|
||||
baseClassForTests: this.baseClassForTests,
|
||||
nameSuffixForTests: this.nameSuffixForTests,
|
||||
ruleClassForTests: this.ruleClassForTests,
|
||||
excludedFiles: new ArrayList<String>(this.excludedFiles),
|
||||
includedFiles: new ArrayList<String>(this.includedFiles),
|
||||
ignoredFiles: new ArrayList<String>(this.ignoredFiles),
|
||||
imports: Arrays.asList(this.imports).toArray() as String[],
|
||||
staticImports: Arrays.asList(this.staticImports).toArray() as String[],
|
||||
contractsDslDir: this.contractsDslDir,
|
||||
generatedTestSourcesDir: this.generatedTestSourcesDir,
|
||||
generatedTestResourcesDir: this.generatedTestResourcesDir,
|
||||
stubsOutputDir: this.stubsOutputDir,
|
||||
stubsSuffix: this.stubsSuffix,
|
||||
assertJsonSize: this.assertJsonSize,
|
||||
failOnNoContracts: this.failOnNoContracts,
|
||||
failOnInProgress: this.failOnInProgress,
|
||||
contractRepository: new ContractRepository(
|
||||
repositoryUrl: this.contractRepository.repositoryUrl,
|
||||
username: this.contractRepository.username,
|
||||
password: this.contractRepository.password,
|
||||
proxyPort: this.contractRepository.proxyPort,
|
||||
proxyHost: this.contractRepository.proxyHost,
|
||||
cacheDownloadedContracts: this.contractRepository.cacheDownloadedContracts,
|
||||
),
|
||||
contractDependency: new Dependency(
|
||||
groupId: this.contractDependency.groupId,
|
||||
artifactId: this.contractDependency.artifactId,
|
||||
classifier: this.contractDependency.classifier,
|
||||
version: this.contractDependency.version,
|
||||
stringNotation: this.contractDependency.stringNotation
|
||||
),
|
||||
contractsPath: this.contractsPath,
|
||||
contractsMode: this.contractsMode,
|
||||
packageWithBaseClasses: this.packageWithBaseClasses,
|
||||
baseClassMappings: new HashMap<String, String>(this.baseClassMappings),
|
||||
excludeBuildFolders: this.excludeBuildFolders,
|
||||
deleteStubsAfterTest: this.deleteStubsAfterTest,
|
||||
convertToYaml: this.convertToYaml,
|
||||
contractsProperties: new HashMap<String, String>(this.contractsProperties)
|
||||
)
|
||||
@Inject
|
||||
ContractVerifierExtension(ObjectFactory objects) {
|
||||
this.testFramework = objects.property(TestFramework).convention(TestFramework.JUNIT)
|
||||
this.testMode = objects.property(TestMode).convention(TestMode.MOCKMVC)
|
||||
this.basePackageForTests = objects.property(String)
|
||||
this.baseClassForTests = objects.property(String)
|
||||
this.nameSuffixForTests = objects.property(String)
|
||||
this.ruleClassForTests = objects.property(String)
|
||||
this.excludedFiles = objects.listProperty(String).convention([])
|
||||
this.includedFiles = objects.listProperty(String).convention([])
|
||||
this.ignoredFiles = objects.listProperty(String).convention([])
|
||||
this.imports = objects.listProperty(String).convention([])
|
||||
this.staticImports = objects.listProperty(String).convention([])
|
||||
this.contractsDslDir = objects.directoryProperty()
|
||||
this.generatedTestSourcesDir = objects.directoryProperty()
|
||||
this.generatedTestResourcesDir = objects.directoryProperty()
|
||||
this.stubsOutputDir = objects.directoryProperty()
|
||||
this.stubsSuffix = objects.property(String).convention("stubs")
|
||||
this.assertJsonSize = objects.property(Boolean).convention(false)
|
||||
this.failOnNoContracts = objects.property(Boolean).convention(true)
|
||||
this.failOnInProgress = objects.property(Boolean).convention(true)
|
||||
this.contractRepository = new ContractRepository(objects)
|
||||
this.contractDependency = new Dependency(objects)
|
||||
this.contractsPath = objects.property(String)
|
||||
this.contractsMode = objects.property(StubRunnerProperties.StubsMode).convention(StubRunnerProperties.StubsMode.CLASSPATH)
|
||||
this.packageWithBaseClasses = objects.property(String)
|
||||
this.baseClassMappings = objects.mapProperty(String, String).convention([:])
|
||||
this.excludeBuildFolders = objects.property(Boolean).convention(false)
|
||||
this.contractsSnapshotCheckSkip = objects.property(Boolean).convention(false)
|
||||
this.deleteStubsAfterTest = objects.property(Boolean).convention(true)
|
||||
this.convertToYaml = objects.property(Boolean).convention(false)
|
||||
this.contractsProperties = objects.mapProperty(String, String).convention([:])
|
||||
this.disableStubPublication = objects.property(Boolean).convention(false)
|
||||
}
|
||||
|
||||
@ToString(includeNames = true, includePackage = false)
|
||||
static class Dependency {
|
||||
String groupId
|
||||
String artifactId
|
||||
String classifier
|
||||
String version
|
||||
String stringNotation
|
||||
@Input
|
||||
@Optional
|
||||
Property<String> groupId
|
||||
@Input
|
||||
@Optional
|
||||
Property<String> artifactId
|
||||
@Input
|
||||
@Optional
|
||||
Property<String> version
|
||||
@Input
|
||||
@Optional
|
||||
Property<String> classifier
|
||||
@Input
|
||||
@Optional
|
||||
Property<String> stringNotation
|
||||
|
||||
void groupId(String groupId) {
|
||||
this.groupId = groupId
|
||||
@Inject
|
||||
Dependency(ObjectFactory objects) {
|
||||
groupId = objects.property(String)
|
||||
artifactId = objects.property(String)
|
||||
version = objects.property(String)
|
||||
classifier = objects.property(String)
|
||||
stringNotation = objects.property(String)
|
||||
}
|
||||
|
||||
void artifactId(String artifactId) {
|
||||
this.artifactId = artifactId
|
||||
}
|
||||
|
||||
void classifier(String classifier) {
|
||||
this.classifier = classifier
|
||||
}
|
||||
|
||||
void version(String version) {
|
||||
this.version = version
|
||||
}
|
||||
|
||||
void stringNotation(String stringNotation) {
|
||||
this.stringNotation = stringNotation
|
||||
@Override
|
||||
String toString() {
|
||||
return "Dependency{" +
|
||||
"groupId=" + groupId.getOrNull() +
|
||||
", artifactId=" + artifactId.getOrNull() +
|
||||
", classifier=" + classifier.getOrNull() +
|
||||
", version=" + version.getOrNull() +
|
||||
", stringNotation=" + stringNotation.getOrNull() +
|
||||
'}'
|
||||
}
|
||||
}
|
||||
|
||||
@ToString(includeNames = true, includePackage = false)
|
||||
static class BaseClassMapping {
|
||||
private final Map<String, String> delegate
|
||||
private final MapProperty<String, String> delegate
|
||||
|
||||
BaseClassMapping(Map<String, String> delegate) {
|
||||
private BaseClassMapping(MapProperty<String, String> delegate) {
|
||||
this.delegate = delegate
|
||||
}
|
||||
|
||||
void baseClassMapping(String packageRegex, String fqnBaseClass) {
|
||||
this.delegate[packageRegex] = fqnBaseClass
|
||||
delegate.put(packageRegex, fqnBaseClass)
|
||||
}
|
||||
|
||||
void baseClassMapping(Map mapping) {
|
||||
this.delegate.putAll(mapping)
|
||||
void baseClassMapping(Map<String, String> mapping) {
|
||||
delegate.putAll(mapping)
|
||||
}
|
||||
}
|
||||
|
||||
@ToString(includeNames = true, includePackage = false)
|
||||
// This class is used as an input to the tasks, so all fields are marked as `@Input` to allow incremental build
|
||||
static class ContractRepository {
|
||||
@Input
|
||||
@Optional
|
||||
Property<String> repositoryUrl
|
||||
@Input
|
||||
@Optional
|
||||
Property<String> username
|
||||
@Input
|
||||
@Optional
|
||||
Property<String> password
|
||||
@Input
|
||||
@Optional
|
||||
Property<Integer> proxyPort
|
||||
@Input
|
||||
@Optional
|
||||
Property<String> proxyHost
|
||||
/**
|
||||
* Repository URL
|
||||
* If set to true then will cache the folder where non snapshot contract artifacts got downloaded.
|
||||
*/
|
||||
String repositoryUrl
|
||||
@Input
|
||||
Property<Boolean> cacheDownloadedContracts
|
||||
|
||||
/**
|
||||
* Repository username
|
||||
*/
|
||||
String username
|
||||
|
||||
/**
|
||||
* Repository password
|
||||
*/
|
||||
String password
|
||||
|
||||
/**
|
||||
* Repository proxy port
|
||||
*/
|
||||
Integer proxyPort
|
||||
|
||||
/**
|
||||
* Repository proxy host
|
||||
*/
|
||||
String proxyHost
|
||||
|
||||
/**
|
||||
* If set to true then will cache the folder where non snapshot contract artifacts
|
||||
* got downloaded.
|
||||
*/
|
||||
boolean cacheDownloadedContracts = true
|
||||
|
||||
void repositoryUrl(String repositoryUrl) {
|
||||
this.repositoryUrl = repositoryUrl
|
||||
@Inject
|
||||
ContractRepository(ObjectFactory objects) {
|
||||
this.repositoryUrl = objects.property(String)
|
||||
this.username = objects.property(String)
|
||||
this.password = objects.property(String)
|
||||
this.proxyHost = objects.property(String)
|
||||
this.proxyPort = objects.property(Integer)
|
||||
this.cacheDownloadedContracts = objects.property(Boolean).convention(true)
|
||||
}
|
||||
|
||||
void username(String username) {
|
||||
this.username = username
|
||||
}
|
||||
|
||||
void password(String password) {
|
||||
this.password = password
|
||||
}
|
||||
|
||||
void proxyPort(Integer proxyPort) {
|
||||
this.proxyPort = proxyPort
|
||||
}
|
||||
|
||||
void proxyHost(String proxyHost) {
|
||||
this.proxyHost = proxyHost
|
||||
}
|
||||
|
||||
void cacheDownloadedContracts(boolean cacheDownloadedContracts) {
|
||||
this.cacheDownloadedContracts = cacheDownloadedContracts
|
||||
@Override
|
||||
String toString() {
|
||||
return "ContractRepository{" +
|
||||
"repositoryUrl=" + repositoryUrl.getOrNull() +
|
||||
", username=" + username.getOrNull() +
|
||||
", password=" + password.getOrNull() +
|
||||
", proxyPort=" + proxyPort.getOrNull() +
|
||||
", proxyHost=" + proxyHost.getOrNull() +
|
||||
", cacheDownloadedContracts=" + cacheDownloadedContracts.get() +
|
||||
'}'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,115 +16,138 @@
|
||||
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import groovy.transform.CompileDynamic
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.PackageScope
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.internal.ConventionTask
|
||||
import org.gradle.api.logging.Logger
|
||||
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.provider.Provider
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
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.verifier.config.ContractVerifierConfigProperties
|
||||
import org.springframework.cloud.contract.verifier.converter.ToYamlConverter
|
||||
|
||||
// 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
|
||||
* generate the jar. It takes into consideration the inclusion
|
||||
* patterns when working with repo with shared contracts.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Anatoliy Balakirev
|
||||
* @since 1.0.2
|
||||
*/
|
||||
@PackageScope
|
||||
@CompileStatic
|
||||
class ContractsCopyTask extends ConventionTask {
|
||||
private static final String ORIGINAL_PATH = "original"
|
||||
class ContractsCopyTask extends DefaultTask {
|
||||
|
||||
ContractVerifierExtension extension
|
||||
GradleContractsDownloader downloader
|
||||
static final String TASK_NAME = 'copyContracts'
|
||||
static final String CONTRACTS = "contracts"
|
||||
static final String BACKUP = "original"
|
||||
@Nested
|
||||
Config config
|
||||
|
||||
static class Config {
|
||||
@Input
|
||||
Provider<Boolean> convertToYaml
|
||||
@Input
|
||||
Provider<Boolean> excludeBuildFolders
|
||||
@Input
|
||||
Provider<Boolean> failOnNoContracts
|
||||
@Input
|
||||
Provider<String> includedRootFolderAntPattern
|
||||
@InputDirectory
|
||||
Provider<Directory> contractsDirectory
|
||||
|
||||
@OutputDirectory
|
||||
DirectoryProperty copiedContractsFolder
|
||||
@Optional
|
||||
@OutputDirectory
|
||||
DirectoryProperty backupContractsFolder
|
||||
}
|
||||
@TaskAction
|
||||
void copy() {
|
||||
ContractVerifierConfigProperties props = ExtensionToProperties.fromExtension(getExtension())
|
||||
File file = getDownloader().downloadAndUnpackContractsIfRequired(getExtension(), props)
|
||||
file = contractsSubDirIfPresent(logger, file)
|
||||
throwExceptionWhenFailOnNoContracts(file)
|
||||
String antPattern = "${props.includedRootFolderAntPattern}*.*"
|
||||
void sync() {
|
||||
File contractsDirectory = config.contractsDirectory.get().asFile
|
||||
throwExceptionWhenFailOnNoContracts(contractsDirectory)
|
||||
String antPattern = "${config.includedRootFolderAntPattern.get()}*.*"
|
||||
String slashSeparatedGroupId = project.group.toString().replace(".", File.separator)
|
||||
String slashSeparatedAntPattern = antPattern.replace(slashSeparatedGroupId, project.group.toString())
|
||||
String root = root(props)
|
||||
File outputContractsFolder = outputContractsFolder(root)
|
||||
project.logger.info("Downloading and unpacking files from [$file] to [$outputContractsFolder]. The inclusion ant patterns are [${antPattern}] and [${slashSeparatedAntPattern}]")
|
||||
copy(file, antPattern, slashSeparatedAntPattern, props, outputContractsFolder)
|
||||
if (getExtension().isConvertToYaml()) {
|
||||
convertBackedUpDslsToYaml(root, file, antPattern, slashSeparatedAntPattern, props, outputContractsFolder)
|
||||
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()) {
|
||||
convertBackedUpDslsToYaml(contractsDirectory, antPattern, slashSeparatedAntPattern, output, config.excludeBuildFolders.get())
|
||||
}
|
||||
}
|
||||
|
||||
static Config fromExtension(ContractVerifierExtension extension, TaskProvider<InitContractsTask> initContractsTask, 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 },
|
||||
|
||||
copiedContractsFolder: createTaskOutput(root, extension.stubsOutputDir, ContractsCopyTask.CONTRACTS, project),
|
||||
backupContractsFolder: createTaskOutput(root, extension.stubsOutputDir, ContractsCopyTask.BACKUP, project)
|
||||
)
|
||||
}
|
||||
|
||||
private void convertBackedUpDslsToYaml(File file, String antPattern, String slashSeparatedAntPattern, File outputContractsFolder, boolean excludeBuildFolders) {
|
||||
sync(file, antPattern, slashSeparatedAntPattern, excludeBuildFolders, config.backupContractsFolder.get().asFile)
|
||||
ToYamlConverter.replaceContractWithYaml(outputContractsFolder)
|
||||
logger.info("Replaced DSL files with their YAML representation at [" + outputContractsFolder + "]")
|
||||
}
|
||||
|
||||
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) {
|
||||
spec.with {
|
||||
from(file)
|
||||
// by default group id is slash separated...
|
||||
include(antPattern)
|
||||
// ...we also want to allow dot separation
|
||||
include(slashSeparatedAntPattern)
|
||||
if (excludeBuildFolders) {
|
||||
exclude "**/target/**", "**/build/**", "**/.mvn/**", "**/.gradle/**"
|
||||
}
|
||||
into(outputContractsFolder)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private static DirectoryProperty createTaskOutput(String root, DirectoryProperty stubsOutputDir, String suffix, Project project) {
|
||||
Provider<Directory> provider = stubsOutputDir.flatMap {
|
||||
Directory dir = it
|
||||
File output = project.file("${dir.asFile}/${root}/${suffix}")
|
||||
|
||||
DirectoryProperty property = project.objects.directoryProperty()
|
||||
property.set(output)
|
||||
return property
|
||||
}
|
||||
DirectoryProperty property = project.objects.directoryProperty();
|
||||
property.set(provider)
|
||||
return property
|
||||
}
|
||||
|
||||
private void throwExceptionWhenFailOnNoContracts(File file) {
|
||||
if (getExtension().isFailOnNoContracts() && (!file.exists() || file.listFiles().length == 0)) {
|
||||
if (config.failOnNoContracts.get() && (!file.exists() || file.listFiles().length == 0)) {
|
||||
throw new GradleException("Contracts could not be found: ["
|
||||
+ file.getAbsolutePath()
|
||||
+ "] .\nPlease make sure that the contracts were defined, or set the [failOnNoContracts] flag to [false]")
|
||||
}
|
||||
}
|
||||
|
||||
@CompileDynamic
|
||||
private File outputContractsFolder(String root) {
|
||||
File outputContractsFolder = outputFolder(root, "contracts")
|
||||
ext.contractsDslDir = outputContractsFolder
|
||||
return outputContractsFolder
|
||||
}
|
||||
|
||||
@CompileDynamic
|
||||
private String root(ContractVerifierConfigProperties props) {
|
||||
String root = OutputFolderBuilder.buildRootPath(project)
|
||||
ext.contractVerifierConfigProperties = props
|
||||
return root
|
||||
}
|
||||
|
||||
@CompileDynamic
|
||||
private void convertBackedUpDslsToYaml(String root, File file, String antPattern, String slashSeparatedAntPattern, ContractVerifierConfigProperties props, File outputContractsFolder) {
|
||||
File originalContracts = outputFolder(root, ORIGINAL_PATH)
|
||||
copy(file, antPattern, slashSeparatedAntPattern, props, originalContracts)
|
||||
ToYamlConverter.replaceContractWithYaml(outputContractsFolder)
|
||||
project.logger.
|
||||
info("Replaced DSL files with their YAML representation at [" + ext.contractsDslDir + "]")
|
||||
}
|
||||
|
||||
@CompileDynamic
|
||||
protected WorkResult copy(File file, String antPattern, String slashSeparatedAntPattern, props, File outputContractsFolder) {
|
||||
return project.copy {
|
||||
from(file)
|
||||
// by default group id is slash separated...
|
||||
include(antPattern)
|
||||
// ...we also want to allow dot separation
|
||||
include(slashSeparatedAntPattern)
|
||||
if (props.isExcludeBuildFolders()) {
|
||||
exclude "**/target/**", "**/build/**", "**/.mvn/**", "**/.gradle/**"
|
||||
}
|
||||
into(outputContractsFolder)
|
||||
}
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
private File outputFolder(String root, String suffix) {
|
||||
return getExtension().stubsOutputDir != null ?
|
||||
project.file("${getExtension().stubsOutputDir}/${root}/${suffix}") :
|
||||
project.file("${project.buildDir}/stubs/${root}/${suffix}")
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
private File contractsSubDirIfPresent(Logger logger, File contractsDirectory) {
|
||||
File contracts = new File(contractsDirectory, "contracts")
|
||||
if (contracts.exists()) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Contracts folder found [" + contracts + "]")
|
||||
}
|
||||
contractsDirectory = contracts
|
||||
}
|
||||
return contractsDirectory
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
/**
|
||||
* Allows to set the {@link Closure} that will modify the existing
|
||||
* {@link ContractVerifierExtension}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 2.1.0
|
||||
*/
|
||||
interface ExtensionHolderSpec {
|
||||
Closure getExtensionClosure()
|
||||
|
||||
void setExtensionClosure(Closure closure)
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.PackageScope
|
||||
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@PackageScope
|
||||
@CompileStatic
|
||||
class ExtensionToProperties {
|
||||
|
||||
protected static ContractVerifierConfigProperties fromExtension(ContractVerifierExtension extension) {
|
||||
return new ContractVerifierConfigProperties(
|
||||
testFramework: extension.getTestFramework(),
|
||||
testMode: extension.getTestMode(),
|
||||
basePackageForTests: extension.getBasePackageForTests(),
|
||||
baseClassForTests: extension.getBaseClassForTests(),
|
||||
nameSuffixForTests: extension.getNameSuffixForTests(),
|
||||
ruleClassForTests: extension.getRuleClassForTests(),
|
||||
excludedFiles: extension.getExcludedFiles(),
|
||||
includedFiles: extension.getIncludedFiles(),
|
||||
ignoredFiles: extension.getIgnoredFiles(),
|
||||
imports: extension.getImports(),
|
||||
staticImports: extension.getStaticImports(),
|
||||
contractsDslDir: extension.getContractsDslDir(),
|
||||
generatedTestSourcesDir: extension.getGeneratedTestSourcesDir(),
|
||||
generatedTestResourcesDir: extension.getGeneratedTestResourcesDir(),
|
||||
stubsOutputDir: extension.getStubsOutputDir(),
|
||||
stubsSuffix: extension.getStubsSuffix(),
|
||||
assertJsonSize: extension.getAssertJsonSize(),
|
||||
packageWithBaseClasses: extension.getPackageWithBaseClasses(),
|
||||
baseClassMappings: extension.getBaseClassMappings(),
|
||||
excludeBuildFolders: extension.getExcludeBuildFolders(),
|
||||
failOnInProgress: extension.getFailOnInProgress()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -16,66 +16,83 @@
|
||||
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import groovy.transform.CompileDynamic
|
||||
import groovy.transform.CompileStatic
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.internal.ConventionTask
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.Directory
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.Nested
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.springframework.cloud.contract.verifier.converter.RecursiveFilesConverter
|
||||
|
||||
//TODO: Implement as an incremental task: https://gradle.org/docs/current/userguide/custom_tasks.html#incremental_tasks ?
|
||||
/**
|
||||
* Generates stubs from the contracts. The name is WireMock related but the implementation
|
||||
* can differ
|
||||
* Generates stubs from the contracts.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Anatoliy Balakirev
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@CompileStatic
|
||||
class GenerateClientStubsFromDslTask extends ConventionTask {
|
||||
class GenerateClientStubsFromDslTask extends DefaultTask {
|
||||
|
||||
File stubsOutputDir
|
||||
static final String TASK_NAME = 'generateClientStubs'
|
||||
private static final String DEFAULT_MAPPINGS_FOLDER = 'mappings'
|
||||
@Nested
|
||||
Config config
|
||||
|
||||
ContractVerifierExtension configProperties
|
||||
GradleContractsDownloader downloader
|
||||
static class Config {
|
||||
@InputDirectory
|
||||
Provider<Directory> contractsDslDir
|
||||
@Input
|
||||
ListProperty<String> excludedFiles
|
||||
@Input
|
||||
Provider<Boolean> excludeBuildFolders
|
||||
|
||||
@OutputDirectory
|
||||
Provider<Directory> stubsOutputDir
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void generate() {
|
||||
logger.info("Stubs output dir [${getStubsOutputDir()}")
|
||||
Task copyContractsTask = project.getTasksByName(SpringCloudContractVerifierGradlePlugin.COPY_CONTRACTS_TASK_NAME, false).first()
|
||||
ContractVerifierConfigProperties props = props(copyContractsTask)
|
||||
File contractsDslDir = contractsDslDir(copyContractsTask, props)
|
||||
File output = config.stubsOutputDir.get().asFile
|
||||
logger.info("Stubs output dir [${output}")
|
||||
logger.info("Spring Cloud Contract Verifier Plugin: Invoking DSL to client stubs conversion")
|
||||
props.contractsDslDir = contractsDslDir
|
||||
props.includedContracts = ".*"
|
||||
File outMappingsDir = OutputFolderBuilder.outputMappingsDir(project, getStubsOutputDir())
|
||||
logger.info("Contracts dir is [${contractsDslDir}] output stubs dir is [${outMappingsDir}]")
|
||||
RecursiveFilesConverter converter = new RecursiveFilesConverter(props, outMappingsDir)
|
||||
logger.info("Contracts dir is [${config.contractsDslDir.get().asFile}] output stubs dir is [${output}]")
|
||||
List<String> excludedFiles = config.excludedFiles.get()
|
||||
RecursiveFilesConverter converter = new RecursiveFilesConverter(output,
|
||||
config.contractsDslDir.get().asFile, excludedFiles, ".*", config.excludeBuildFolders.get())
|
||||
converter.processFiles()
|
||||
}
|
||||
|
||||
@CompileDynamic
|
||||
private ContractVerifierConfigProperties props(Task task) {
|
||||
try {
|
||||
return task.ext.contractVerifierConfigProperties
|
||||
}
|
||||
catch (Exception e) {
|
||||
project.logger.error("Couldn't retrieve the configuration property set by the copy contracts task", e)
|
||||
ContractVerifierConfigProperties props = ExtensionToProperties.fromExtension(getConfigProperties())
|
||||
getDownloader().downloadAndUnpackContractsIfRequired(getConfigProperties(), props)
|
||||
return props
|
||||
}
|
||||
static Config fromExtension(ContractVerifierExtension extension, TaskProvider<ContractsCopyTask> copyContracts,
|
||||
String root, Project project) {
|
||||
return new Config(
|
||||
contractsDslDir: copyContracts.flatMap { it.config.copiedContractsFolder },
|
||||
excludedFiles: extension.excludedFiles,
|
||||
excludeBuildFolders: extension.excludeBuildFolders,
|
||||
|
||||
stubsOutputDir: createTaskOutput(root, extension.stubsOutputDir, project)
|
||||
)
|
||||
}
|
||||
|
||||
@CompileDynamic
|
||||
private File contractsDslDir(Task task, ContractVerifierConfigProperties props) {
|
||||
try {
|
||||
return task.ext.contractsDslDir
|
||||
}
|
||||
catch (Exception e) {
|
||||
project.logger.error("Couldn't retrieve the contract dsl property set by the copy contracts task", e)
|
||||
return props.contractsDslDir
|
||||
private static DirectoryProperty createTaskOutput(String root, DirectoryProperty stubsOutputDir, Project project) {
|
||||
Provider<Directory> provider = stubsOutputDir.flatMap {
|
||||
Directory dir = it
|
||||
File output = new File(dir.asFile, "${root}/${DEFAULT_MAPPINGS_FOLDER}")
|
||||
|
||||
DirectoryProperty property = project.objects.directoryProperty();
|
||||
property.set(output)
|
||||
return property
|
||||
}
|
||||
DirectoryProperty property = project.objects.directoryProperty();
|
||||
property.set(provider)
|
||||
return property
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,56 +16,121 @@
|
||||
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import groovy.transform.CompileDynamic
|
||||
import groovy.transform.CompileStatic
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.internal.ConventionTask
|
||||
import org.gradle.api.file.Directory
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.MapProperty
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
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.springframework.cloud.contract.spec.Contract
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.springframework.cloud.contract.spec.ContractVerifierException
|
||||
import org.springframework.cloud.contract.verifier.TestGenerator
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
import org.springframework.cloud.contract.verifier.config.TestFramework
|
||||
|
||||
import static org.springframework.cloud.contract.verifier.plugin.SpringCloudContractVerifierGradlePlugin.COPY_CONTRACTS_TASK_NAME
|
||||
import org.springframework.cloud.contract.verifier.config.TestMode
|
||||
|
||||
/**
|
||||
* Task used to generate server side tests
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Anatoliy Balakirev
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@CompileStatic
|
||||
class GenerateServerTestsTask extends ConventionTask {
|
||||
class GenerateServerTestsTask extends DefaultTask {
|
||||
static final String TASK_NAME = 'generateContractTests'
|
||||
@Nested
|
||||
Config config
|
||||
|
||||
File generatedTestSourcesDir
|
||||
static class Config {
|
||||
@InputDirectory
|
||||
Provider<Directory> contractsDslDir
|
||||
@Input
|
||||
Provider<String> includedContracts
|
||||
@Input
|
||||
@Optional
|
||||
Provider<String> nameSuffixForTests
|
||||
@Input
|
||||
@Optional
|
||||
Provider<String> basePackageForTests
|
||||
@Input
|
||||
@Optional
|
||||
Provider<String> baseClassForTests
|
||||
@Input
|
||||
@Optional
|
||||
Provider<String> packageWithBaseClasses
|
||||
@Input
|
||||
ListProperty<String> excludedFiles
|
||||
@Input
|
||||
ListProperty<String> ignoredFiles
|
||||
@Input
|
||||
ListProperty<String> includedFiles
|
||||
@Input
|
||||
ListProperty<String> imports
|
||||
@Input
|
||||
ListProperty<String> staticImports
|
||||
@Input
|
||||
Provider<TestMode> testMode
|
||||
@Input
|
||||
Provider<TestFramework> testFramework
|
||||
@Input
|
||||
MapProperty<String, String> baseClassMappings
|
||||
@Input
|
||||
Provider<Boolean> assertJsonSize
|
||||
@Input
|
||||
Provider<Boolean> failOnInProgress
|
||||
|
||||
//TODO: How to deal with @Input*, @Output* and that domain object?
|
||||
ContractVerifierExtension configProperties
|
||||
GradleContractsDownloader downloader
|
||||
@OutputDirectory
|
||||
DirectoryProperty generatedTestSourcesDir
|
||||
@OutputDirectory
|
||||
DirectoryProperty generatedTestResourcesDir
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void generate() {
|
||||
logger.info("Generated test sources dir [${getGeneratedTestSourcesDir()}]")
|
||||
Task copyContractsTask = project.getTasksByName(COPY_CONTRACTS_TASK_NAME, false).first()
|
||||
ContractVerifierConfigProperties props = props(copyContractsTask)
|
||||
File contractsDslDir = contractsDslDir(copyContractsTask, props)
|
||||
if (getConfigProperties().getContractDependency()) {
|
||||
project.logger.debug("Updating the stubs locations for the case where we have a JAR with contracts")
|
||||
props.contractsDslDir = contractsDslDir
|
||||
props.includedContracts = ".*"
|
||||
}
|
||||
File generatedTestSources = config.generatedTestSourcesDir.get().asFile
|
||||
File generatedTestResources = config.generatedTestResourcesDir.get().asFile
|
||||
logger.info("Generated test sources dir [${ generatedTestSources}]")
|
||||
logger.info("Generated test resources dir [${generatedTestResources}]")
|
||||
File contractsDslDir = config.contractsDslDir.get().asFile
|
||||
String includedContracts = config.includedContracts.get()
|
||||
project.logger.info("Spring Cloud Contract Verifier Plugin: Invoking test sources generation")
|
||||
project.logger.info("Contracts are unpacked to [${contractsDslDir}]")
|
||||
project.logger.info("Included contracts are [${props.includedContracts}]")
|
||||
def sourceSetType = getConfigProperties().getTestFramework() == TestFramework.SPOCK ?
|
||||
"groovy" : "java"
|
||||
applySourceSets(sourceSetType)
|
||||
project.logger.info("Included contracts are [${includedContracts}]")
|
||||
try {
|
||||
props = props ?: ExtensionToProperties.fromExtension(getConfigProperties())
|
||||
props.contractsDslDir = contractsDslDir
|
||||
TestGenerator generator = new TestGenerator(props)
|
||||
List<String> excludedFiles = config.excludedFiles.get()
|
||||
List<String> ignoredFiles = config.ignoredFiles.get()
|
||||
List<String> includedFiles = config.includedFiles.get()
|
||||
String[] imports = config.imports.get().toArray(new String[0])
|
||||
String[] staticImports = config.staticImports.get().toArray(new String[0])
|
||||
TestGenerator generator = new TestGenerator(new ContractVerifierConfigProperties(
|
||||
includedContracts: includedContracts,
|
||||
contractsDslDir: contractsDslDir,
|
||||
nameSuffixForTests: config.nameSuffixForTests.getOrNull(),
|
||||
generatedTestSourcesDir: generatedTestSources,
|
||||
generatedTestResourcesDir: generatedTestResources,
|
||||
basePackageForTests: config.basePackageForTests.getOrNull(),
|
||||
baseClassForTests: config.baseClassForTests.getOrNull(),
|
||||
packageWithBaseClasses: config.packageWithBaseClasses.getOrNull(),
|
||||
excludedFiles: excludedFiles,
|
||||
ignoredFiles: ignoredFiles,
|
||||
includedFiles: includedFiles,
|
||||
imports: imports,
|
||||
staticImports: staticImports,
|
||||
testMode: config.testMode.get(),
|
||||
testFramework: config.testFramework.get(),
|
||||
baseClassMappings: config.baseClassMappings.get(),
|
||||
assertJsonSize: config.assertJsonSize.get(),
|
||||
failOnInProgress: config.failOnInProgress.get()
|
||||
))
|
||||
int generatedClasses = generator.generate()
|
||||
project.logger.info("Generated {} test classes", generatedClasses)
|
||||
}
|
||||
@@ -74,41 +139,28 @@ class GenerateServerTestsTask extends ConventionTask {
|
||||
}
|
||||
}
|
||||
|
||||
@CompileDynamic
|
||||
private void applySourceSets(sourceSetType) {
|
||||
project.sourceSets.test."${sourceSetType}" {
|
||||
project.logger.
|
||||
info("Registering ${getConfigProperties().generatedTestSourcesDir} as test source directory")
|
||||
srcDir getConfigProperties().getGeneratedTestSourcesDir()
|
||||
}
|
||||
project.sourceSets.test.resources {
|
||||
project.logger.
|
||||
info("Registering ${getConfigProperties().generatedTestResourcesDir} as test resource directory")
|
||||
srcDir getConfigProperties().getGeneratedTestResourcesDir()
|
||||
}
|
||||
}
|
||||
static Config fromExtension(ContractVerifierExtension extension, TaskProvider<InitContractsTask> initContractsTask,
|
||||
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,
|
||||
packageWithBaseClasses: extension.packageWithBaseClasses,
|
||||
excludedFiles: extension.excludedFiles,
|
||||
ignoredFiles: extension.ignoredFiles,
|
||||
includedFiles: extension.includedFiles,
|
||||
imports: extension.imports,
|
||||
staticImports: extension.staticImports,
|
||||
testMode: extension.testMode,
|
||||
testFramework: extension.testFramework,
|
||||
baseClassMappings: extension.baseClassMappings,
|
||||
assertJsonSize: extension.assertJsonSize,
|
||||
failOnInProgress: extension.failOnInProgress,
|
||||
|
||||
@CompileDynamic
|
||||
private ContractVerifierConfigProperties props(Task task) {
|
||||
try {
|
||||
return task.ext.contractVerifierConfigProperties
|
||||
}
|
||||
catch (Exception e) {
|
||||
project.logger.error("Couldn't retrieve the configuration property set by the copy contracts task", e)
|
||||
ContractVerifierConfigProperties props = ExtensionToProperties.fromExtension(getConfigProperties())
|
||||
getDownloader().downloadAndUnpackContractsIfRequired(getConfigProperties(), props)
|
||||
return props
|
||||
}
|
||||
}
|
||||
|
||||
@CompileDynamic
|
||||
private File contractsDslDir(Task task, ContractVerifierConfigProperties props) {
|
||||
try {
|
||||
return task.ext.contractsDslDir
|
||||
}
|
||||
catch (Exception e) {
|
||||
project.logger.error("Couldn't retrieve the contract dsl property set by the copy contracts task", e)
|
||||
return props.contractsDslDir
|
||||
}
|
||||
generatedTestSourcesDir: extension.generatedTestSourcesDir,
|
||||
generatedTestResourcesDir: extension.generatedTestResourcesDir,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
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.StubRunnerOptions
|
||||
import org.springframework.cloud.contract.stubrunner.StubRunnerOptionsBuilder
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
|
||||
import org.springframework.util.StringUtils
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@@ -26,83 +25,79 @@ class GradleContractsDownloader {
|
||||
private static final String LATEST_VERSION = '+'
|
||||
|
||||
private final Project project
|
||||
private final Logger log
|
||||
private final Logger logger
|
||||
protected static final Map<StubConfiguration, File> downloadedContract = new ConcurrentHashMap<>()
|
||||
|
||||
GradleContractsDownloader(Project project, Logger log) {
|
||||
GradleContractsDownloader(Project project, Logger logger) {
|
||||
this.project = project
|
||||
this.log = log
|
||||
this.logger = logger
|
||||
}
|
||||
|
||||
File downloadAndUnpackContractsIfRequired(ContractVerifierExtension extension,
|
||||
ContractVerifierConfigProperties config) {
|
||||
File defaultContractsDir = extension.contractsDslDir
|
||||
this.log.info("Project has group id [{}], artifact id [{}]", this.project.group, this.project.name)
|
||||
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
|
||||
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)
|
||||
this.log.info("The contract dependency is a changing one [{}] and cache download switch is set to [{}]",
|
||||
configuration.isVersionChanging(), extension.contractRepository.cacheDownloadedContracts)
|
||||
if (!configuration.isVersionChanging() && extension.contractRepository.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
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
}
|
||||
File downloadedContracts = contractDownloader(extension, configuration).unpackedDownloadedContracts(config)
|
||||
downloadedContract.put(configuration, downloadedContracts)
|
||||
return downloadedContracts
|
||||
}
|
||||
this.log.info("For project [{}] will use contracts provided in the folder [{}]", this.project.name, defaultContractsDir)
|
||||
return defaultContractsDir
|
||||
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 boolean shouldDownloadContracts(ContractVerifierExtension extension) {
|
||||
return StringUtils.hasText(extension.contractDependency.getArtifactId()) ||
|
||||
StringUtils.hasText(extension.contractDependency.getStringNotation()) ||
|
||||
StringUtils.hasText(extension.contractRepository.repositoryUrl)
|
||||
}
|
||||
|
||||
protected ContractDownloader contractDownloader(ContractVerifierExtension extension, StubConfiguration configuration) {
|
||||
return new ContractDownloader(stubDownloader(extension), configuration,
|
||||
extension.contractsPath, this.project.group as String, this.project.name, this.project.version as String)
|
||||
}
|
||||
|
||||
protected StubDownloader stubDownloader(ContractVerifierExtension extension) {
|
||||
StubDownloaderBuilderProvider provider = new StubDownloaderBuilderProvider()
|
||||
return provider.get(options(extension))
|
||||
}
|
||||
|
||||
protected StubRunnerOptions options(ContractVerifierExtension extension) {
|
||||
StubRunnerOptionsBuilder options = new StubRunnerOptionsBuilder()
|
||||
.withOptions(StubRunnerOptions.fromSystemProps())
|
||||
.withStubRepositoryRoot(extension.contractRepository.repositoryUrl)
|
||||
.withStubsMode(extension.contractsMode)
|
||||
.withUsername(extension.contractRepository.username)
|
||||
.withPassword(extension.contractRepository.password)
|
||||
.withDeleteStubsAfterTest(extension.deleteStubsAfterTest)
|
||||
.withProperties(extension.contractsProperties)
|
||||
if (extension.contractRepository.proxyPort) {
|
||||
options = options.withProxy(extension.contractRepository.proxyHost, extension.contractRepository.proxyPort)
|
||||
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
|
||||
}
|
||||
options = options.withFailOnNoStubs(extension.failOnNoContracts)
|
||||
return options.build()
|
||||
return contractsDirectory
|
||||
}
|
||||
|
||||
@PackageScope
|
||||
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
|
||||
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,
|
||||
@@ -110,4 +105,35 @@ class GradleContractsDownloader {
|
||||
}
|
||||
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,90 @@
|
||||
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.OutputDirectory
|
||||
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
|
||||
|
||||
@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
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.PackageScope
|
||||
import org.gradle.api.Project
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@CompileStatic
|
||||
@PackageScope
|
||||
class OutputFolderBuilder {
|
||||
|
||||
private static final String DEFAULT_MAPPINGS_FOLDER = 'mappings'
|
||||
|
||||
static String buildRootPath(Project project) {
|
||||
String groupId = project.group as String
|
||||
String artifactId = project.name
|
||||
String version = project.version
|
||||
return "META-INF/${groupId}/${artifactId}/${version}"
|
||||
}
|
||||
|
||||
static File outputMappingsDir(Project project, File stubsOutputDir) {
|
||||
String root = OutputFolderBuilder.buildRootPath(project)
|
||||
return stubsOutputDir != null ?
|
||||
new File(stubsOutputDir, "${root}/${DEFAULT_MAPPINGS_FOLDER}")
|
||||
: new File(project.buildDir, "stubs/${root}/${DEFAULT_MAPPINGS_FOLDER}")
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,19 @@
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import org.gradle.api.internal.ConventionTask
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
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.tasks.TaskAction
|
||||
|
||||
import org.springframework.cloud.contract.stubrunner.ContractProjectUpdater
|
||||
import org.springframework.cloud.contract.stubrunner.ScmStubDownloaderBuilder
|
||||
import org.springframework.cloud.contract.stubrunner.StubRunnerOptions
|
||||
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
|
||||
|
||||
/**
|
||||
* For SCM based repositories will copy the generated stubs
|
||||
@@ -30,49 +37,66 @@ import org.springframework.cloud.contract.stubrunner.StubRunnerOptions
|
||||
* commit the changes and push them to origin.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Anatoliy Balakirev
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@CompileStatic
|
||||
class PublishStubsToScmTask extends ConventionTask {
|
||||
File stubsOutputDir
|
||||
ContractVerifierExtension configProperties
|
||||
GradleContractsDownloader downloader
|
||||
private final ExtensionHolderSpec closureHolder = new ClosureHolder()
|
||||
class PublishStubsToScmTask extends DefaultTask {
|
||||
|
||||
static final String TASK_NAME = 'publishStubsToScm'
|
||||
@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.ContractRepository contractRepository
|
||||
@Input
|
||||
@Optional
|
||||
Provider<StubRunnerProperties.StubsMode> contractsMode
|
||||
@Input
|
||||
Provider<Boolean> deleteStubsAfterTest
|
||||
@Input
|
||||
Provider<Boolean> failOnNoContracts
|
||||
@Input
|
||||
MapProperty<String, String> contractsProperties
|
||||
|
||||
@OutputDirectory
|
||||
DirectoryProperty stubsOutputDir
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void publishStubsToScm() {
|
||||
ContractVerifierExtension clonedExtension = modifyExtension()
|
||||
if (!shouldRun(clonedExtension)) {
|
||||
if (!shouldRun()) {
|
||||
return
|
||||
}
|
||||
String projectName = project.group.toString() + ":" + project.name.toString() + ":" + this.project.version.toString()
|
||||
project.logger.info("Pushing Stubs to SCM for project [" + projectName + "]")
|
||||
StubRunnerOptions options = getDownloader().options(clonedExtension)
|
||||
new ContractProjectUpdater(options).updateContractProject(projectName, getStubsOutputDir().toPath())
|
||||
StubRunnerOptions stubRunnerOptions = StubRunnerOptionsFactory.createStubRunnerOptions(
|
||||
config.contractRepository, config.contractsMode.getOrNull(), config.deleteStubsAfterTest.get(),
|
||||
config.contractsProperties.get(), config.failOnNoContracts.get())
|
||||
new ContractProjectUpdater(stubRunnerOptions).updateContractProject(projectName, config.stubsOutputDir.get().asFile.toPath())
|
||||
}
|
||||
|
||||
private ContractVerifierExtension modifyExtension() {
|
||||
ContractVerifierExtension clone = getConfigProperties().copy()
|
||||
this.closureHolder.extensionClosure.delegate = clone
|
||||
this.closureHolder.extensionClosure.call(clone)
|
||||
return clone
|
||||
static Config fromExtension(ContractVerifierExtension extension) {
|
||||
return new Config(
|
||||
contractRepository: extension.contractRepository,
|
||||
contractsMode: extension.contractsMode,
|
||||
failOnNoContracts: extension.failOnNoContracts,
|
||||
deleteStubsAfterTest: extension.deleteStubsAfterTest,
|
||||
contractsProperties: extension.contractsProperties,
|
||||
|
||||
stubsOutputDir: extension.stubsOutputDir
|
||||
)
|
||||
}
|
||||
|
||||
private boolean shouldRun(ContractVerifierExtension clonedExtension) {
|
||||
String contractRepoUrl = clonedExtension.contractRepository.repositoryUrl ?: ""
|
||||
private boolean shouldRun() {
|
||||
String contractRepoUrl = config.contractRepository.repositoryUrl.getOrNull() ?: ""
|
||||
if (!contractRepoUrl || !ScmStubDownloaderBuilder.isProtocolAccepted(contractRepoUrl)) {
|
||||
project.logger.warn("Skipping pushing stubs to scm since your contracts repository URL [${contractRepoUrl}] doesn't match any of the accepted protocols for SCM stub downloader")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
void customize(@DelegatesTo(ContractVerifierExtension) Closure closure) {
|
||||
project.logger.debug("Storing the extension closure")
|
||||
this.closureHolder.extensionClosure = closure
|
||||
}
|
||||
|
||||
private static class ClosureHolder implements ExtensionHolderSpec {
|
||||
Closure extensionClosure = Closure.IDENTITY
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,10 @@ import org.gradle.api.Task
|
||||
import org.gradle.api.plugins.GroovyPlugin
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
import org.springframework.cloud.contract.verifier.config.TestFramework
|
||||
|
||||
/**
|
||||
* Gradle plugin for Spring Cloud Contract Verifier that from the DSL contract can
|
||||
* <ul>
|
||||
@@ -34,18 +37,14 @@ import org.gradle.jvm.tasks.Jar
|
||||
*
|
||||
* @author Jakub Kubrynski, codearte.io
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Anatoliy Balakirev
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@CompileStatic
|
||||
class SpringCloudContractVerifierGradlePlugin implements Plugin<Project> {
|
||||
|
||||
protected static final String GENERATE_SERVER_TESTS_TASK_NAME = 'generateContractTests'
|
||||
protected static final String DSL_TO_CLIENT_TASK_NAME = 'generateClientStubs'
|
||||
protected static final String COPY_CONTRACTS_TASK_NAME = 'copyContracts'
|
||||
protected static final String VERIFIER_STUBS_JAR_TASK_NAME = 'verifierStubsJar'
|
||||
protected static final String PUBLISH_STUBS_TO_SCM_TASK_NAME = 'publishStubsToScm'
|
||||
|
||||
private static final String VERIFIER_STUBS_JAR_TASK_NAME = 'verifierStubsJar'
|
||||
private static final String GROUP_NAME = "Verification"
|
||||
private static final String EXTENSION_NAME = 'contracts'
|
||||
|
||||
@@ -56,33 +55,48 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin<Project> {
|
||||
this.project = project
|
||||
project.plugins.apply(GroovyPlugin)
|
||||
ContractVerifierExtension extension = project.extensions.create(EXTENSION_NAME, ContractVerifierExtension)
|
||||
GradleContractsDownloader downloader = new GradleContractsDownloader(this.project, this.project.logger)
|
||||
addDependsOn(project)
|
||||
setConfigurationDefaults(extension)
|
||||
Task stubsJar = createAndConfigureStubsJarTasks(extension)
|
||||
Task copyContracts = createAndConfigureCopyContractsTask(stubsJar, downloader, extension)
|
||||
createAndConfigureMavenPublishPlugin(stubsJar, extension)
|
||||
createGenerateTestsTask(extension, copyContracts, downloader)
|
||||
createAndConfigureGenerateClientStubs(extension, copyContracts)
|
||||
createAndConfigurePublishStubsToScmTask(extension, downloader)
|
||||
addIdeaTestSources(project, extension)
|
||||
|
||||
TaskProvider<InitContractsTask> initContracts = createAndConfigureContractsInitTask(extension)
|
||||
TaskProvider<ContractsCopyTask> copyContracts = createAndConfigureCopyContractsTask(initContracts, extension)
|
||||
TaskProvider<GenerateClientStubsFromDslTask> generateClientStubs = createAndConfigureGenerateClientStubs(extension, copyContracts)
|
||||
|
||||
createAndConfigureStubsJarTasks(extension, copyContracts, generateClientStubs)
|
||||
createGenerateTestsTask(extension, copyContracts, initContracts)
|
||||
createAndConfigurePublishStubsToScmTask(extension, generateClientStubs)
|
||||
project.afterEvaluate {
|
||||
addIdeaTestSources(project, extension)
|
||||
applyDefaultSourceSets(extension)
|
||||
}
|
||||
}
|
||||
|
||||
// This must be called within afterEvaluate due to getting data from extension, which must be initialised first:
|
||||
@CompileDynamic
|
||||
private void addDependsOn(Project project) {
|
||||
project.check.dependsOn(GENERATE_SERVER_TESTS_TASK_NAME)
|
||||
private void applyDefaultSourceSets(ContractVerifierExtension extension) {
|
||||
String sourceSetType = extension.testFramework.get() == TestFramework.SPOCK ? "groovy" : "java"
|
||||
project.sourceSets.test."${sourceSetType}" {
|
||||
project.logger.
|
||||
info("Registering ${extension.generatedTestSourcesDir.get().asFile} as test source directory")
|
||||
srcDir extension.generatedTestSourcesDir.get().asFile
|
||||
}
|
||||
project.sourceSets.test.resources {
|
||||
project.logger.
|
||||
info("Registering ${extension.generatedTestResourcesDir.get().asFile} as test resource directory")
|
||||
srcDir extension.generatedTestResourcesDir.get().asFile
|
||||
}
|
||||
}
|
||||
|
||||
// This must be called within afterEvaluate due to getting data from extension, which must be initialised first:
|
||||
@CompileDynamic
|
||||
private addIdeaTestSources(Project project, ContractVerifierExtension extension) {
|
||||
def hasIdea = new File(project.rootDir, ".idea").exists()
|
||||
boolean hasIdea = new File(project.rootDir, ".idea").exists()
|
||||
if (hasIdea) {
|
||||
project.apply(plugin: 'idea')
|
||||
project.idea {
|
||||
module {
|
||||
testSourceDirs += extension.generatedTestSourcesDir
|
||||
testSourceDirs += extension.generatedTestResourcesDir
|
||||
testSourceDirs += extension.contractsDslDir
|
||||
testSourceDirs += extension.generatedTestSourcesDir.get().asFile
|
||||
testSourceDirs += extension.generatedTestResourcesDir.get().asFile
|
||||
testSourceDirs += extension.contractsDslDir.get().asFile
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,92 +104,108 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin<Project> {
|
||||
|
||||
private void setConfigurationDefaults(ContractVerifierExtension extension) {
|
||||
extension.with {
|
||||
generatedTestSourcesDir = generatedTestSourcesDir ?: project.file("${project.buildDir}/generated-test-sources/contracts")
|
||||
generatedTestSourcesDir.mkdirs()
|
||||
generatedTestResourcesDir = generatedTestResourcesDir ?: project.file("${project.buildDir}/generated-test-resources/contracts")
|
||||
generatedTestResourcesDir.mkdirs()
|
||||
contractsDslDir = contractsDslDir ?: defaultContractsDir() //TODO: Use sourceset
|
||||
contractsDslDir.mkdirs()
|
||||
stubsOutputDir = stubsOutputDir ?: project.file("${project.buildDir}/stubs/")
|
||||
stubsOutputDir.mkdirs()
|
||||
it.contractsDslDir.convention(project.layout.projectDirectory.dir("src/test/resources/contracts"))
|
||||
it.generatedTestSourcesDir.convention(project.layout.getBuildDirectory().dir("generated-test-sources/contracts"))
|
||||
it.generatedTestResourcesDir.convention(project.layout.getBuildDirectory().dir("generated-test-resources/contracts"))
|
||||
it.stubsOutputDir.convention(project.layout.getBuildDirectory().dir("stubs"))
|
||||
}
|
||||
}
|
||||
|
||||
private File defaultContractsDir() {
|
||||
return project.file("${project.projectDir}/src/test/resources/contracts")
|
||||
}
|
||||
|
||||
@CompileDynamic
|
||||
private void createGenerateTestsTask(ContractVerifierExtension extension, Task copyContracts,
|
||||
GradleContractsDownloader gradleContractsDownloader) {
|
||||
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 {
|
||||
downloader = { gradleContractsDownloader }
|
||||
generatedTestSourcesDir = { extension.generatedTestSourcesDir }
|
||||
configProperties = { extension }
|
||||
private void createGenerateTestsTask(ContractVerifierExtension extension,
|
||||
TaskProvider<ContractsCopyTask> copyContracts, TaskProvider<InitContractsTask> initContracts) {
|
||||
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.dependsOn copyContracts
|
||||
}
|
||||
task.enabled = !project.gradle.startParameter.excludedTaskNames.contains("test")
|
||||
task.dependsOn copyContracts
|
||||
project.tasks.findByName("compileTestJava").dependsOn(task)
|
||||
project.tasks.findByName("check").dependsOn(task)
|
||||
}
|
||||
|
||||
@CompileDynamic
|
||||
private void createAndConfigurePublishStubsToScmTask(ContractVerifierExtension extension,
|
||||
GradleContractsDownloader gradleContractsDownloader) {
|
||||
Task task = project.tasks.create(PUBLISH_STUBS_TO_SCM_TASK_NAME, PublishStubsToScmTask)
|
||||
task.description = "The generated stubs get committed to the SCM repo and pushed to origin"
|
||||
task.group = GROUP_NAME
|
||||
task.conventionMapping.with {
|
||||
downloader = { gradleContractsDownloader }
|
||||
configProperties = { extension }
|
||||
stubsOutputDir = { extension.stubsOutputDir }
|
||||
TaskProvider<GenerateClientStubsFromDslTask> generateClientStubs) {
|
||||
TaskProvider<PublishStubsToScmTask> task = project.tasks.register(PublishStubsToScmTask.TASK_NAME, PublishStubsToScmTask)
|
||||
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.dependsOn generateClientStubs
|
||||
}
|
||||
task.dependsOn DSL_TO_CLIENT_TASK_NAME
|
||||
}
|
||||
|
||||
@CompileDynamic
|
||||
private Task createAndConfigureGenerateClientStubs(ContractVerifierExtension extension,
|
||||
Task copyContracts) {
|
||||
Task task = project.tasks.create(DSL_TO_CLIENT_TASK_NAME, GenerateClientStubsFromDslTask)
|
||||
task.description = "Generate client stubs from the contracts"
|
||||
task.group = GROUP_NAME
|
||||
task.conventionMapping.with {
|
||||
downloader = { gradleContractsDownloader }
|
||||
stubsOutputDir = { extension.stubsOutputDir }
|
||||
configProperties = { extension }
|
||||
private TaskProvider<GenerateClientStubsFromDslTask> createAndConfigureGenerateClientStubs(ContractVerifierExtension extension,
|
||||
TaskProvider<ContractsCopyTask> copyContracts) {
|
||||
TaskProvider<GenerateClientStubsFromDslTask> task = project.tasks.register(GenerateClientStubsFromDslTask.TASK_NAME, GenerateClientStubsFromDslTask)
|
||||
task.configure {
|
||||
it.description = "Generate client stubs from the contracts"
|
||||
it.group = GROUP_NAME
|
||||
it.config = GenerateClientStubsFromDslTask.fromExtension(extension, copyContracts, buildRootPath(), project)
|
||||
|
||||
it.dependsOn copyContracts
|
||||
}
|
||||
task.dependsOn copyContracts
|
||||
return task
|
||||
}
|
||||
|
||||
@CompileDynamic
|
||||
private Task createAndConfigureStubsJarTasks(ContractVerifierExtension extension) {
|
||||
Task task = stubsTask()
|
||||
private TaskProvider<Task> createAndConfigureStubsJarTasks(ContractVerifierExtension extension,
|
||||
TaskProvider<ContractsCopyTask> copyContracts,
|
||||
TaskProvider<GenerateClientStubsFromDslTask> generateClientStubs) {
|
||||
TaskProvider<Task> task = stubsTask()
|
||||
if (task) {
|
||||
// How is this possible? Where can it come from?
|
||||
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!")
|
||||
return task
|
||||
} else {
|
||||
task = createStubsJarTask(extension, generateClientStubs)
|
||||
}
|
||||
else {
|
||||
task = project.tasks.create(type: Jar, name: VERIFIER_STUBS_JAR_TASK_NAME,
|
||||
dependsOn: DSL_TO_CLIENT_TASK_NAME) {
|
||||
baseName = project.name
|
||||
classifier = extension.stubsSuffix
|
||||
from { extension.stubsOutputDir ?: project.file("${project.buildDir}/stubs") }
|
||||
task.configure {
|
||||
it.dependsOn copyContracts
|
||||
}
|
||||
createAndConfigureMavenPublishPlugin(task, extension)
|
||||
return task
|
||||
}
|
||||
|
||||
private void createAndConfigureMavenPublishPlugin(TaskProvider<Task> stubsTask, ContractVerifierExtension extension) {
|
||||
if (!classIsOnClasspath("org.gradle.api.publish.maven.plugins.MavenPublishPlugin")) {
|
||||
project.logger.debug("Maven Publish Plugin is not present - won't add default publication")
|
||||
return
|
||||
}
|
||||
// This must be called within afterEvaluate due to getting data from extension, which must be initialised first:
|
||||
project.afterEvaluate {
|
||||
project.logger.debug("Spring Cloud Contract Verifier Plugin: Generating default publication")
|
||||
if (extension.disableStubPublication.get()) {
|
||||
project.logger.info("You've switched off the stub publication - won't add default publication")
|
||||
return
|
||||
}
|
||||
task.description = "Creates the stubs JAR task"
|
||||
task.group = GROUP_NAME
|
||||
project.artifacts {
|
||||
archives task
|
||||
project.plugins.withType(MavenPublishPlugin) { def publishingPlugin ->
|
||||
def publishingExtension = project.extensions.findByName('publishing')
|
||||
if (hasStubsPublication(publishingExtension)) {
|
||||
project.logger.info("Spring Cloud Contract Verifier Plugin: Stubs publication was present - won't create a new one. Remember about passing stubs as artifact")
|
||||
} else {
|
||||
project.logger.debug("Spring Cloud Contract Verifier Plugin: Stubs publication is not present - will create one")
|
||||
setPublications(publishingExtension, stubsTask)
|
||||
}
|
||||
}
|
||||
return task
|
||||
}
|
||||
}
|
||||
|
||||
private Task stubsTask() {
|
||||
@CompileDynamic
|
||||
private void setPublications(def publishingExtension, TaskProvider<Task> stubsTask) {
|
||||
publishingExtension.publications {
|
||||
stubs(MavenPublication) {
|
||||
artifactId "${project.name}"
|
||||
artifact stubsTask.get() // TODO: How to make it lazily initialised?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TaskProvider<Task> stubsTask() {
|
||||
try {
|
||||
return project.tasks.getByName(VERIFIER_STUBS_JAR_TASK_NAME)
|
||||
return project.tasks.named(VERIFIER_STUBS_JAR_TASK_NAME)
|
||||
}
|
||||
catch (Exception e) {
|
||||
return null
|
||||
@@ -183,52 +213,7 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin<Project> {
|
||||
}
|
||||
|
||||
@CompileDynamic
|
||||
private Task createAndConfigureCopyContractsTask(Task stubs,
|
||||
GradleContractsDownloader gradleContractsDownloader,
|
||||
ContractVerifierExtension contractVerifierExtension) {
|
||||
Task task = project.tasks.create(COPY_CONTRACTS_TASK_NAME, ContractsCopyTask)
|
||||
task.description = "Copies contracts to the output folder"
|
||||
task.group = GROUP_NAME
|
||||
task.conventionMapping.with {
|
||||
downloader = { gradleContractsDownloader }
|
||||
extension = { contractVerifierExtension }
|
||||
}
|
||||
stubs.dependsOn task
|
||||
return task
|
||||
}
|
||||
|
||||
@CompileDynamic
|
||||
private void createAndConfigureMavenPublishPlugin(Task stubsTask, ContractVerifierExtension extension) {
|
||||
if (!classIsOnClasspath("org.gradle.api.publish.maven.plugins.MavenPublishPlugin")) {
|
||||
project.logger.debug("Maven Publish Plugin is not present - won't add default publication")
|
||||
return
|
||||
}
|
||||
project.logger.debug("Spring Cloud Contract Verifier Plugin: Generating default publication")
|
||||
project.afterEvaluate {
|
||||
if (extension.isDisableStubPublication()) {
|
||||
project.logger.info("You've switched off the stub publication - won't add default publication")
|
||||
return
|
||||
}
|
||||
project.plugins.withType(MavenPublishPlugin) { def publishingPlugin ->
|
||||
def publishingExtension = project.extensions.findByName('publishing')
|
||||
if (!hasPublication(publishingExtension)) {
|
||||
project.logger.debug("Spring Cloud Contract Verifier Plugin: Stubs publication is not present - will create one")
|
||||
publishingExtension.publications {
|
||||
stubs(MavenPublication) {
|
||||
artifactId "${project.name}"
|
||||
artifact stubsTask
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
project.logger.info("Spring Cloud Contract Verifier Plugin: Stubs publication was present - won't create a new one. Remember about passing stubs as artifact")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CompileDynamic
|
||||
private boolean hasPublication(def publishingExtension) {
|
||||
private boolean hasStubsPublication(def publishingExtension) {
|
||||
try {
|
||||
return publishingExtension.publications.getByName('stubs')
|
||||
}
|
||||
@@ -237,6 +222,49 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin<Project> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Can we define inputs / outputs and make it incremental?
|
||||
@CompileDynamic
|
||||
private TaskProvider<Task> createStubsJarTask(ContractVerifierExtension extension,
|
||||
TaskProvider<GenerateClientStubsFromDslTask> generateClientStubs) {
|
||||
TaskProvider<Jar> task = project.tasks.register(VERIFIER_STUBS_JAR_TASK_NAME, Jar)
|
||||
task.configure {
|
||||
it.description = "Creates the stubs JAR task"
|
||||
it.group = GROUP_NAME
|
||||
it.getArchiveBaseName().set(project.name)
|
||||
it.getArchiveClassifier().set(extension.stubsSuffix)
|
||||
it.from { extension.stubsOutputDir }
|
||||
|
||||
it.dependsOn generateClientStubs
|
||||
}
|
||||
project.artifacts {
|
||||
archives task
|
||||
}
|
||||
return task
|
||||
}
|
||||
|
||||
private TaskProvider<ContractsCopyTask> createAndConfigureCopyContractsTask(TaskProvider<InitContractsTask> initContractsTask,
|
||||
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)
|
||||
}
|
||||
return task
|
||||
}
|
||||
|
||||
private boolean classIsOnClasspath(String className) {
|
||||
try {
|
||||
Class.forName(className)
|
||||
@@ -247,4 +275,11 @@ class SpringCloudContractVerifierGradlePlugin implements Plugin<Project> {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private String buildRootPath() {
|
||||
String groupId = project.group as String
|
||||
String artifactId = project.name
|
||||
String version = project.version
|
||||
return "META-INF/${groupId}/${artifactId}/${version}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.PackageScope
|
||||
import org.springframework.cloud.contract.stubrunner.StubRunnerOptions
|
||||
import org.springframework.cloud.contract.stubrunner.StubRunnerOptionsBuilder
|
||||
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
|
||||
|
||||
/**
|
||||
* Helper class to create StubRunnerOptions.
|
||||
*
|
||||
* @author Anatoliy Balakirev
|
||||
*/
|
||||
@CompileStatic
|
||||
@PackageScope
|
||||
class StubRunnerOptionsFactory {
|
||||
|
||||
static StubRunnerOptions createStubRunnerOptions(ContractVerifierExtension.ContractRepository contractRepository,
|
||||
StubRunnerProperties.StubsMode contractsMode, boolean deleteStubsAfterTest,
|
||||
Map<String, String> contractsProperties, boolean failOnNoContracts) {
|
||||
StubRunnerOptionsBuilder options = new StubRunnerOptionsBuilder()
|
||||
.withOptions(StubRunnerOptions.fromSystemProps())
|
||||
.withStubRepositoryRoot(contractRepository.repositoryUrl.getOrNull())
|
||||
.withStubsMode(contractsMode)
|
||||
.withUsername(contractRepository.username.getOrNull())
|
||||
.withPassword(contractRepository.password.getOrNull())
|
||||
.withDeleteStubsAfterTest(deleteStubsAfterTest)
|
||||
.withProperties(contractsProperties)
|
||||
.withFailOnNoStubs(failOnNoContracts)
|
||||
if (contractRepository.proxyPort.getOrNull()) {
|
||||
options = options.withProxy(contractRepository.proxyHost.getOrNull(), contractRepository.proxyPort.getOrNull())
|
||||
}
|
||||
return options.build()
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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
|
||||
*
|
||||
* https://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.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import spock.lang.Specification
|
||||
|
||||
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
|
||||
import org.springframework.cloud.contract.verifier.config.TestFramework
|
||||
import org.springframework.cloud.contract.verifier.config.TestMode
|
||||
|
||||
class ContractVerifierExtensionSpec extends Specification {
|
||||
def "should make a copy of the object"() {
|
||||
given:
|
||||
ContractVerifierExtension original = new ContractVerifierExtension(
|
||||
testFramework: TestFramework.JUNIT5,
|
||||
testMode: TestMode.EXPLICIT,
|
||||
basePackageForTests: "foo1",
|
||||
baseClassForTests: "foo2",
|
||||
nameSuffixForTests: "foo3",
|
||||
ruleClassForTests: "foo4",
|
||||
excludedFiles: ["foo5"],
|
||||
includedFiles: ["foo6"],
|
||||
ignoredFiles: ["foo7"],
|
||||
imports: ["foo8"],
|
||||
staticImports: ["foo9"],
|
||||
contractsDslDir: new File("foo10"),
|
||||
generatedTestSourcesDir: new File("foo11"),
|
||||
generatedTestResourcesDir: new File("foo12"),
|
||||
stubsOutputDir: new File("foo13"),
|
||||
stubsSuffix: 'foo14',
|
||||
assertJsonSize: false,
|
||||
contractRepository: new ContractVerifierExtension.ContractRepository(
|
||||
repositoryUrl: "foo15",
|
||||
username: "foo16",
|
||||
password: "foo17",
|
||||
proxyPort: 18,
|
||||
proxyHost: "foo19",
|
||||
cacheDownloadedContracts: false
|
||||
),
|
||||
contractDependency: new ContractVerifierExtension.Dependency(
|
||||
groupId: "foo20",
|
||||
artifactId: "foo21",
|
||||
classifier: "foo22",
|
||||
version: "foo23",
|
||||
stringNotation: "foo24"
|
||||
),
|
||||
contractsPath: "foo25",
|
||||
contractsMode: StubRunnerProperties.StubsMode.CLASSPATH,
|
||||
packageWithBaseClasses: "foo26",
|
||||
baseClassMappings: [foo27: "foo28"],
|
||||
excludeBuildFolders: false,
|
||||
deleteStubsAfterTest: false,
|
||||
convertToYaml: false,
|
||||
contractsProperties: [foo29: "foo30"]
|
||||
)
|
||||
when:
|
||||
ContractVerifierExtension copy = original.copy()
|
||||
original.with {
|
||||
testFramework = TestFramework.CUSTOM
|
||||
testMode = TestMode.MOCKMVC
|
||||
basePackageForTests = "bar1"
|
||||
baseClassForTests = "bar2"
|
||||
nameSuffixForTests = "bar3"
|
||||
ruleClassForTests = "bar4"
|
||||
excludedFiles = ["bar5"]
|
||||
includedFiles = ["bar6"]
|
||||
ignoredFiles = ["bar7"]
|
||||
imports = ["bar8"]
|
||||
staticImports = ["bar9"]
|
||||
contractsDslDir = new File("bar10")
|
||||
generatedTestSourcesDir = new File("bar11")
|
||||
generatedTestResourcesDir = new File("bar12")
|
||||
stubsOutputDir = new File("bar13")
|
||||
stubsSuffix = 'bar14'
|
||||
assertJsonSize = true
|
||||
contractRepository.with {
|
||||
repositoryUrl = "bar15"
|
||||
username = "bar16"
|
||||
password = "bar17"
|
||||
proxyPort = 28
|
||||
proxyHost = "bar19"
|
||||
cacheDownloadedContracts = true
|
||||
}
|
||||
contractDependency.with {
|
||||
groupId = "bar20"
|
||||
artifactId = "bar21"
|
||||
classifier = "bar22"
|
||||
version = "bar23"
|
||||
stringNotation = "bar24"
|
||||
}
|
||||
contractsPath = "bar25"
|
||||
contractsMode = StubRunnerProperties.StubsMode.REMOTE
|
||||
packageWithBaseClasses = "bar26"
|
||||
baseClassMappings = [bar27: "bar28"]
|
||||
excludeBuildFolders = true
|
||||
deleteStubsAfterTest = true
|
||||
convertToYaml = true
|
||||
contractsProperties = [bar29: "bar30"]
|
||||
}
|
||||
then:
|
||||
copy.testFramework == TestFramework.JUNIT5
|
||||
copy.testMode == TestMode.EXPLICIT
|
||||
copy.basePackageForTests == "foo1"
|
||||
copy.baseClassForTests == "foo2"
|
||||
copy.nameSuffixForTests == "foo3"
|
||||
copy.ruleClassForTests == "foo4"
|
||||
copy.excludedFiles == ["foo5"]
|
||||
copy.includedFiles == ["foo6"]
|
||||
copy.ignoredFiles == ["foo7"]
|
||||
copy.imports == ["foo8"]
|
||||
copy.staticImports == ["foo9"]
|
||||
copy.contractsDslDir == new File("foo10")
|
||||
copy.generatedTestSourcesDir == new File("foo11")
|
||||
copy.generatedTestResourcesDir == new File("foo12")
|
||||
copy.stubsOutputDir == new File("foo13")
|
||||
copy.stubsSuffix == 'foo14'
|
||||
copy.assertJsonSize == false
|
||||
copy.contractRepository.repositoryUrl == "foo15"
|
||||
copy.contractRepository.username == "foo16"
|
||||
copy.contractRepository.password == "foo17"
|
||||
copy.contractRepository.proxyPort == 18
|
||||
copy.contractRepository.proxyHost == "foo19"
|
||||
copy.contractRepository.cacheDownloadedContracts == false
|
||||
copy.contractDependency.groupId == "foo20"
|
||||
copy.contractDependency.artifactId == "foo21"
|
||||
copy.contractDependency.classifier == "foo22"
|
||||
copy.contractDependency.version == "foo23"
|
||||
copy.contractDependency.stringNotation == "foo24"
|
||||
copy.contractsPath == "foo25"
|
||||
copy.contractsMode == StubRunnerProperties.StubsMode.CLASSPATH
|
||||
copy.packageWithBaseClasses == "foo26"
|
||||
copy.baseClassMappings == [foo27: "foo28"]
|
||||
copy.excludeBuildFolders == false
|
||||
copy.deleteStubsAfterTest == false
|
||||
copy.convertToYaml == false
|
||||
copy.contractsProperties == [foo29: "foo30"]
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.internal.project.DefaultProject
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.plugins.GroovyPlugin
|
||||
import org.gradle.api.publish.PublicationContainer
|
||||
import org.gradle.api.publish.PublishingExtension
|
||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
|
||||
import org.gradle.testfixtures.ProjectBuilder
|
||||
@@ -12,92 +13,71 @@ class ContractVerifierSpec extends Specification {
|
||||
DefaultProject project
|
||||
|
||||
def setup() {
|
||||
def dateString = new Date().format("yyyy-MM-dd_HH-mm-ss")
|
||||
def testFolder = new File("build/generated-tests/${getClass().simpleName}/${dateString}")
|
||||
String dateString = new Date().format("yyyy-MM-dd_HH-mm-ss")
|
||||
File testFolder = new File("build/generated-tests/${getClass().simpleName}/${dateString}")
|
||||
testFolder.mkdirs()
|
||||
project = (DefaultProject) ProjectBuilder.builder().withProjectDir(testFolder).build()
|
||||
project.plugins.apply(SpringCloudContractVerifierGradlePlugin)
|
||||
}
|
||||
|
||||
def "should apply groovy plugin"() {
|
||||
given:
|
||||
project.plugins.apply(SpringCloudContractVerifierGradlePlugin)
|
||||
|
||||
expect:
|
||||
project.plugins.hasPlugin(GroovyPlugin)
|
||||
}
|
||||
|
||||
def "should create contracts extension"() {
|
||||
given:
|
||||
project.plugins.apply(SpringCloudContractVerifierGradlePlugin)
|
||||
|
||||
expect:
|
||||
project.extensions.findByType(ContractVerifierExtension) != null
|
||||
}
|
||||
|
||||
def "should create generateContractTests task"() {
|
||||
given:
|
||||
project.plugins.apply(SpringCloudContractVerifierGradlePlugin)
|
||||
|
||||
expect:
|
||||
project.tasks.findByName("generateContractTests") != null
|
||||
project.tasks.named("generateContractTests") != null
|
||||
}
|
||||
|
||||
def "should configure generateContractTests task as a dependency of the check task"() {
|
||||
given:
|
||||
project.plugins.apply(SpringCloudContractVerifierGradlePlugin)
|
||||
|
||||
expect:
|
||||
project.tasks.check.getDependsOn().contains("generateContractTests")
|
||||
project.tasks.check.getDependsOn().contains(project.tasks.named("generateContractTests"))
|
||||
}
|
||||
|
||||
def "should create generateClientStubs task"() {
|
||||
given:
|
||||
project.plugins.apply(SpringCloudContractVerifierGradlePlugin)
|
||||
|
||||
expect:
|
||||
project.tasks.findByName("generateClientStubs") != null
|
||||
project.tasks.named("generateClientStubs") != null
|
||||
}
|
||||
|
||||
def "should create verifierStubsJar task"() {
|
||||
given:
|
||||
project.plugins.apply(SpringCloudContractVerifierGradlePlugin)
|
||||
|
||||
expect:
|
||||
project.tasks.findByName("verifierStubsJar") != null
|
||||
project.tasks.named("verifierStubsJar") != null
|
||||
}
|
||||
|
||||
def "should configure generateClientStubs task as a dependency of the verifierStubsJar task"() {
|
||||
given:
|
||||
project.plugins.apply(SpringCloudContractVerifierGradlePlugin)
|
||||
|
||||
expect:
|
||||
project.tasks.verifierStubsJar.getDependsOn().contains("generateClientStubs")
|
||||
project.tasks.verifierStubsJar.getDependsOn().contains(project.tasks.named("generateClientStubs"))
|
||||
}
|
||||
|
||||
def "should configure generateClientStubs task as a dependency of the publishStubsToScm task"() {
|
||||
given:
|
||||
project.plugins.apply(SpringCloudContractVerifierGradlePlugin)
|
||||
|
||||
expect:
|
||||
project.tasks.publishStubsToScm.getDependsOn().contains("generateClientStubs")
|
||||
project.tasks.publishStubsToScm.getDependsOn().contains(project.tasks.named("generateClientStubs"))
|
||||
}
|
||||
|
||||
def "should create copyContracts task"() {
|
||||
given:
|
||||
project.plugins.apply(SpringCloudContractVerifierGradlePlugin)
|
||||
|
||||
expect:
|
||||
project.tasks.findByName("copyContracts") != null
|
||||
project.tasks.named("copyContracts") != null
|
||||
}
|
||||
|
||||
def "should configure copyContracts task as a dependency of the verifierStubsJar task"() {
|
||||
given:
|
||||
project.plugins.apply(SpringCloudContractVerifierGradlePlugin)
|
||||
Task copyContracts = project.tasks.copyContracts
|
||||
assert copyContracts != null
|
||||
|
||||
expect:
|
||||
project.tasks.verifierStubsJar.getDependsOn().contains(copyContracts)
|
||||
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"))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,22 +92,23 @@ class ContractVerifierSpec extends Specification {
|
||||
project.evaluate() // Currently internal method to trigger afterEvaluate blocks.
|
||||
|
||||
expect:
|
||||
def publications = project.extensions.getByType(PublishingExtension).publications
|
||||
PublicationContainer publications = project.extensions.getByType(PublishingExtension).publications
|
||||
publications.size() > 0
|
||||
publications.findByName("stubs") != null
|
||||
publications.named("stubs") != null
|
||||
}
|
||||
|
||||
def "should compile"() {
|
||||
given:
|
||||
ContractVerifierExtension extension = new ContractVerifierExtension()
|
||||
ObjectFactory objectFactory = Stub(ObjectFactory)
|
||||
ContractVerifierExtension extension = new ContractVerifierExtension(objectFactory)
|
||||
extension.with {
|
||||
|
||||
// tag::package_with_base_classes[]
|
||||
packageWithBaseClasses = 'com.example.base'
|
||||
packageWithBaseClasses.set('com.example.base')
|
||||
// end::package_with_base_classes[]
|
||||
|
||||
// tag::base_class_mappings[]
|
||||
baseClassForTests = "com.example.FooBase"
|
||||
baseClassForTests.set("com.example.FooBase")
|
||||
baseClassMappings {
|
||||
baseClassMapping('.*/com/.*', 'com.example.ComBase')
|
||||
baseClassMapping('.*/bar/.*': 'com.example.BarBase')
|
||||
|
||||
@@ -1,27 +1,38 @@
|
||||
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 spock.lang.Specification
|
||||
|
||||
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 org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
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"
|
||||
def dep = new ContractVerifierExtension.Dependency(stringNotation: stringNotation)
|
||||
ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory)
|
||||
dep.stringNotation.set(stringNotation)
|
||||
when:
|
||||
StubConfiguration stubConfig = new GradleContractsDownloader(null, null).stubConfiguration(dep)
|
||||
then:
|
||||
@@ -33,12 +44,11 @@ class GradleContractsDownloaderSpec extends Specification {
|
||||
|
||||
def "should parse dependency via direct setting"() {
|
||||
given:
|
||||
def dep = new ContractVerifierExtension.Dependency(
|
||||
groupId: "com.example",
|
||||
artifactId: "foo",
|
||||
version: "1.0.0",
|
||||
classifier: "stubs"
|
||||
)
|
||||
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:
|
||||
@@ -51,8 +61,8 @@ class GradleContractsDownloaderSpec extends Specification {
|
||||
def "should parse dependency via string notation with methods"() {
|
||||
given:
|
||||
String stringNotation = "com.example:foo:1.0.0:stubs"
|
||||
def dep = new ContractVerifierExtension.Dependency()
|
||||
dep.stringNotation(stringNotation)
|
||||
ContractVerifierExtension.Dependency dep = new ContractVerifierExtension.Dependency(objectFactory)
|
||||
dep.stringNotation.set(stringNotation)
|
||||
when:
|
||||
StubConfiguration stubConfig = new GradleContractsDownloader(null, null).stubConfiguration(dep)
|
||||
then:
|
||||
@@ -64,11 +74,11 @@ class GradleContractsDownloaderSpec extends Specification {
|
||||
|
||||
def "should parse dependency via direct setting with methods"() {
|
||||
given:
|
||||
def dep = new ContractVerifierExtension.Dependency()
|
||||
dep.groupId("com.example")
|
||||
dep.artifactId("foo")
|
||||
dep.version("1.0.0")
|
||||
dep.classifier("stubs")
|
||||
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:
|
||||
@@ -80,158 +90,138 @@ class GradleContractsDownloaderSpec extends Specification {
|
||||
|
||||
def "should pick dependency from cache for a non snapshot contract dependency with new property"() {
|
||||
given:
|
||||
ContractVerifierExtension ext = new ContractVerifierExtension()
|
||||
ext.with {
|
||||
contractsMode = StubRunnerProperties.StubsMode.REMOTE
|
||||
contractDependency {
|
||||
groupId("com.example")
|
||||
artifactId("foo")
|
||||
version("1.0.0")
|
||||
classifier("stubs")
|
||||
}
|
||||
contractRepository {
|
||||
repositoryUrl("foo")
|
||||
}
|
||||
}
|
||||
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:
|
||||
def gradleDownloader = stubbedContractDownloader(downloader, contractDownloader)
|
||||
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:
|
||||
File file = gradleDownloader.downloadAndUnpackContractsIfRequired(ext, new ContractVerifierConfigProperties())
|
||||
GradleContractsDownloader.DownloadedData downloaded = gradleDownloader.downloadAndUnpackContractsIfRequired(contractDependency, contractRepository, null, StubRunnerProperties.StubsMode.REMOTE, true, [:], true)
|
||||
then:
|
||||
file == expectedFileFromCache
|
||||
downloaded.downloadedContracts == 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 {
|
||||
contractsMode = StubRunnerProperties.StubsMode.REMOTE
|
||||
contractDependency {
|
||||
groupId("com.example")
|
||||
artifactId("foo")
|
||||
version("1.0.0")
|
||||
classifier("stubs")
|
||||
}
|
||||
contractRepository {
|
||||
repositoryUrl("foo")
|
||||
cacheDownloadedContracts(false)
|
||||
}
|
||||
disableStubPublication(true)
|
||||
}
|
||||
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:
|
||||
def gradleDownloader = stubbedContractDownloader(downloader, contractDownloader)
|
||||
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.unpackedDownloadedContracts(_) >> expectedFileNotFromCache
|
||||
contractDownloader.unpackAndDownloadContracts() >> expectedFileNotFromCache
|
||||
when:
|
||||
File file = gradleDownloader.downloadAndUnpackContractsIfRequired(ext, new ContractVerifierConfigProperties())
|
||||
GradleContractsDownloader.DownloadedData downloaded = gradleDownloader.downloadAndUnpackContractsIfRequired(contractDependency, contractRepository, null, StubRunnerProperties.StubsMode.REMOTE, true, [:], true)
|
||||
then:
|
||||
file == expectedFileNotFromCache
|
||||
downloaded.downloadedContracts == expectedFileNotFromCache
|
||||
}
|
||||
|
||||
def "should not pick dependency from cache for snapshot contract dependency"() {
|
||||
given:
|
||||
ContractVerifierExtension ext = new ContractVerifierExtension()
|
||||
ext.with {
|
||||
contractsMode = StubRunnerProperties.StubsMode.REMOTE
|
||||
contractDependency {
|
||||
groupId("com.example")
|
||||
artifactId("foo")
|
||||
version("1.0.0.BUILD-SNAPSHOT")
|
||||
classifier("stubs")
|
||||
}
|
||||
contractRepository {
|
||||
repositoryUrl("foo")
|
||||
}
|
||||
}
|
||||
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.unpackedDownloadedContracts(_) >> expectedFileNotFromCache
|
||||
contractDownloader.unpackAndDownloadContracts() >> expectedFileNotFromCache
|
||||
and:
|
||||
def gradleDownloader = stubbedContractDownloader(downloader, contractDownloader)
|
||||
GradleContractsDownloader gradleDownloader = stubbedContractDownloader(downloader, contractDownloader)
|
||||
when:
|
||||
File file = gradleDownloader.downloadAndUnpackContractsIfRequired(ext, new ContractVerifierConfigProperties())
|
||||
GradleContractsDownloader.DownloadedData downloaded = gradleDownloader.downloadAndUnpackContractsIfRequired(contractDependency, contractRepository, null, StubRunnerProperties.StubsMode.REMOTE, true, [:], true)
|
||||
then:
|
||||
file == expectedFileNotFromCache
|
||||
downloaded.downloadedContracts == expectedFileNotFromCache
|
||||
}
|
||||
|
||||
private GradleContractsDownloader stubbedContractDownloader(downloader, contractDownloader) {
|
||||
new GradleContractsDownloader(project, logger) {
|
||||
@Override
|
||||
protected AetherStubDownloader stubDownloader(ContractVerifierExtension extension) {
|
||||
protected AetherStubDownloader stubDownloader(ContractVerifierExtension.ContractRepository contractRepository,
|
||||
StubRunnerProperties.StubsMode contractsMode, boolean deleteStubsAfterTest,
|
||||
Map<String, String> contractsProperties, boolean failOnNoContracts) {
|
||||
return downloader
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ContractDownloader contractDownloader(ContractVerifierExtension extension, StubConfiguration configuration) {
|
||||
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 pick contract directory location from extension"() {
|
||||
def "should not start downloading"() {
|
||||
given:
|
||||
ContractVerifierExtension ext = new ContractVerifierExtension()
|
||||
ext.with {
|
||||
contractsDslDir = new File("/foo/bar/baz")
|
||||
}
|
||||
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:
|
||||
def gradleDownloader = stubbedContractDownloader(downloader, contractDownloader)
|
||||
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:
|
||||
File file = gradleDownloader.downloadAndUnpackContractsIfRequired(ext, new ContractVerifierConfigProperties())
|
||||
GradleContractsDownloader.DownloadedData downloaded = gradleDownloader.downloadAndUnpackContractsIfRequired(contractDependency, contractRepository, null, StubRunnerProperties.StubsMode.CLASSPATH, true, [:], true)
|
||||
then:
|
||||
file == new File("/foo/bar/baz")
|
||||
downloaded == null
|
||||
}
|
||||
|
||||
def "should pass contract dependency properties as a parameter to the builder"() {
|
||||
given:
|
||||
ContractVerifierExtension ext = new ContractVerifierExtension()
|
||||
ext.with {
|
||||
contractDependency {
|
||||
groupId("com.example")
|
||||
artifactId("foo")
|
||||
version("1.0.0.BUILD-SNAPSHOT")
|
||||
classifier("stubs")
|
||||
}
|
||||
contractRepository {
|
||||
repositoryUrl("foo")
|
||||
username("foo1")
|
||||
password("foo2")
|
||||
proxyHost("foo3")
|
||||
proxyPort(12)
|
||||
}
|
||||
}
|
||||
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.unpackedDownloadedContracts(_) >> expectedFileNotFromCache
|
||||
contractDownloader.unpackAndDownloadContracts() >> expectedFileNotFromCache
|
||||
and:
|
||||
def gradleDownloader = assertingContractDownloader(downloader, contractDownloader)
|
||||
GradleContractsDownloader gradleDownloader = assertingContractDownloader(downloader, contractDownloader)
|
||||
when:
|
||||
gradleDownloader.downloadAndUnpackContractsIfRequired(ext, new ContractVerifierConfigProperties())
|
||||
gradleDownloader.downloadAndUnpackContractsIfRequired(contractDependency, contractRepository, null, StubRunnerProperties.StubsMode.CLASSPATH, true, [:], true)
|
||||
then:
|
||||
noExceptionThrown()
|
||||
}
|
||||
@@ -239,7 +229,9 @@ class GradleContractsDownloaderSpec extends Specification {
|
||||
private GradleContractsDownloader assertingContractDownloader(downloader, contractDownloader) {
|
||||
new GradleContractsDownloader(project, logger) {
|
||||
@Override
|
||||
protected AetherStubDownloader stubDownloader(ContractVerifierExtension extension) {
|
||||
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"
|
||||
@@ -248,10 +240,18 @@ class GradleContractsDownloaderSpec extends Specification {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ContractDownloader contractDownloader(ContractVerifierExtension extension, StubConfiguration configuration) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,12 @@
|
||||
|
||||
package org.springframework.cloud.contract.verifier.plugin
|
||||
|
||||
import org.gradle.testkit.runner.BuildResult
|
||||
import org.junit.Ignore
|
||||
import spock.lang.Stepwise
|
||||
|
||||
import static org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE
|
||||
|
||||
@Stepwise
|
||||
@Ignore
|
||||
class SampleProjectSpec extends ContractVerifierIntegrationSpec {
|
||||
@@ -32,22 +35,33 @@ class SampleProjectSpec extends ContractVerifierIntegrationSpec {
|
||||
def "should pass basic flow for Spock"() {
|
||||
given:
|
||||
assert fileExists('build.gradle')
|
||||
expect:
|
||||
when:
|
||||
String[] args = ["check", "publishToMavenLocal", "--debug"] as String[]
|
||||
if (WORK_OFFLINE) {
|
||||
args << "--offline"
|
||||
}
|
||||
runTasksSuccessfully(args)
|
||||
then:
|
||||
jarContainsContractVerifierContracts('fraudDetectionService/build/libs')
|
||||
when: "running generation without change inputs"
|
||||
BuildResult secondExecutionResult = runTasksSuccessfully()
|
||||
then: "tasks should be up-to-date"
|
||||
validateTasksOutcome(secondExecutionResult, UP_TO_DATE, 'generateClientStubs', 'generateContractTests', 'copyContracts')
|
||||
|
||||
}
|
||||
|
||||
def "should pass basic flow for JUnit"() {
|
||||
given:
|
||||
switchToJunitTestFramework()
|
||||
assert fileExists('build.gradle')
|
||||
expect:
|
||||
when:
|
||||
runTasksSuccessfully(checkAndPublishToMavenLocal())
|
||||
then:
|
||||
jarContainsContractVerifierContracts('fraudDetectionService/build/libs')
|
||||
when: "running generation without change inputs"
|
||||
BuildResult secondExecutionResult = runTasksSuccessfully()
|
||||
then: "tasks should be up-to-date"
|
||||
validateTasksOutcome(secondExecutionResult, UP_TO_DATE, 'generateClientStubs', 'generateContractTests', 'copyContracts')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ contracts {
|
||||
testFramework = 'Spock'
|
||||
}
|
||||
|
||||
generateContractTests.dependsOn generateClientStubs
|
||||
project.tasks.named("generateContractTests").get().dependsOn project.tasks.named("generateClientStubs")
|
||||
|
||||
test {
|
||||
testLogging {
|
||||
|
||||
@@ -128,5 +128,5 @@ configure(project(':loanApplicationService')) {
|
||||
into "src/test/resources/"
|
||||
}
|
||||
|
||||
generateContractTests.dependsOn('copyCollaboratorStubs')
|
||||
project.tasks.named("generateContractTests").get().dependsOn('copyCollaboratorStubs')
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ subprojects {
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
version = "1.0.0"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@@ -154,5 +155,5 @@ configure(project(':loanApplicationService')) {
|
||||
into "src/test/resources/mappings"
|
||||
}
|
||||
|
||||
generateContractTests.dependsOn('copyCollaboratorStubs')
|
||||
project.tasks.named("generateContractTests").get().dependsOn('copyCollaboratorStubs')
|
||||
}
|
||||
|
||||
@@ -215,7 +215,10 @@ public class ConvertMojo extends AbstractMojo {
|
||||
config.setContractsDslDir(contractsDslDir);
|
||||
config.setStubsOutputDir(stubsOutputDir(rootPath));
|
||||
logSetup(config, contractsDslDir);
|
||||
RecursiveFilesConverter converter = new RecursiveFilesConverter(config);
|
||||
RecursiveFilesConverter converter = new RecursiveFilesConverter(
|
||||
config.getStubsOutputDir(), config.getContractsDslDir(),
|
||||
config.getExcludedFiles(), config.getIncludedContracts(),
|
||||
config.isExcludeBuildFolders());
|
||||
converter.processFiles();
|
||||
}
|
||||
|
||||
|
||||
@@ -106,15 +106,24 @@ class MavenContractsDownloader {
|
||||
this.log.info(
|
||||
"Another mojo has downloaded the contracts - will reuse them from ["
|
||||
+ downloadedContractsDir + "]");
|
||||
contractDownloader().updatePropertiesWithInclusion(downloadedContractsDir,
|
||||
config);
|
||||
final ContractDownloader.InclusionProperties inclusionProperties = contractDownloader()
|
||||
.createNewInclusionProperties(downloadedContractsDir);
|
||||
config.setIncludedContracts(inclusionProperties.getIncludedContracts());
|
||||
config.setIncludedRootFolderAntPattern(
|
||||
inclusionProperties.getIncludedRootFolderAntPattern());
|
||||
return downloadedContractsDir;
|
||||
}
|
||||
else if (shouldDownloadContracts()) {
|
||||
this.log.info(
|
||||
"Download dependency is provided - will retrieve contracts from a remote location");
|
||||
File downloadedContracts = contractDownloader()
|
||||
.unpackedDownloadedContracts(config);
|
||||
final ContractDownloader contractDownloader = contractDownloader();
|
||||
final File downloadedContracts = contractDownloader
|
||||
.unpackAndDownloadContracts();
|
||||
final ContractDownloader.InclusionProperties inclusionProperties = contractDownloader
|
||||
.createNewInclusionProperties(downloadedContracts);
|
||||
config.setIncludedContracts(inclusionProperties.getIncludedContracts());
|
||||
config.setIncludedRootFolderAntPattern(
|
||||
inclusionProperties.getIncludedRootFolderAntPattern());
|
||||
this.project.getProperties().setProperty(CONTRACTS_DIRECTORY_PROP,
|
||||
downloadedContracts.getAbsolutePath());
|
||||
return downloadedContracts;
|
||||
|
||||
@@ -26,7 +26,6 @@ import groovy.transform.PackageScope
|
||||
import groovy.util.logging.Commons
|
||||
|
||||
import org.springframework.cloud.contract.verifier.builder.SingleTestGenerator
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
|
||||
import static org.springframework.cloud.contract.verifier.util.NamesUtil.beforeLast
|
||||
import static org.springframework.cloud.contract.verifier.util.NamesUtil.capitalize
|
||||
@@ -39,12 +38,12 @@ class FileSaver {
|
||||
|
||||
private final File targetDirectory
|
||||
private final SingleTestGenerator generator
|
||||
private final ContractVerifierConfigProperties properties
|
||||
private final String fileExtension
|
||||
|
||||
FileSaver(File targetDirectory, SingleTestGenerator generator, ContractVerifierConfigProperties properties) {
|
||||
FileSaver(File targetDirectory, String fileExtension, SingleTestGenerator generator) {
|
||||
this.targetDirectory = targetDirectory
|
||||
this.generator = generator
|
||||
this.properties = properties
|
||||
this.fileExtension = fileExtension
|
||||
}
|
||||
|
||||
void saveClassFile(Path classPath, byte[] classBytes) {
|
||||
@@ -55,8 +54,7 @@ class FileSaver {
|
||||
|
||||
protected Path pathToClass(Path testBaseDir, String fileName) {
|
||||
return Paths.get(testBaseDir.toString(),
|
||||
capitalize(fileName) + generator.fileExtension(this.properties)).
|
||||
toAbsolutePath()
|
||||
capitalize(fileName) + fileExtension).toAbsolutePath()
|
||||
}
|
||||
|
||||
protected Path generateTestBaseDir(String basePackageClass, String includedDirectoryRelativePath) {
|
||||
|
||||
@@ -60,8 +60,8 @@ class TestGenerator {
|
||||
|
||||
TestGenerator(ContractVerifierConfigProperties configProperties) {
|
||||
this(configProperties, singleTestGenerator(),
|
||||
new FileSaver(configProperties.generatedTestSourcesDir,
|
||||
singleTestGenerator(), configProperties))
|
||||
new FileSaver(configProperties.generatedTestSourcesDir, configProperties.testFramework.classExtension,
|
||||
singleTestGenerator()))
|
||||
}
|
||||
|
||||
private static SingleTestGenerator singleTestGenerator() {
|
||||
|
||||
@@ -22,8 +22,6 @@ import java.util.Optional;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties;
|
||||
import org.springframework.cloud.contract.verifier.util.NamesUtil;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -33,47 +31,44 @@ class BaseClassProvider {
|
||||
|
||||
private static final String SEPARATOR = "_REPLACEME_";
|
||||
|
||||
String retrieveBaseClass(ContractVerifierConfigProperties properties,
|
||||
String retrieveBaseClass(Map<String, String> baseClassMappings, String packageWithBaseClasses, String baseClassForTests,
|
||||
String includedDirectoryRelativePath) {
|
||||
String contractPathAsPackage = includedDirectoryRelativePath
|
||||
.replace(File.separator, ".");
|
||||
String contractPackage = includedDirectoryRelativePath.replace(File.separator,
|
||||
SEPARATOR);
|
||||
// package mapping takes super precedence
|
||||
if (properties.getBaseClassMappings() != null
|
||||
&& !properties.getBaseClassMappings().isEmpty()) {
|
||||
Optional<Map.Entry<String, String>> mapping = properties
|
||||
.getBaseClassMappings().entrySet().stream().filter(entry -> {
|
||||
if (baseClassMappings != null && !baseClassMappings.isEmpty()) {
|
||||
Optional<Map.Entry<String, String>> mapping = baseClassMappings.entrySet().stream().filter(entry -> {
|
||||
String pattern = entry.getKey();
|
||||
return contractPathAsPackage.matches(pattern);
|
||||
}).findFirst();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Matching pattern for contract package ["
|
||||
+ contractPathAsPackage + "] with setup "
|
||||
+ properties.getBaseClassMappings() + " is [" + mapping + "]");
|
||||
+ baseClassMappings + " is [" + mapping
|
||||
+ "]");
|
||||
}
|
||||
if (mapping.isPresent()) {
|
||||
return mapping.get().getValue();
|
||||
}
|
||||
}
|
||||
if (StringUtils.isEmpty(properties.getPackageWithBaseClasses())) {
|
||||
return properties.getBaseClassForTests();
|
||||
if (StringUtils.isEmpty(packageWithBaseClasses)) {
|
||||
return baseClassForTests;
|
||||
}
|
||||
String generatedClassName = generateDefaultBaseClassName(contractPackage,
|
||||
properties);
|
||||
String generatedClassName = generateDefaultBaseClassName(contractPackage, packageWithBaseClasses);
|
||||
return generatedClassName + "Base";
|
||||
}
|
||||
|
||||
private String generateDefaultBaseClassName(String classPackage,
|
||||
ContractVerifierConfigProperties properties) {
|
||||
private String generateDefaultBaseClassName(String classPackage, String packageWithBaseClasses) {
|
||||
String[] splitPackage = NamesUtil.convertIllegalPackageChars(classPackage)
|
||||
.split(SEPARATOR);
|
||||
if (splitPackage.length > 1) {
|
||||
String last = NamesUtil.capitalize(splitPackage[splitPackage.length - 1]);
|
||||
String butLast = NamesUtil.capitalize(splitPackage[splitPackage.length - 2]);
|
||||
return properties.getPackageWithBaseClasses() + "." + butLast + last;
|
||||
return packageWithBaseClasses + "." + butLast + last;
|
||||
}
|
||||
return properties.getPackageWithBaseClasses() + "."
|
||||
return packageWithBaseClasses + "."
|
||||
+ NamesUtil.capitalize(splitPackage[0]);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ interface DefaultBaseClassProvider {
|
||||
default String fqnBaseClass() {
|
||||
ContractVerifierConfigProperties properties = generatedClassMetaData().configProperties;
|
||||
String includedDirectoryRelativePath = generatedClassMetaData().includedDirectoryRelativePath;
|
||||
return baseClassProvider().retrieveBaseClass(properties,
|
||||
includedDirectoryRelativePath);
|
||||
return baseClassProvider().retrieveBaseClass(properties.getBaseClassMappings(), properties.getPackageWithBaseClasses(),
|
||||
properties.getBaseClassForTests(), includedDirectoryRelativePath);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ class GenericJsonBodyThen implements Then {
|
||||
private void addJsonBodyVerification(SingleContractMetadata contractMetadata,
|
||||
Object responseBody, BodyMatchers bodyMatchers) {
|
||||
JsonBodyVerificationBuilder jsonBodyVerificationBuilder = new JsonBodyVerificationBuilder(
|
||||
this.generatedClassMetaData.configProperties, this.templateProcessor,
|
||||
this.generatedClassMetaData.configProperties.getAssertJsonSize(), this.templateProcessor,
|
||||
this.contractTemplate, contractMetadata.getContract(),
|
||||
Optional.of(this.blockBuilder.getLineEnding()),
|
||||
bodyParser::postProcessJsonPath);
|
||||
|
||||
@@ -73,8 +73,8 @@ class GroovyClassMetaData implements ClassMetaData, DefaultClassMetadata {
|
||||
public ClassMetaData parentClass() {
|
||||
ContractVerifierConfigProperties properties = generatedClassMetaData().configProperties;
|
||||
String includedDirectoryRelativePath = generatedClassMetaData().includedDirectoryRelativePath;
|
||||
String baseClass = baseClassProvider().retrieveBaseClass(properties,
|
||||
includedDirectoryRelativePath);
|
||||
String baseClass = baseClassProvider().retrieveBaseClass(properties.getBaseClassMappings(),
|
||||
properties.getPackageWithBaseClasses(), properties.getBaseClassForTests(), includedDirectoryRelativePath);
|
||||
baseClass = StringUtils.hasText(baseClass) ? baseClass : "Specification";
|
||||
int lastIndexOf = baseClass.lastIndexOf(".");
|
||||
if (lastIndexOf > 0) {
|
||||
|
||||
@@ -121,6 +121,7 @@ public class JavaTestGenerator implements SingleTestGenerator {
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public String fileExtension(ContractVerifierConfigProperties properties) {
|
||||
return properties.getTestFramework().getClassExtension();
|
||||
|
||||
@@ -34,7 +34,6 @@ import org.springframework.cloud.contract.spec.internal.BodyMatchers;
|
||||
import org.springframework.cloud.contract.spec.internal.ExecutionProperty;
|
||||
import org.springframework.cloud.contract.spec.internal.MatchingType;
|
||||
import org.springframework.cloud.contract.spec.internal.RegexProperty;
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties;
|
||||
import org.springframework.cloud.contract.verifier.template.TemplateProcessor;
|
||||
import org.springframework.cloud.contract.verifier.util.JsonPaths;
|
||||
import org.springframework.cloud.contract.verifier.util.JsonToJsonPathsConverter;
|
||||
@@ -53,7 +52,7 @@ class JsonBodyVerificationBuilder implements BodyMethodGeneration, ClassVerifier
|
||||
|
||||
private static final String FROM_REQUEST_PATH = "path";
|
||||
|
||||
private final ContractVerifierConfigProperties configProperties;
|
||||
private final boolean assertJsonSize;
|
||||
|
||||
private final TemplateProcessor templateProcessor;
|
||||
|
||||
@@ -69,11 +68,11 @@ class JsonBodyVerificationBuilder implements BodyMethodGeneration, ClassVerifier
|
||||
// Passing way more arguments here than I would like to, but since we are planning a
|
||||
// major
|
||||
// refactoring of this module for Hoxton release, leaving it this way for now
|
||||
JsonBodyVerificationBuilder(ContractVerifierConfigProperties configProperties,
|
||||
JsonBodyVerificationBuilder(boolean assertJsonSize,
|
||||
TemplateProcessor templateProcessor, ContractTemplate contractTemplate,
|
||||
Contract contract, Optional<String> lineSuffix,
|
||||
Function<String, String> postProcessJsonPathCall) {
|
||||
this.configProperties = configProperties;
|
||||
this.assertJsonSize = assertJsonSize;
|
||||
this.templateProcessor = templateProcessor;
|
||||
this.contractTemplate = contractTemplate;
|
||||
this.contract = contract;
|
||||
@@ -107,7 +106,7 @@ class JsonBodyVerificationBuilder implements BodyMethodGeneration, ClassVerifier
|
||||
? TestSideRequestTemplateModel.from(contract.getRequest()) : null;
|
||||
convertedResponseBody = MapConverter.transformValues(convertedResponseBody,
|
||||
returnReferencedEntries(templateModel), parsingClosure);
|
||||
JsonPaths jsonPaths = new JsonToJsonPathsConverter(configProperties)
|
||||
JsonPaths jsonPaths = new JsonToJsonPathsConverter(assertJsonSize)
|
||||
.transformToJsonPathWithTestsSideValues(convertedResponseBody,
|
||||
parsingClosure);
|
||||
DocumentContext finalParsedRequestBody = parsedRequestBody;
|
||||
|
||||
@@ -69,6 +69,7 @@ public interface SingleTestGenerator {
|
||||
* or {@code .php}
|
||||
* @param properties - properties passed to the plugin
|
||||
*/
|
||||
@Deprecated
|
||||
String fileExtension(ContractVerifierConfigProperties properties);
|
||||
|
||||
class GeneratedClassData {
|
||||
|
||||
@@ -57,14 +57,20 @@ class JsonToJsonPathsConverter {
|
||||
private static final String ANY_ARRAY_NOTATION_IN_JSONPATH = "[*]"
|
||||
private static final String DESCENDANT_OPERATOR = ".."
|
||||
|
||||
private final ContractVerifierConfigProperties configProperties
|
||||
private final boolean assertJsonSize
|
||||
|
||||
// Use constructor with dedicated input param instead
|
||||
@Deprecated
|
||||
JsonToJsonPathsConverter(ContractVerifierConfigProperties configProperties) {
|
||||
this.configProperties = configProperties
|
||||
assertJsonSize = configProperties.assertJsonSize
|
||||
}
|
||||
|
||||
JsonToJsonPathsConverter(boolean assertJsonSize) {
|
||||
this.assertJsonSize = assertJsonSize
|
||||
}
|
||||
|
||||
JsonToJsonPathsConverter() {
|
||||
this(new ContractVerifierConfigProperties())
|
||||
this(false)
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Creating JsonToJsonPaths converter with default properties")
|
||||
}
|
||||
@@ -401,7 +407,7 @@ class JsonToJsonPathsConverter {
|
||||
// Size verification: https://github.com/Codearte/accurest/issues/279
|
||||
private void addSizeVerificationForListWithPrimitives(MethodBufferingJsonVerifiable key, Closure closure, List value) {
|
||||
String systemPropValue = System.getProperty(SIZE_ASSERTION_SYSTEM_PROP)
|
||||
Boolean configPropValue = configProperties.assertJsonSize
|
||||
Boolean configPropValue = assertJsonSize
|
||||
if ((systemPropValue != null && Boolean.parseBoolean(systemPropValue))
|
||||
||
|
||||
configPropValue && listContainsOnlyPrimitives(value)) {
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package org.springframework.cloud.contract.verifier.builder
|
||||
|
||||
|
||||
import spock.lang.Issue
|
||||
import spock.lang.Specification
|
||||
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@@ -12,72 +11,82 @@ class ClassBuilderSpec extends Specification {
|
||||
|
||||
def "should return explicit base class if provided and no default package for base classes is provided"() {
|
||||
given:
|
||||
ContractVerifierConfigProperties props = new ContractVerifierConfigProperties(baseClassForTests: 'a.b.Class')
|
||||
Map<String, String> baseClassMappings = null
|
||||
String packageWithBaseClasses = null
|
||||
String baseClassForTests = 'a.b.Class'
|
||||
expect:
|
||||
'a.b.Class' == new BaseClassProvider().retrieveBaseClass(props, 'com/example/foo')
|
||||
'a.b.Class' == new BaseClassProvider().retrieveBaseClass(baseClassMappings, packageWithBaseClasses, baseClassForTests, 'com/example/foo')
|
||||
}
|
||||
|
||||
def "should return a class from the generated path by taking two last folders when package with base classes is provided"() {
|
||||
given:
|
||||
ContractVerifierConfigProperties props = new ContractVerifierConfigProperties(packageWithBaseClasses: 'com.example.base')
|
||||
Map<String, String> baseClassMappings = null
|
||||
String packageWithBaseClasses = 'com.example.base'
|
||||
String baseClassForTests = null
|
||||
String contractRelativeFolder = ['com', 'example', 'some', 'superpackage'].join(File.separator)
|
||||
expect:
|
||||
new BaseClassProvider().retrieveBaseClass(props, contractRelativeFolder) == 'com.example.base.SomeSuperpackageBase'
|
||||
new BaseClassProvider().retrieveBaseClass(baseClassMappings, packageWithBaseClasses, baseClassForTests, contractRelativeFolder) == 'com.example.base.SomeSuperpackageBase'
|
||||
}
|
||||
|
||||
def "should return a class from the generated path by taking two last folders when package with base classes is provided and contains invalid chars"() {
|
||||
given:
|
||||
ContractVerifierConfigProperties props = new ContractVerifierConfigProperties(packageWithBaseClasses: 'com.example.base')
|
||||
Map<String, String> baseClassMappings = null
|
||||
String packageWithBaseClasses = 'com.example.base'
|
||||
String baseClassForTests = null
|
||||
String contractRelativeFolder = ['com', 'example', 'beer-api-producer-external', 'beer-api-consumer'].join(File.separator)
|
||||
expect:
|
||||
new BaseClassProvider().retrieveBaseClass(props, contractRelativeFolder) == 'com.example.base.Beer_api_producer_externalBeer_api_consumerBase'
|
||||
new BaseClassProvider().retrieveBaseClass(baseClassMappings, packageWithBaseClasses, baseClassForTests, contractRelativeFolder) == 'com.example.base.Beer_api_producer_externalBeer_api_consumerBase'
|
||||
}
|
||||
|
||||
def "should return a class from the generated path by taking a single folder when package with base classes is provided and there are not enough package elements"() {
|
||||
given:
|
||||
ContractVerifierConfigProperties props = new ContractVerifierConfigProperties(packageWithBaseClasses: 'com.example.base')
|
||||
Map<String, String> baseClassMappings = null
|
||||
String packageWithBaseClasses = 'com.example.base'
|
||||
String baseClassForTests = null
|
||||
String contractRelativeFolder = 'superpackage'
|
||||
expect:
|
||||
new BaseClassProvider().retrieveBaseClass(props, contractRelativeFolder) == 'com.example.base.SuperpackageBase'
|
||||
new BaseClassProvider().retrieveBaseClass(baseClassMappings, packageWithBaseClasses, baseClassForTests, contractRelativeFolder) == 'com.example.base.SuperpackageBase'
|
||||
}
|
||||
|
||||
def "should return a class from mappings regardless of other entries if mapping exists"() {
|
||||
given:
|
||||
ContractVerifierConfigProperties props = new ContractVerifierConfigProperties(
|
||||
packageWithBaseClasses: 'com.example.base',
|
||||
baseClassMappings: ['.*': 'com.example.base.SuperClass'])
|
||||
Map<String, String> baseClassMappings = ['.*': 'com.example.base.SuperClass']
|
||||
String packageWithBaseClasses = 'com.example.base'
|
||||
String baseClassForTests = null
|
||||
String contractRelativeFolder = 'superpackage'
|
||||
expect:
|
||||
new BaseClassProvider().retrieveBaseClass(props, contractRelativeFolder) == 'com.example.base.SuperClass'
|
||||
new BaseClassProvider().retrieveBaseClass(baseClassMappings, packageWithBaseClasses, baseClassForTests, contractRelativeFolder) == 'com.example.base.SuperClass'
|
||||
}
|
||||
|
||||
@Issue("701")
|
||||
def "should match base class when mapping regex has multiple folders"() {
|
||||
given:
|
||||
ContractVerifierConfigProperties props = new ContractVerifierConfigProperties(
|
||||
baseClassMappings: ['.*bar.baz.some.*': 'com.example.base.SuperClass'])
|
||||
Map<String, String> baseClassMappings = ['.*bar.baz.some.*': 'com.example.base.SuperClass']
|
||||
String packageWithBaseClasses = null
|
||||
String baseClassForTests = null
|
||||
String contractRelativeFolder = 'foo/bar/baz/some/package'.split("/").join(File.separator)
|
||||
expect:
|
||||
new BaseClassProvider().retrieveBaseClass(props, contractRelativeFolder) == 'com.example.base.SuperClass'
|
||||
new BaseClassProvider().retrieveBaseClass(baseClassMappings, packageWithBaseClasses, baseClassForTests, contractRelativeFolder) == 'com.example.base.SuperClass'
|
||||
}
|
||||
|
||||
def "should return the first matching base class when provided mapping doesn't match"() {
|
||||
given:
|
||||
ContractVerifierConfigProperties props = new ContractVerifierConfigProperties(
|
||||
baseClassForTests: 'a.b.Class',
|
||||
packageWithBaseClasses: 'com.example.base',
|
||||
baseClassMappings: ['patternNotMatchingAnything': 'com.example.base.SuperClass'])
|
||||
Map<String, String> baseClassMappings = ['patternNotMatchingAnything': 'com.example.base.SuperClass']
|
||||
String packageWithBaseClasses = 'com.example.base'
|
||||
String baseClassForTests = 'a.b.Class'
|
||||
String contractRelativeFolder = 'superpackage'
|
||||
expect:
|
||||
new BaseClassProvider().retrieveBaseClass(props, contractRelativeFolder) == 'com.example.base.SuperpackageBase'
|
||||
new BaseClassProvider().retrieveBaseClass(baseClassMappings, packageWithBaseClasses, baseClassForTests, contractRelativeFolder) == 'com.example.base.SuperpackageBase'
|
||||
}
|
||||
|
||||
def "should return a class from the generated path by when external contracts are picked"() {
|
||||
given:
|
||||
ContractVerifierConfigProperties props = new ContractVerifierConfigProperties(packageWithBaseClasses: "foo.Bar")
|
||||
Map<String, String> baseClassMappings = null
|
||||
String packageWithBaseClasses = 'foo.Bar'
|
||||
String baseClassForTests = null
|
||||
String contractRelativeFolder = ["org", "springframework", "cloud", "contract", "verifier", "tests", "META_INF", "com.example", "hello_world", "0.1.0_dev.1.uncommitted+d1174dd"].join(File.separator)
|
||||
expect:
|
||||
new BaseClassProvider().retrieveBaseClass(props, contractRelativeFolder) == 'foo.Bar.Hello_world0_1_0_dev_1_uncommitted_d1174ddBase'
|
||||
new BaseClassProvider().retrieveBaseClass(baseClassMappings, packageWithBaseClasses, baseClassForTests, contractRelativeFolder) == 'foo.Bar.Hello_world0_1_0_dev_1_uncommitted_d1174ddBase'
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.function.Supplier;
|
||||
import org.springframework.cloud.contract.spec.Contract;
|
||||
import org.springframework.cloud.contract.verifier.util.ContractVerifierUtil;
|
||||
|
||||
// tag::class[]
|
||||
class contract_docs_examples implements Supplier<Collection<Contract>> {
|
||||
|
||||
org.springframework.cloud.contract.spec.Contract httpDsl =
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.cloud.contract.spec.internal.DslProperty;
|
||||
import org.springframework.cloud.contract.spec.internal.Request;
|
||||
import org.springframework.cloud.contract.verifier.util.ContractVerifierUtil;
|
||||
|
||||
// tag::class[]
|
||||
class contract_multipart implements Supplier<Collection<Contract>> {
|
||||
|
||||
private static Map<String, DslProperty> namedProps(Request r) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.function.Supplier;
|
||||
import org.springframework.cloud.contract.spec.Contract;
|
||||
import org.springframework.cloud.contract.verifier.util.ContractVerifierUtil;
|
||||
|
||||
// tag::class[]
|
||||
class contract_rest implements Supplier<Collection<Contract>> {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.cloud.contract.spec.Contract;
|
||||
|
||||
// tag::class[]
|
||||
class contract_rest_from_file implements Supplier<Collection<Contract>> {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.function.Supplier;
|
||||
import org.springframework.cloud.contract.spec.Contract;
|
||||
import org.springframework.cloud.contract.verifier.util.ContractVerifierUtil;
|
||||
|
||||
// tag::class[]
|
||||
class contract_rest_with_tags implements Supplier<Collection<Contract>> {
|
||||
|
||||
static Object description = Collections.singletonList(
|
||||
|
||||
Reference in New Issue
Block a user