Added support for Stub Runner and Pact Broker
fixes gh-191 commit 533f1d3d0e8f6dcd37677bfef555fc2ea86bc1b1 Author: Tim Ysewyn <Tim.Ysewyn@me.com> Date: Wed Mar 7 15:56:48 2018 +0100 Upgraded pact-jvm-model to 3.5.13
This commit is contained in:
@@ -19,7 +19,7 @@
|
||||
|
||||
<modules>
|
||||
<module>spring-cloud-contract-converters</module>
|
||||
<module>spring-cloud-contract-spec-pact</module>
|
||||
<module>spring-cloud-contract-pact</module>
|
||||
<module>spring-cloud-contract-maven-plugin</module>
|
||||
<module>spring-cloud-contract-gradle-plugin</module>
|
||||
</modules>
|
||||
|
||||
@@ -415,7 +415,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-spec-pact</artifactId>
|
||||
<artifactId>spring-cloud-contract-pact</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -90,7 +90,7 @@ class MavenContractsDownloader {
|
||||
contractDownloader().updatePropertiesWithInclusion(downloadedContractsDir, config);
|
||||
return downloadedContractsDir;
|
||||
} else if (shouldDownloadContracts()) {
|
||||
this.log.info("Download dependency is provided - will download contract jars");
|
||||
this.log.info("Download dependency is provided - will retrieve contracts from a remote location");
|
||||
File downloadedContracts = contractDownloader().unpackedDownloadedContracts(config);
|
||||
this.project.getProperties().setProperty(CONTRACTS_DIRECTORY_PROP, downloadedContracts.getAbsolutePath());
|
||||
return downloadedContracts;
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-spec-pact</artifactId>
|
||||
<artifactId>spring-cloud-contract-pact</artifactId>
|
||||
<version>${spring.cloud.contract.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
<version>2.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-contract-spec-pact</artifactId>
|
||||
<artifactId>spring-cloud-contract-pact</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Contract Spec Pact</name>
|
||||
<description>Spring Cloud Contract Spec Pact</description>
|
||||
<name>Spring Cloud Contract Pact</name>
|
||||
<description>Spring Cloud Contract Pact</description>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
@@ -19,7 +19,11 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-verifier</artifactId>
|
||||
<artifactId>spring-cloud-contract-converters</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-stub-runner</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -37,6 +41,10 @@
|
||||
<groupId>au.com.dius</groupId>
|
||||
<artifactId>pact-jvm-consumer-java8_2.12</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>au.com.dius</groupId>
|
||||
<artifactId>pact-jvm-provider-junit_2.12</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spockframework</groupId>
|
||||
<artifactId>spock-core</artifactId>
|
||||
@@ -52,6 +60,11 @@
|
||||
<artifactId>spock-global-unroll</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-wiremock</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
@@ -62,8 +75,13 @@
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>addSources</goal>
|
||||
<goal>addTestSources</goal>
|
||||
<goal>generateStubs</goal>
|
||||
<goal>compile</goal>
|
||||
<goal>testGenerateStubs</goal>
|
||||
<goal>testCompile</goal>
|
||||
<goal>removeStubs</goal>
|
||||
<goal>removeTestStubs</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
@@ -0,0 +1,376 @@
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.contract.stubrunner;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import au.com.dius.pact.model.Pact;
|
||||
import au.com.dius.pact.model.PactSpecVersion;
|
||||
import au.com.dius.pact.provider.junit.loader.PactBroker;
|
||||
import au.com.dius.pact.provider.junit.loader.PactBrokerAuth;
|
||||
import au.com.dius.pact.provider.junit.loader.PactBrokerLoader;
|
||||
import au.com.dius.pact.provider.junit.loader.PactLoader;
|
||||
import au.com.dius.pact.provider.junit.sysprops.SystemPropertyResolver;
|
||||
import au.com.dius.pact.provider.junit.sysprops.ValueResolver;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.cloud.contract.spec.Contract;
|
||||
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
|
||||
import org.springframework.cloud.contract.verifier.converter.StubGenerator;
|
||||
import org.springframework.cloud.contract.verifier.converter.StubGeneratorProvider;
|
||||
import org.springframework.cloud.contract.verifier.file.ContractMetadata;
|
||||
import org.springframework.cloud.contract.verifier.spec.pact.PactContractConverter;
|
||||
import org.springframework.core.io.AbstractResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Allows downloading of Pact files from the Pact Broker.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public final class PactStubDownloaderBuilder implements StubDownloaderBuilder {
|
||||
private static final List<String> ACCEPTABLE_PROTOCOLS = Collections
|
||||
.singletonList("pact");
|
||||
|
||||
/**
|
||||
* Does any of the accepted protocols matches the URL of the repository
|
||||
* @param url - of the repository
|
||||
*/
|
||||
private static boolean isProtocolAccepted(String url) {
|
||||
return ACCEPTABLE_PROTOCOLS.stream().anyMatch(url::startsWith);
|
||||
}
|
||||
|
||||
@Override public StubDownloader build(StubRunnerOptions stubRunnerOptions) {
|
||||
if (stubRunnerOptions.getStubsMode() == StubRunnerProperties.StubsMode.CLASSPATH ||
|
||||
stubRunnerOptions.getStubRepositoryRoot() == null) {
|
||||
return null;
|
||||
}
|
||||
Resource resource = stubRunnerOptions.getStubRepositoryRoot();
|
||||
if (!(resource instanceof PactResource)) {
|
||||
return null;
|
||||
}
|
||||
return new PactStubDownloader(stubRunnerOptions);
|
||||
}
|
||||
|
||||
@Override public Resource resolve(String location, ResourceLoader resourceLoader) {
|
||||
if (StringUtils.isEmpty(location) || !isProtocolAccepted(location)) {
|
||||
return null;
|
||||
}
|
||||
return new PactResource(location);
|
||||
}
|
||||
}
|
||||
|
||||
class PactResource extends AbstractResource {
|
||||
|
||||
private final String rawLocation;
|
||||
|
||||
PactResource(String location) {
|
||||
this.rawLocation = location;
|
||||
}
|
||||
|
||||
@Override public String getDescription() {
|
||||
return this.rawLocation;
|
||||
}
|
||||
|
||||
@Override public InputStream getInputStream() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public URI getURI() {
|
||||
return URI.create(this.rawLocation);
|
||||
}
|
||||
}
|
||||
|
||||
class PactStubDownloader implements StubDownloader {
|
||||
private static final String TEMP_DIR_PREFIX = "pact";
|
||||
private static final Log log = LogFactory.getLog(PactStubDownloader.class);
|
||||
|
||||
// Preloading class for the shutdown hook not to throw ClassNotFound
|
||||
private static final Class CLAZZ = TemporaryFileStorage.class;
|
||||
private static final String ARTIFICIAL_NAME_ENDING_WITH_GROOVY = "name.groovy";
|
||||
|
||||
private final StubRunnerOptions stubRunnerOptions;
|
||||
private final boolean deleteStubsAfterTest;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
private static final String PROVIDER_NAME_WITH_GROUP_ID = "pactbroker.provider-name-with-group-id";
|
||||
|
||||
PactStubDownloader(StubRunnerOptions stubRunnerOptions) {
|
||||
this.stubRunnerOptions = stubRunnerOptions;
|
||||
this.objectMapper = new ObjectMapper();
|
||||
this.deleteStubsAfterTest = stubRunnerOptions.isDeleteStubsAfterTest();
|
||||
registerShutdownHook();
|
||||
}
|
||||
|
||||
@Override public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
|
||||
StubConfiguration stubConfiguration) {
|
||||
String version = stubConfiguration.version;
|
||||
final FromPropsThenFromSysEnv resolver = new FromPropsThenFromSysEnv(this.stubRunnerOptions);
|
||||
List<String> tags = tags(version, resolver);
|
||||
try {
|
||||
PactLoader loader = pactBrokerLoader(resolver, tags);
|
||||
String providerName = providerName(stubConfiguration);
|
||||
List<Pact> pacts = loader.load(providerName);
|
||||
if (pacts.isEmpty()) {
|
||||
log.warn("No pact definitions found for provider [" + providerName + "]");
|
||||
return null;
|
||||
}
|
||||
File tmpDirWhereStubsWillBeUnzipped = TemporaryFileStorage.createTempDir(TEMP_DIR_PREFIX);
|
||||
// make the groupid / artifactid folders
|
||||
String coordinatesFolderName = stubConfiguration.getGroupId().replace(".", File.separator) +
|
||||
File.separator + stubConfiguration.getArtifactId();
|
||||
File contractsFolder = new File(tmpDirWhereStubsWillBeUnzipped,
|
||||
coordinatesFolderName + File.separator + "contracts");
|
||||
File mappingsFolder = new File(tmpDirWhereStubsWillBeUnzipped,
|
||||
coordinatesFolderName + File.separator + "mappings");
|
||||
boolean createdContractsDirs = contractsFolder.mkdirs();
|
||||
boolean createdMappingsDirs = mappingsFolder.mkdirs();
|
||||
if (!createdContractsDirs || !createdMappingsDirs) {
|
||||
throw new IllegalStateException("Failed to create mandatory [contracts] or [mappings] folders under [" + coordinatesFolderName + "]");
|
||||
}
|
||||
storePacts(providerName, pacts, contractsFolder, mappingsFolder);
|
||||
return new AbstractMap.SimpleEntry<>(stubConfiguration, tmpDirWhereStubsWillBeUnzipped);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void storePacts(String providerName, List<Pact> pacts, File contractsFolder,
|
||||
File mappingsFolder) {
|
||||
for (int i = 0; i < pacts.size(); i++) {
|
||||
String json = toJson(pacts.get(i).toMap(PactSpecVersion.V3));
|
||||
File file = new File(contractsFolder, i + "_" +
|
||||
providerName.replace(":", "_") + "_pact.json");
|
||||
storeFile(file.toPath(), json.getBytes());
|
||||
try {
|
||||
storeMapping(mappingsFolder, file);
|
||||
} catch (Exception e) {
|
||||
log.warn("Exception occurred while trying to store the mapping", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void storeMapping(File mappingsFolder, File file) {
|
||||
Collection<Contract> contracts = new PactContractConverter()
|
||||
.convertFrom(file);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Converted pact file [" + file + "] to [" + contracts.size() + "] contracts");
|
||||
}
|
||||
StubGeneratorProvider provider = new StubGeneratorProvider();
|
||||
Collection<StubGenerator> stubGenerators = provider
|
||||
.converterForName(ARTIFICIAL_NAME_ENDING_WITH_GROOVY);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Found following matching stub generators " + stubGenerators);
|
||||
}
|
||||
for (StubGenerator stubGenerator : stubGenerators) {
|
||||
Map<Contract, String> map = stubGenerator
|
||||
.convertContents(file.getName(),
|
||||
new ContractMetadata(file.toPath(), false,
|
||||
contracts.size(), null, contracts));
|
||||
for (Map.Entry<Contract, String> entry : map.entrySet()) {
|
||||
String value = entry.getValue();
|
||||
File mapping = new File(mappingsFolder,
|
||||
StringUtils.stripFilenameExtension(file.getName()) + "_" +
|
||||
Math.abs(entry.getKey().hashCode()) + ".json");
|
||||
storeFile(mapping.toPath(), value.getBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void storeFile(Path path, byte[] contents) {
|
||||
try {
|
||||
Files.write(path, contents);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Stored file [" + path.toString() + "]");
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String providerName(StubConfiguration stubConfiguration) {
|
||||
boolean providerNameWithGroupId = Boolean.parseBoolean(
|
||||
StubRunnerPropertyUtils.getProperty(this.stubRunnerOptions.getProperties(),
|
||||
PROVIDER_NAME_WITH_GROUP_ID));
|
||||
if (providerNameWithGroupId) {
|
||||
return stubConfiguration.getGroupId() + ":" + stubConfiguration.getArtifactId();
|
||||
}
|
||||
return stubConfiguration.getArtifactId();
|
||||
}
|
||||
|
||||
@NotNull PactLoader pactBrokerLoader(ValueResolver resolver,
|
||||
List<String> tags) throws IOException {
|
||||
Resource repo = this.stubRunnerOptions.getStubRepositoryRoot();
|
||||
String schemeSpecificPart = schemeSpecificPart(repo.getURI());
|
||||
URI pactBrokerUrl = URI.create(schemeSpecificPart);
|
||||
return new PactBrokerLoader(new PactBroker() {
|
||||
|
||||
@Override public Class<? extends Annotation> annotationType() {
|
||||
return PactBroker.class;
|
||||
}
|
||||
|
||||
@Override public String host() {
|
||||
return resolver.resolveValue("pactbroker.host:" + pactBrokerUrl.getHost());
|
||||
}
|
||||
|
||||
@Override public String port() {
|
||||
return resolver.resolveValue("pactbroker.port:" + pactBrokerUrl.getPort());
|
||||
}
|
||||
|
||||
@Override public String protocol() {
|
||||
return resolver.resolveValue("pactbroker.protocol:" + pactBrokerUrl.getScheme());
|
||||
}
|
||||
|
||||
@Override public String[] tags() {
|
||||
return tags.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@Override public boolean failIfNoPactsFound() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public PactBrokerAuth authentication() {
|
||||
return new PactBrokerAuth() {
|
||||
@Override public Class<? extends Annotation> annotationType() {
|
||||
return PactBrokerAuth.class;
|
||||
}
|
||||
|
||||
@Override public String scheme() {
|
||||
return resolver.resolveValue("pactbroker.auth.scheme:basic");
|
||||
}
|
||||
|
||||
@Override public String username() {
|
||||
return resolver.resolveValue("pactbroker.auth.username:");
|
||||
}
|
||||
|
||||
@Override public String password() {
|
||||
return resolver.resolveValue("pactbroker.auth.password:");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override public Class<? extends ValueResolver> valueResolver() {
|
||||
return SystemPropertyResolver.class;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String schemeSpecificPart(URI uri) {
|
||||
String part = uri.getSchemeSpecificPart();
|
||||
if (StringUtils.isEmpty(part)) {
|
||||
return part;
|
||||
}
|
||||
return part.startsWith("//") ? part.substring(2) : part;
|
||||
}
|
||||
|
||||
@NotNull private List<String> tags(String version, ValueResolver resolver) {
|
||||
String defaultTag = StubConfiguration.DEFAULT_VERSION.equals(version)
|
||||
? "latest" : version;
|
||||
return new ArrayList<>(Arrays.asList(StringUtils
|
||||
.commaDelimitedListToStringArray(
|
||||
resolver.resolveValue("pactbroker.tags:" + defaultTag + ""))));
|
||||
}
|
||||
|
||||
private String toJson(Map map) {
|
||||
try {
|
||||
return this.objectMapper.writeValueAsString(map);
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerShutdownHook() {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(
|
||||
() -> TemporaryFileStorage.cleanup(PactStubDownloader.this.deleteStubsAfterTest)));
|
||||
}
|
||||
}
|
||||
|
||||
class FromPropsThenFromSysEnv implements ValueResolver {
|
||||
|
||||
final SystemPropertyResolver resolver = new SystemPropertyResolver();
|
||||
StubRunnerOptions options;
|
||||
|
||||
FromPropsThenFromSysEnv(StubRunnerOptions options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
@Override public String resolveValue(String expression) {
|
||||
PropertyValueTuple tuple = new PropertyValueTuple(expression).invoke();
|
||||
String propertyName = tuple.getPropertyName();
|
||||
String property = StubRunnerPropertyUtils
|
||||
.getProperty(this.options.getProperties(), propertyName);
|
||||
if (StringUtils.hasText(property)) {
|
||||
return property;
|
||||
}
|
||||
return this.resolver.resolveValue(expression);
|
||||
}
|
||||
|
||||
@Override public boolean propertyDefined(String property) {
|
||||
PropertyValueTuple tuple = new PropertyValueTuple(property).invoke();
|
||||
String propertyName = tuple.getPropertyName();
|
||||
boolean hasProperty = StubRunnerPropertyUtils
|
||||
.hasProperty(this.options.getProperties(), propertyName);
|
||||
if (hasProperty) {
|
||||
return true;
|
||||
}
|
||||
return this.resolver.propertyDefined(property);
|
||||
}
|
||||
}
|
||||
|
||||
// taken from pact - au.com.dius.pact.provider.junit.sysprops.SystemPropertyResolver.PropertyValueTuple
|
||||
class PropertyValueTuple {
|
||||
private String propertyName;
|
||||
|
||||
PropertyValueTuple(String property) {
|
||||
this.propertyName = property;
|
||||
}
|
||||
|
||||
String getPropertyName() {
|
||||
return this.propertyName;
|
||||
}
|
||||
|
||||
PropertyValueTuple invoke() {
|
||||
if (this.propertyName.contains(":")) {
|
||||
String[] kv = org.apache.commons.lang3.StringUtils
|
||||
.splitPreserveAllTokens(this.propertyName, ':');
|
||||
this.propertyName = kv[0];
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
org.springframework.cloud.contract.spec.ContractConverter=\
|
||||
org.springframework.cloud.contract.verifier.spec.pact.PactContractConverter
|
||||
|
||||
org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder=\
|
||||
org.springframework.cloud.contract.stubrunner.PactStubDownloaderBuilder
|
||||
@@ -0,0 +1,114 @@
|
||||
package org.springframework.cloud.contract.stubrunner
|
||||
|
||||
import java.nio.file.Files
|
||||
|
||||
import au.com.dius.pact.model.Pact
|
||||
import au.com.dius.pact.model.PactSource
|
||||
import au.com.dius.pact.provider.junit.loader.PactLoader
|
||||
import au.com.dius.pact.provider.junit.sysprops.ValueResolver
|
||||
import com.github.tomakehurst.wiremock.stubbing.StubMapping
|
||||
import org.jetbrains.annotations.NotNull
|
||||
import spock.lang.Specification
|
||||
|
||||
import org.springframework.cloud.contract.spec.Contract
|
||||
import org.springframework.cloud.contract.verifier.spec.pact.PactContractConverter
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
class PactStubDownloaderBuilderSpec extends Specification {
|
||||
|
||||
def "should retrieve pacts from broker"() throws IOException {
|
||||
given:
|
||||
Collection<Pact> pacts = new PactContractConverter().convertTo([Contract.make {
|
||||
request {
|
||||
url "/foo"
|
||||
method GET()
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
}
|
||||
},Contract.make {
|
||||
request {
|
||||
url "/bar"
|
||||
method GET()
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
}
|
||||
}])
|
||||
StubRunnerOptions options = new StubRunnerOptionsBuilder()
|
||||
.withProperties(props())
|
||||
.build()
|
||||
PactStubDownloader downloader = new PactStubDownloader(options) {
|
||||
@NotNull @Override PactLoader pactBrokerLoader(ValueResolver resolver,
|
||||
List<String> tags) {
|
||||
return new PactLoader() {
|
||||
@Override List<Pact> load(String providerName) {
|
||||
return pacts
|
||||
}
|
||||
@Override PactSource getPactSource() {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
when:
|
||||
Map.Entry<StubConfiguration, File> entry = downloader
|
||||
.downloadAndUnpackStubJar(new StubConfiguration("com.example:bobby:+:classifier"))
|
||||
then:
|
||||
entry != null
|
||||
entry.getValue().exists()
|
||||
File contracts = new File(entry.getValue(), "com/example/bobby/contracts")
|
||||
contracts.exists()
|
||||
contracts.list() != null
|
||||
File mappings = new File(entry.getValue(), "com/example/bobby/mappings")
|
||||
mappings.exists()
|
||||
mappings.list() != null
|
||||
mappings.list().size() == 2
|
||||
StubMapping.buildFrom(new String(Files.readAllBytes(mappings.listFiles()[0].toPath())))
|
||||
StubMapping.buildFrom(new String(Files.readAllBytes(mappings.listFiles()[1].toPath())))
|
||||
}
|
||||
|
||||
Map<String, String> props() {
|
||||
Map<String, String> map = new HashMap<>()
|
||||
// map.put("pactbroker.host", "localhost")
|
||||
// map.put("pactbroker.port", String.valueOf(this.port))
|
||||
// map.put("pactbroker.host", "test.pact.dius.com.au")
|
||||
// map.put("pactbroker.port", "443")
|
||||
// map.put("pactbroker.protocol", "https")
|
||||
// map.put("pactbroker.auth.scheme", "Basic")
|
||||
// map.put("pactbroker.auth.username", "dXfltyFMgNOFZAxr8io9wJ37iUpY42M")
|
||||
// map.put("pactbroker.auth.password", "O5AIZWxelWbLvqMd8PkAVycBJh2Psyg1")
|
||||
return map
|
||||
}
|
||||
|
||||
// @After
|
||||
// void tearDown() {
|
||||
// SnapshotRecordResult recording = WireMock.stopRecording()
|
||||
// List<StubMapping> mappings = recording.getStubMappings()
|
||||
// storeMappings(mappings)
|
||||
// }
|
||||
|
||||
// private void recordFromBroker() {
|
||||
// WireMock.startRecording(WireMock.recordSpec()
|
||||
// .forTarget("https://test.pact.dius.com.au")
|
||||
// .extractTextBodiesOver(9999999L)
|
||||
// .extractBinaryBodiesOver(9999999L)
|
||||
// .makeStubsPersistent(false))
|
||||
// }
|
||||
|
||||
// private void storeMappings(List<StubMapping> mappings) {
|
||||
// try {
|
||||
// File proxiedStubs = new File("target/stubs")
|
||||
// proxiedStubs.mkdirs()
|
||||
// for (StubMapping mapping : mappings) {
|
||||
// File stub = new File(proxiedStubs, "foo" + ".json")
|
||||
// stub.createNewFile()
|
||||
// Files.write(stub.toPath(), mapping.toString().getBytes())
|
||||
// }
|
||||
// } catch (Exception e) {
|
||||
// throw new RuntimeException(e)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
org.springframework.cloud.contract.spec.ContractConverter=\
|
||||
org.springframework.cloud.contract.verifier.spec.pact.PactContractConverter
|
||||
@@ -1,68 +0,0 @@
|
||||
package contracts
|
||||
|
||||
org.springframework.cloud.contract.spec.Contract.make {
|
||||
request {
|
||||
method 'POST'
|
||||
url '/'
|
||||
body([
|
||||
someInteger: 1234567890,
|
||||
someDecimal: 123.123,
|
||||
someHex: 'DEADC0DE',
|
||||
someAlphaNumeric: 'Some alpha numeric string with 1234567890',
|
||||
someUUID: '00000000-0000-0000-0000-000000000000',
|
||||
someDate: '2018-03-26',
|
||||
someTime: '13:37:00',
|
||||
someDateTime: '2018-03-26 13:37:00',
|
||||
someBoolean: 'true',
|
||||
someNullValue: null
|
||||
])
|
||||
headers {
|
||||
contentType('application/json')
|
||||
header("Some-Header", $(c(regex('[a-zA-Z]{9}')), p('someValue')))
|
||||
header("Header-Without-Matcher", 'someValue')
|
||||
}
|
||||
bodyMatchers {
|
||||
jsonPath('$.someInteger', byRegex(anInteger()))
|
||||
jsonPath('$.someDecimal', byRegex(aDouble()))
|
||||
jsonPath('$.someHex', byRegex('[a-fA-F0-9]+'))
|
||||
jsonPath('$.someAlphaNumeric', byRegex(alphaNumeric()))
|
||||
jsonPath('$.someUUID', byRegex(uuid()))
|
||||
jsonPath('$.someDate', byDate())
|
||||
jsonPath('$.someTime', byTime())
|
||||
jsonPath('$.someDateTime', byTimestamp())
|
||||
jsonPath('$.someBoolean', byRegex(anyBoolean()))
|
||||
}
|
||||
}
|
||||
response {
|
||||
status OK()
|
||||
body([
|
||||
someInteger: 1234567890,
|
||||
someDecimal: 123.123,
|
||||
someHex: 'DEADC0DE',
|
||||
someAlphaNumeric: 'Some alpha numeric string with 1234567890',
|
||||
someUUID: '00000000-0000-0000-0000-000000000000',
|
||||
someDate: '2018-03-26',
|
||||
someTime: '13:37:00',
|
||||
someDateTime: '2018-03-26 13:37:00',
|
||||
someBoolean: 'true',
|
||||
someRegex: 1234567890
|
||||
])
|
||||
headers {
|
||||
contentType('application/json')
|
||||
header("Some-Header", $(c('someValue'), p(regex('[a-zA-Z]{9}'))))
|
||||
header("Header-Without-Matcher", 'someValue')
|
||||
}
|
||||
bodyMatchers {
|
||||
jsonPath('$.someInteger', byRegex(anInteger()))
|
||||
jsonPath('$.someDecimal', byRegex(aDouble()))
|
||||
jsonPath('$.someHex', byRegex('[a-fA-F0-9]+'))
|
||||
jsonPath('$.someAlphaNumeric', byRegex(alphaNumeric()))
|
||||
jsonPath('$.someUUID', byRegex(uuid()))
|
||||
jsonPath('$.someDate', byDate())
|
||||
jsonPath('$.someTime', byTime())
|
||||
jsonPath('$.someDateTime', byTimestamp())
|
||||
jsonPath('$.someBoolean', byRegex(anyBoolean()))
|
||||
jsonPath('$.someNullValue', byNull())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,276 +0,0 @@
|
||||
{
|
||||
"provider": {
|
||||
"name": "Provider"
|
||||
},
|
||||
"consumer": {
|
||||
"name": "Consumer"
|
||||
},
|
||||
"interactions": [
|
||||
{
|
||||
"description": "",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"path": "/",
|
||||
"headers": {
|
||||
"Content-Type": "application/json",
|
||||
"Header-Without-Matcher": "someValue",
|
||||
"Some-Header": "someValue"
|
||||
},
|
||||
"body": {
|
||||
"someHex": "DEADC0DE",
|
||||
"someTime": "13:37:00",
|
||||
"someNullValue": null,
|
||||
"someInteger": 1234567890,
|
||||
"someBoolean": "true",
|
||||
"someDate": "2018-03-26",
|
||||
"someUUID": "00000000-0000-0000-0000-000000000000",
|
||||
"someDecimal": 123.123,
|
||||
"someAlphaNumeric": "Some alpha numeric string with 1234567890",
|
||||
"someDateTime": "2018-03-26 13:37:00"
|
||||
},
|
||||
"matchingRules": {
|
||||
"header": {
|
||||
"Content-Type": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "regex",
|
||||
"regex": "application/json.*"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"Some-Header": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "regex",
|
||||
"regex": "[a-zA-Z]{9}"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"$.someInteger": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "integer"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"$.someDecimal": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "decimal"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"$.someHex": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "regex",
|
||||
"regex": "[a-fA-F0-9]+"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"$.someAlphaNumeric": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "regex",
|
||||
"regex": "[a-zA-Z0-9]+"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"$.someUUID": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "regex",
|
||||
"regex": "[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"$.someDate": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "date",
|
||||
"date": "yyyy-MM-dd"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"$.someTime": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "time",
|
||||
"time": "HH:mm:ss"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"$.someDateTime": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "timestamp",
|
||||
"timestamp": "yyyy-MM-dd HH:mm:ssZZZ"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"$.someBoolean": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "regex",
|
||||
"regex": "(true|false)"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"headers": {
|
||||
"Content-Type": "application/json",
|
||||
"Header-Without-Matcher": "someValue",
|
||||
"Some-Header": "someValue"
|
||||
},
|
||||
"body": {
|
||||
"someHex": "DEADC0DE",
|
||||
"someTime": "13:37:00",
|
||||
"someInteger": 1234567890,
|
||||
"someBoolean": "true",
|
||||
"someRegex": 1234567890,
|
||||
"someDate": "2018-03-26",
|
||||
"someUUID": "00000000-0000-0000-0000-000000000000",
|
||||
"someDecimal": 123.123,
|
||||
"someAlphaNumeric": "Some alpha numeric string with 1234567890",
|
||||
"someDateTime": "2018-03-26 13:37:00"
|
||||
},
|
||||
"matchingRules": {
|
||||
"header": {
|
||||
"Content-Type": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "regex",
|
||||
"regex": "application/json.*"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"Some-Header": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "regex",
|
||||
"regex": "[a-zA-Z]{9}"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"$.someInteger": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "integer"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"$.someDecimal": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "decimal"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"$.someHex": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "regex",
|
||||
"regex": "[a-fA-F0-9]+"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"$.someAlphaNumeric": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "regex",
|
||||
"regex": "[a-zA-Z0-9]+"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"$.someUUID": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "regex",
|
||||
"regex": "[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"$.someDate": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "date",
|
||||
"date": "yyyy-MM-dd"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"$.someTime": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "time",
|
||||
"time": "HH:mm:ss"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"$.someDateTime": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "timestamp",
|
||||
"timestamp": "yyyy-MM-dd HH:mm:ssZZZ"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"$.someBoolean": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "regex",
|
||||
"regex": "(true|false)"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
},
|
||||
"$.someNullValue": {
|
||||
"matchers": [
|
||||
{
|
||||
"match": "null"
|
||||
}
|
||||
],
|
||||
"combine": "AND"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"pact-specification": {
|
||||
"version": "3.0.0"
|
||||
},
|
||||
"pact-jvm": {
|
||||
"version": "3.5.13"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user