Consumer Contracts (#83)

With this functionality you can have one centralized repository containing all contracts. This repo will have to produce a JAR containing all contracts. The layout of the repository can be arbitrary but some sensible defaults are assumed. The producer will be able to then download that JAR and produce tests and stubs from it.

fixes #38
This commit is contained in:
Marcin Grzejszczak
2016-09-23 10:16:51 +02:00
committed by GitHub
parent 93782cb049
commit 01f4ad76be
50 changed files with 1995 additions and 168 deletions

View File

@@ -0,0 +1,91 @@
package org.springframework.cloud.contract.stubrunner;
import java.io.File;
import java.lang.invoke.MethodHandles;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties;
import org.springframework.util.StringUtils;
/**
* Downloads a JAR with contracts and sets up the plugin configuration with proper
* inclusion patterns
*
* @author Marcin Grzejszczak
*
* @since 1.0.0
*/
public class ContractDownloader {
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
private final StubDownloader stubDownloader;
private final StubConfiguration contractsJarStubConfiguration;
private final String contractsPath;
private final String projectGroupId;
private final String projectArtifactId;
public ContractDownloader(StubDownloader stubDownloader,
StubConfiguration contractsJarStubConfiguration,
String contractsPath, String projectGroupId, String projectArtifactId) {
this.stubDownloader = stubDownloader;
this.contractsJarStubConfiguration = contractsJarStubConfiguration;
this.contractsPath = contractsPath;
this.projectGroupId = projectGroupId;
this.projectArtifactId = projectArtifactId;
}
/**
* Downloads JAR containing all the contracts. Plugin configuration gets updated with
* the inclusion pattern for the downloaded contracts. The JAR with the contracts contains all
* the contracts for all the projects. We're interested only in its subset.
*
* @param config - Plugin configuration that will get updated with the inclusion pattern
* @return location of the unpacked downloaded stubs
*/
public File unpackedDownloadedContracts(ContractVerifierConfigProperties config) {
File contractsDirectory = unpackAndDownloadContracts();
updatePropertiesWithInclusion(contractsDirectory, config);
return contractsDirectory;
}
public ContractVerifierConfigProperties updatePropertiesWithInclusion(File contractsDirectory,
ContractVerifierConfigProperties config) {
String pattern = StringUtils.hasText(this.contractsPath) ? patternFromProperty(contractsDirectory) :
groupArtifactToPattern(contractsDirectory);
log.info("Pattern to pick contracts equals [" + pattern + "]");
config.setIncludedContracts(pattern);
return config;
}
private String patternFromProperty(File contractsDirectory) {
return "^" + contractsDirectory.getAbsolutePath() + contractsPath() + ".*$";
}
private String contractsPath() {
return this.contractsPath.startsWith(File.separator) ? this.contractsPath : File.separator + this.contractsPath;
}
private File unpackAndDownloadContracts() {
log.info("Will download contracts for [" + this.contractsJarStubConfiguration + "]");
Map.Entry<StubConfiguration, File> unpackedContractStubs = this.stubDownloader
.downloadAndUnpackStubJar(null, this.contractsJarStubConfiguration);
if (unpackedContractStubs == null) {
throw new IllegalStateException("The contracts failed to be downloaded!");
}
return unpackedContractStubs.getValue();
}
private String groupArtifactToPattern(File contractsDirectory) {
return "^" +
contractsDirectory.getAbsolutePath() +
File.separator +
this.projectGroupId.replace(".", File.separator) +
File.separator +
this.projectArtifactId
+ File.separator +
".*$";
}
}

View File

@@ -91,10 +91,17 @@ public class StubConfiguration {
return "";
}
return StringUtils.arrayToDelimitedString(
new String[] { this.groupId, this.artifactId, this.version, this.classifier },
new String[] { nullCheck(this.groupId),
nullCheck(this.artifactId),
nullCheck(this.version),
nullCheck(this.classifier) },
STUB_COLON_DELIMITER);
}
private String nullCheck(String value) {
return StringUtils.hasText(value) ? value : "";
}
public boolean groupIdAndArtifactMatches(String ivyNotationAsString) {
String[] parts = ivyNotationFrom(ivyNotationAsString);
String groupId = parts[0];
@@ -117,6 +124,10 @@ public class StubConfiguration {
return this.classifier;
}
public String getVersion() {
return this.version;
}
@Override
public int hashCode() {
final int prime = 31;

View File

@@ -0,0 +1,41 @@
package org.springframework.cloud.contract.stubrunner
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import spock.lang.Specification
/**
* @author Marcin Grzejszczak
*/
class ContractDownloaderSpec extends Specification {
StubDownloader stubDownloader = Stub()
StubConfiguration stubConfiguration = new StubConfiguration('')
File file = new File('/some/path/to/somewhere')
def 'should set inclusion pattern on config when path pattern was explicitly provided with a separator at the beginning'() {
given:
String contractPath = '/a/b/c/d'
ContractDownloader contractDownloader = new ContractDownloader(stubDownloader,
stubConfiguration, contractPath, '', '')
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
and:
stubDownloader.downloadAndUnpackStubJar(_, _) >> new AbstractMap.SimpleEntry(stubConfiguration, file)
when:
contractDownloader.unpackedDownloadedContracts(properties)
then:
properties.includedContracts == '^/some/path/to/somewhere/a/b/c/d.*$'
}
def 'should set inclusion pattern on config when path pattern was explicitly provided without a separator at the beginning'() {
given:
String contractPath = 'a/b/c/d'
ContractDownloader contractDownloader = new ContractDownloader(stubDownloader,
stubConfiguration, contractPath, '', '')
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
and:
stubDownloader.downloadAndUnpackStubJar(_, _) >> new AbstractMap.SimpleEntry(stubConfiguration, file)
when:
contractDownloader.unpackedDownloadedContracts(properties)
then:
properties.includedContracts == '^/some/path/to/somewhere/a/b/c/d.*$'
}
}