diff --git a/README.adoc b/README.adoc index 36e6bb57a8..8bab5e7f92 100644 --- a/README.adoc +++ b/README.adoc @@ -1311,7 +1311,7 @@ Example of a `pom.xml` inside the `server` folder. UTF-8 1.8 - 1.0.2.BUILD-SNAPSHOT + 1.0.3.BUILD-SNAPSHOT Camden.BUILD-SNAPSHOT diff --git a/docs/src/main/asciidoc/verifier/stubrunner.adoc b/docs/src/main/asciidoc/verifier/stubrunner.adoc index 80cae8436e..0b60564416 100644 --- a/docs/src/main/asciidoc/verifier/stubrunner.adoc +++ b/docs/src/main/asciidoc/verifier/stubrunner.adoc @@ -66,6 +66,8 @@ Some of the properties that are repetitive can be set using system properties or |stubrunner.classifier|stubs| Default classifier for the stub artifacts |stubrunner.workOffline|false| If true then will not contact any remote repositories to download stubs |stubrunner.ids|| Array of Ivy notation stubs to download +|stubrunner.username|| Optional username to access the tool that stores the JARs with stubs +|stubrunner.password|| Optional password to access the tool that stores the JARs with stubs |====================== ===== Stub runner stubs ids @@ -80,7 +82,7 @@ groupId:artifactId:version:classifier:port `version`, `classifier` and `port` are optional. * If you don't provide the `port` then a random one will be picked -* If you don't provide the `classifier` then the default one will be taken. +* If you don't provide the `classifier` then the default one will be taken. (NOTE that you can pass an empty classifier like this `groupId:artifactId:version:`) * If you don't provide the `version` then the `+` will be passed and the latest one will be downloaded Where `port` means the port of the WireMock server. \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/README.adoc b/spring-cloud-contract-stub-runner/README.adoc index 23d117147d..611a3c2290 100644 --- a/spring-cloud-contract-stub-runner/README.adoc +++ b/spring-cloud-contract-stub-runner/README.adoc @@ -14,24 +14,35 @@ You can set the following options to the main class: [source,groovy,indent=0] ---- - -maxp (--maxPort) N : Maximum port value to be assigned to the - Wiremock instance. Defaults to 15000 - (default: 15000) - -minp (--minPort) N : Minimal port value to be assigned to the - Wiremock instance. Defaults to 10000 - (default: 10000) - -s (--stubs) VAL : Comma separated list of Ivy representation of - jars with stubs. Eg. groupid:artifactid1,group - id2:artifactid2:version:classifier - -sr (--stubRepositoryRoot) VAL : Location of a Jar containing server where you - keep your stubs (e.g. http://nexus.net/content - /repositories/repository) - -ss (--stubsSuffix) VAL : Suffix for the jar containing stubs (e.g. - 'stubs' if the stub jar would have a 'stubs' - classifier for stubs: foobar-stubs ). - Defaults to 'stubs' (default: stubs) - -wo (--workOffline) : Switch to work offline. Defaults to 'false' - (default: false) +-c, --classifier Suffix for the jar containing stubs (e. + g. 'stubs' if the stub jar would + have a 'stubs' classifier for stubs: + foobar-stubs ). Defaults to 'stubs' + (default: stubs) +--maxPort, --maxp Maximum port value to be assigned to + the WireMock instance. Defaults to + 15000 (default: 15000) +--minPort, --minp Minimum port value to be assigned to + the WireMock instance. Defaults to + 10000 (default: 10000) +-p, --password Password to user when connecting to + repository +--phost, --proxyHost Proxy host to use for repository + requests +--pport, --proxyPort [Integer] Proxy port to use for repository + requests +-r, --root Location of a Jar containing server + where you keep your stubs (e.g. http: + //nexus. + net/content/repositories/repository) +-s, --stubs Comma separated list of Ivy + representation of jars with stubs. + Eg. groupid:artifactid1,groupid2: + artifactid2:classifier +-u, --username Username to user when connecting to + repository +--wo, --workOffline Switch to work offline. Defaults to + 'false' ---- ===== HTTP Stubs diff --git a/spring-cloud-contract-stub-runner/pom.xml b/spring-cloud-contract-stub-runner/pom.xml index cd02deaa41..420a36c6dd 100644 --- a/spring-cloud-contract-stub-runner/pom.xml +++ b/spring-cloud-contract-stub-runner/pom.xml @@ -94,6 +94,12 @@ junit junit + + io.specto + hoverfly-junit + 0.1.8 + test + com.sun.jersey jersey-client diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherFactories.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherFactories.java index 709ed9db7e..d45fd40861 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherFactories.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherFactories.java @@ -16,9 +16,6 @@ package org.springframework.cloud.contract.stubrunner; -import java.util.ArrayList; -import java.util.List; - import org.apache.maven.repository.internal.MavenRepositorySystemUtils; import org.eclipse.aether.DefaultRepositorySystemSession; import org.eclipse.aether.RepositorySystem; @@ -26,13 +23,11 @@ import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory; import org.eclipse.aether.impl.DefaultServiceLocator; import org.eclipse.aether.repository.LocalRepository; -import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.repository.RepositoryPolicy; import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; import org.eclipse.aether.spi.connector.transport.TransporterFactory; import org.eclipse.aether.transport.file.FileTransporterFactory; import org.eclipse.aether.transport.http.HttpTransporterFactory; -import org.springframework.util.StringUtils; class AetherFactories { @@ -52,22 +47,12 @@ class AetherFactories { if (!workOffline) { session.setUpdatePolicy(RepositoryPolicy.UPDATE_POLICY_ALWAYS); } + session.setChecksumPolicy(RepositoryPolicy.CHECKSUM_POLICY_WARN); LocalRepository localRepo = new LocalRepository(localRepositoryDirectory()); session.setLocalRepositoryManager(system.newLocalRepositoryManager(session, localRepo)); return session; } - public static List newRepositories(List repositories) { - List result = new ArrayList<>(); - for (int index = 0; index < repositories.size(); index++) { - String repo = repositories.get(index); - if (StringUtils.hasText(repo)) { - result.add(new RemoteRepository.Builder("remote" + index, "default", repo).build()); - } - } - return result; - } - private static String localRepositoryDirectory() { return System.getProperty(MAVEN_LOCAL_REPOSITORY_LOCATION, System.getProperty("user.home") + "/.m2/repository"); } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherStubDownloader.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherStubDownloader.java index c62b6bc035..51045307ed 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherStubDownloader.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/AetherStubDownloader.java @@ -21,7 +21,6 @@ import java.io.IOException; import java.net.URI; import java.util.AbstractMap; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Map; @@ -29,6 +28,7 @@ import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.repository.Proxy; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.resolution.ArtifactRequest; import org.eclipse.aether.resolution.ArtifactResult; @@ -38,12 +38,13 @@ import org.eclipse.aether.resolution.VersionRangeResult; import org.eclipse.aether.resolution.VersionRequest; import org.eclipse.aether.resolution.VersionResolutionException; import org.eclipse.aether.resolution.VersionResult; +import org.eclipse.aether.util.repository.AuthenticationBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.cloud.contract.stubrunner.StubRunnerOptions.StubRunnerProxyOptions; import org.springframework.util.StringUtils; import static java.nio.file.Files.createTempDirectory; -import static org.springframework.cloud.contract.stubrunner.AetherFactories.newRepositories; import static org.springframework.cloud.contract.stubrunner.AetherFactories.newRepositorySystem; import static org.springframework.cloud.contract.stubrunner.AetherFactories.newSession; import static org.springframework.cloud.contract.stubrunner.util.ZipCategory.unzipTo; @@ -106,13 +107,27 @@ public class AetherStubDownloader implements StubDownloader { } } - private List remoteRepositories( - StubRunnerOptions stubRunnerOptions) { + private List remoteRepositories(StubRunnerOptions stubRunnerOptions) { if (stubRunnerOptions.stubRepositoryRoot == null) { return new ArrayList<>(); } - List remoteRepos = newRepositories( - Arrays.asList(stubRunnerOptions.stubRepositoryRoot.split(","))); + final String[] repos = stubRunnerOptions.stubRepositoryRoot.split(","); + final List remoteRepos = new ArrayList<>(); + for (int i = 0; i < repos.length; i++) { + if(StringUtils.hasText(repos[i])) { + final RemoteRepository.Builder builder = new RemoteRepository.Builder("remote" + i, "default", repos[i]) + .setAuthentication(new AuthenticationBuilder() + .addUsername(stubRunnerOptions.username) + .addPassword(stubRunnerOptions.password) + .build()); + if(stubRunnerOptions.getProxyOptions() != null) { + final StubRunnerProxyOptions p = stubRunnerOptions.getProxyOptions(); + builder.setProxy(new Proxy(null, p.getProxyHost(), p.getProxyPort())); + } + + remoteRepos.add(builder.build()); + } + } if (log.isDebugEnabled()) { log.debug("Using the following remote repos " + remoteRepos); } @@ -165,8 +180,7 @@ public class AetherStubDownloader implements StubDownloader { } @Override - public Map.Entry downloadAndUnpackStubJar( - StubRunnerOptions options, StubConfiguration stubConfiguration) { + public Map.Entry downloadAndUnpackStubJar(StubConfiguration stubConfiguration) { String version = getVersion(stubConfiguration.groupId, stubConfiguration.artifactId, stubConfiguration.version, stubConfiguration.classifier); diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/Arguments.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/Arguments.java index 4f61887b46..8878053ee0 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/Arguments.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/Arguments.java @@ -48,4 +48,9 @@ class Arguments { public StubConfiguration getStub() { return this.stub; } + + @Override public String toString() { + return "Arguments{" + "stubRunnerOptions=" + this.stubRunnerOptions + + ", repositoryPath='" + this.repositoryPath + '\'' + ", stub=" + this.stub + '}'; + } } \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ContractDownloader.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ContractDownloader.java index c2fda66ba9..2001b75d1f 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ContractDownloader.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/ContractDownloader.java @@ -91,7 +91,7 @@ public class ContractDownloader { private File unpackAndDownloadContracts() { log.info("Will download contracts for [" + this.contractsJarStubConfiguration + "]"); Map.Entry unpackedContractStubs = this.stubDownloader - .downloadAndUnpackStubJar(null, this.contractsJarStubConfiguration); + .downloadAndUnpackStubJar(this.contractsJarStubConfiguration); if (unpackedContractStubs == null) { throw new IllegalStateException("The contracts failed to be downloaded!"); } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubDownloader.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubDownloader.java index 725a1827a8..00ea82a126 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubDownloader.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubDownloader.java @@ -25,5 +25,5 @@ public interface StubDownloader { * Returns a mapping of updated StubConfiguration (it will contain the resolved version) and the location of the downloaded JAR. * If there was no artifact this method will return {@code null}. */ - Map.Entry downloadAndUnpackStubJar(StubRunnerOptions options, StubConfiguration stubConfiguration); + Map.Entry downloadAndUnpackStubJar(StubConfiguration stubConfiguration); } \ No newline at end of file diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerFactory.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerFactory.java index 1a2c5187ac..972d4ece2b 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerFactory.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerFactory.java @@ -57,7 +57,7 @@ class StubRunnerFactory { Collection result = new ArrayList<>(); for (StubConfiguration stubsConfiguration : this.stubRunnerOptions.getDependencies()) { Map.Entry entry = this.stubDownloader - .downloadAndUnpackStubJar(this.stubRunnerOptions, stubsConfiguration); + .downloadAndUnpackStubJar(stubsConfiguration); if (log.isDebugEnabled()) { log.debug("For stub configuration [" + stubsConfiguration + "] the downloaded entry is [" + entry + "]"); } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerMain.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerMain.java index 77a01b90e9..e55b915950 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerMain.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerMain.java @@ -38,11 +38,11 @@ public class StubRunnerMain { ArgumentAcceptingOptionSpec minPortValueOpt = parser .acceptsAll(Arrays.asList("minp", "minPort"), "Minimum port value to be assigned to the WireMock instance. Defaults to 10000") - .withRequiredArg().ofType(Integer.class); + .withRequiredArg().ofType(Integer.class).defaultsTo(10000); ArgumentAcceptingOptionSpec maxPortValueOpt = parser .acceptsAll(Arrays.asList("maxp", "maxPort"), "Maximum port value to be assigned to the WireMock instance. Defaults to 15000") - .withRequiredArg().ofType(Integer.class); + .withRequiredArg().ofType(Integer.class).defaultsTo(15000); ArgumentAcceptingOptionSpec stubsOpt = parser .acceptsAll(Arrays.asList("s", "stubs"), "Comma separated list of Ivy representation of jars with stubs. Eg. groupid:artifactid1,groupid2:artifactid2:classifier") @@ -54,7 +54,19 @@ public class StubRunnerMain { ArgumentAcceptingOptionSpec rootOpt = parser .acceptsAll(Arrays.asList("r", "root"),"Location of a Jar containing server where you keep your stubs (e.g. http://nexus.net/content/repositories/repository)") .withRequiredArg(); - + ArgumentAcceptingOptionSpec usernameOpt = parser + .acceptsAll(Arrays.asList("u", "username"),"Username to user when connecting to repository") + .withOptionalArg(); + ArgumentAcceptingOptionSpec passwordOpt = parser + .acceptsAll(Arrays.asList("p", "password"),"Password to user when connecting to repository") + .withOptionalArg(); + ArgumentAcceptingOptionSpec proxyHostOpt = parser + .acceptsAll(Arrays.asList("phost", "proxyHost"),"Proxy host to use for repository requests") + .withOptionalArg(); + ArgumentAcceptingOptionSpec proxyPortOpt = parser + .acceptsAll(Arrays.asList("pport", "proxyPort"),"Proxy port to use for repository requests") + .withOptionalArg() + .ofType(Integer.class); parser.acceptsAll(Arrays.asList("wo", "workOffline"), "Switch to work offline. Defaults to 'false'"); OptionSet options = parser.parse(args); @@ -64,11 +76,21 @@ public class StubRunnerMain { Integer maxPortValue = options.valueOf(maxPortValueOpt); String stubRepositoryRoot= options.valueOf(rootOpt); String stubsSuffix = options.valueOf(classifierOpt); - StubRunnerOptions stubRunnerOptions = new StubRunnerOptionsBuilder() + final String username = options.valueOf(usernameOpt); + final String password = options.valueOf(passwordOpt); + final String proxyHost = options.valueOf(proxyHostOpt); + final Integer proxyPort = options.valueOf(proxyPortOpt); + final StubRunnerOptionsBuilder builder = new StubRunnerOptionsBuilder() .withMinMaxPort(minPortValue, maxPortValue) .withStubRepositoryRoot(stubRepositoryRoot) .withWorkOffline(workOffline).withStubsClassifier(stubsSuffix) - .withStubs(stubs).build(); + .withUsername(username) + .withPassword(password) + .withStubs(stubs); + if (proxyHost != null) { + builder.withProxy(proxyHost, proxyPort); + } + StubRunnerOptions stubRunnerOptions = builder.build(); this.arguments = new Arguments(stubRunnerOptions); } catch (Exception e) { diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.java index 4f1e740112..957880d31f 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptions.java @@ -60,9 +60,26 @@ public class StubRunnerOptions { */ final Map stubIdsToPortMapping; - public StubRunnerOptions(Integer minPortValue, Integer maxPortValue, String stubRepositoryRoot, boolean workOffline, - String stubsClassifier, Collection dependencies, - Map stubIdsToPortMapping) { + /** + * Optional username for authorization header + */ + final String username; + + /** + * Optional password for authorization header + */ + final String password; + + /** + * Optional proxy settings + */ + private final StubRunnerProxyOptions stubRunnerProxyOptions; + + StubRunnerOptions(Integer minPortValue, Integer maxPortValue, + String stubRepositoryRoot, boolean workOffline, String stubsClassifier, + Collection dependencies, + Map stubIdsToPortMapping, + String username, String password, final StubRunnerProxyOptions stubRunnerProxyOptions) { this.minPortValue = minPortValue; this.maxPortValue = maxPortValue; this.stubRepositoryRoot = stubRepositoryRoot; @@ -70,17 +87,9 @@ public class StubRunnerOptions { this.stubsClassifier = stubsClassifier; this.dependencies = dependencies; this.stubIdsToPortMapping = stubIdsToPortMapping; - } - - /** - * @deprecated there is no context path any longer - */ - @Deprecated - public StubRunnerOptions(Integer minPortValue, Integer maxPortValue, String stubRepositoryRoot, boolean workOffline, - String stubsClassifier, Collection dependencies, - Map stubIdsToPortMapping, String contextPath) { - this(minPortValue, maxPortValue, stubRepositoryRoot, workOffline, stubsClassifier, dependencies, - stubIdsToPortMapping); + this.username = username; + this.password = password; + this.stubRunnerProxyOptions = stubRunnerProxyOptions; } public Integer port(StubConfiguration stubConfiguration) { @@ -108,12 +117,41 @@ public class StubRunnerOptions { return this.stubIdsToPortMapping; } - @Override - public String toString() { - return "StubRunnerOptions [minPortValue=" + this.minPortValue + ", maxPortValue=" + this.maxPortValue - + ", stubRepositoryRoot=" + this.stubRepositoryRoot + ", workOffline=" + this.workOffline - + ", stubsClassifier=" + this.stubsClassifier + ", dependencies=" + this.dependencies - + ", stubIdsToPortMapping=" + this.stubIdsToPortMapping + "]"; + public StubRunnerProxyOptions getProxyOptions() { + return this.stubRunnerProxyOptions; } + public static class StubRunnerProxyOptions { + + private final String proxyHost; + private final int proxyPort; + + public StubRunnerProxyOptions(final String proxyHost, final int proxyPort) { + this.proxyHost = proxyHost; + this.proxyPort = proxyPort; + } + + public String getProxyHost() { + return this.proxyHost; + } + + public int getProxyPort() { + return this.proxyPort; + } + + @Override public String toString() { + return "StubRunnerProxyOptions{" + "proxyHost='" + this.proxyHost + '\'' + + ", proxyPort=" + this.proxyPort + '}'; + } + } + + @Override public String toString() { + return "StubRunnerOptions{" + "minPortValue=" + this.minPortValue + ", maxPortValue=" + + this.maxPortValue + ", stubRepositoryRoot='" + this.stubRepositoryRoot + '\'' + + ", workOffline=" + this.workOffline + ", stubsClassifier='" + this.stubsClassifier + + '\'' + ", dependencies=" + this.dependencies + ", stubIdsToPortMapping=" + + this.stubIdsToPortMapping + ", username='" + this.username + '\'' + ", password='" + + this.password + '\'' + ", stubRunnerProxyOptions=" + this.stubRunnerProxyOptions + + '}'; + } } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.java index 39e67e8f5a..b08b26ad1f 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRunnerOptionsBuilder.java @@ -16,6 +16,9 @@ package org.springframework.cloud.contract.stubrunner; +import org.springframework.cloud.contract.stubrunner.util.StubsParser; +import org.springframework.util.StringUtils; + import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; @@ -23,9 +26,6 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import org.springframework.cloud.contract.stubrunner.util.StubsParser; -import org.springframework.util.StringUtils; - public class StubRunnerOptionsBuilder { private static final String DELIMITER = ":"; @@ -37,6 +37,9 @@ public class StubRunnerOptionsBuilder { private String stubRepositoryRoot; private boolean workOffline = false; private String stubsClassifier = "stubs"; + private String username; + private String password; + private StubRunnerOptions.StubRunnerProxyOptions stubRunnerProxyOptions; public StubRunnerOptionsBuilder() { } @@ -57,8 +60,7 @@ public class StubRunnerOptionsBuilder { return this; } - public StubRunnerOptionsBuilder withMinMaxPort(Integer minPortValue, - Integer maxPortValue) { + public StubRunnerOptionsBuilder withMinMaxPort(Integer minPortValue, Integer maxPortValue) { this.minPortValue = minPortValue; this.maxPortValue = maxPortValue; return this; @@ -114,7 +116,8 @@ public class StubRunnerOptionsBuilder { public StubRunnerOptions build() { return new StubRunnerOptions(this.minPortValue, this.maxPortValue, this.stubRepositoryRoot, - this.workOffline, this.stubsClassifier, buildDependencies(), this.stubIdsToPortMapping); + this.workOffline, this.stubsClassifier, buildDependencies(), this.stubIdsToPortMapping, + this.username, this.password, this.stubRunnerProxyOptions); } private Collection buildDependencies() { @@ -139,8 +142,7 @@ public class StubRunnerOptionsBuilder { if (StubsParser.hasPort(notation)) { addPort(notation); this.stubs.add(StubsParser.ivyFromStringWithPort(notation)); - } - else { + } else { this.stubs.add(notation); } } @@ -154,4 +156,18 @@ public class StubRunnerOptionsBuilder { this.stubIdsToPortMapping.putAll(stubIdsToPortMapping); } + public StubRunnerOptionsBuilder withUsername(final String username) { + this.username = username; + return this; + } + + public StubRunnerOptionsBuilder withPassword(final String password) { + this.password = password; + return this; + } + + public StubRunnerOptionsBuilder withProxy(final String proxyHost, final int proxyPort) { + this.stubRunnerProxyOptions = new StubRunnerOptions.StubRunnerProxyOptions(proxyHost, proxyPort); + return this; + } } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRule.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRule.java index f4665a93b5..229b221edf 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRule.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/junit/StubRunnerRule.java @@ -65,14 +65,20 @@ public class StubRunnerRule implements TestRule, StubFinder { } private StubRunnerOptions defaultStubRunnerOptions() { - return new StubRunnerOptionsBuilder() + StubRunnerOptionsBuilder builder = new StubRunnerOptionsBuilder() .withMinPort(Integer.valueOf(System.getProperty("stubrunner.port.range.min", "10000"))) .withMaxPort(Integer.valueOf(System.getProperty("stubrunner.port.range.max", "15000"))) .withStubRepositoryRoot(System.getProperty("stubrunner.repository.root", "")) .withWorkOffline(Boolean.parseBoolean(System.getProperty("stubrunner.work-offline", "false"))) .withStubsClassifier(System.getProperty("stubrunner.classifier", "stubs")) .withStubs(System.getProperty("stubrunner.ids", "")) - .build(); + .withUsername(System.getProperty("stubrunner.username")) + .withPassword(System.getProperty("stubrunner.password")); + String proxyHost = System.getProperty("stubrunner.proxy.host"); + if (proxyHost != null) { + builder.withProxy(proxyHost, Integer.parseInt(System.getProperty("stubrunner.proxy.port"))); + } + return builder.build(); } /** diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfiguration.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfiguration.java index 2eb4b1cd3a..0f240e4c21 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfiguration.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerConfiguration.java @@ -67,14 +67,11 @@ public class StubRunnerConfiguration { */ @Bean public BatchStubRunner batchStubRunner() throws IOException { - StubRunnerOptions stubRunnerOptions = new StubRunnerOptionsBuilder() - .withMinMaxPort(this.props.getMinPort(), this.props.getMaxPort()) - .withStubRepositoryRoot( - uriStringOrEmpty(this.props.getRepositoryRoot())) - .withWorkOffline(this.props.isWorkOffline()) - .withStubsClassifier(this.props.getClassifier()) - .withStubs(this.props.getIds()) - .build(); + StubRunnerOptionsBuilder builder = builder(); + if (this.props.getProxyHost() != null) { + builder.withProxy(this.props.getProxyHost(), this.props.getProxyPort()); + } + StubRunnerOptions stubRunnerOptions = builder.build(); BatchStubRunner batchStubRunner = new BatchStubRunnerFactory(stubRunnerOptions, this.stubDownloader != null ? this.stubDownloader : new AetherStubDownloader(stubRunnerOptions), @@ -86,6 +83,18 @@ public class StubRunnerConfiguration { return batchStubRunner; } + private StubRunnerOptionsBuilder builder() throws IOException { + return new StubRunnerOptionsBuilder() + .withMinMaxPort(this.props.getMinPort(), this.props.getMaxPort()) + .withStubRepositoryRoot( + uriStringOrEmpty(this.props.getRepositoryRoot())) + .withWorkOffline(this.props.isWorkOffline()) + .withStubsClassifier(this.props.getClassifier()) + .withStubs(this.props.getIds()) + .withUsername(this.props.getUsername()) + .withPassword(this.props.getPassword()); + } + private String uriStringOrEmpty(Resource stubRepositoryRoot) throws IOException { return stubRepositoryRoot != null ? stubRepositoryRoot.getURI().toString() : ""; } diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerProperties.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerProperties.java index 27ab22716f..651bdf6680 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerProperties.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/spring/StubRunnerProperties.java @@ -65,6 +65,26 @@ public class StubRunnerProperties { */ private String contextPath = ""; + /** + * Repository username + */ + private String username; + + /** + * Repository password + */ + private String password; + + /** + * Repository proxy port + */ + private Integer proxyPort; + + /** + * Repository proxy host + */ + private String proxyHost; + public int getMinPort() { return this.minPort; } @@ -97,6 +117,38 @@ public class StubRunnerProperties { this.repositoryRoot = new DefaultResourceLoader().getResource(repositoryRoot); } + public String getUsername() { + return this.username; + } + + public void setUsername(final String username) { + this.username = username; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(final String password) { + this.password = password; + } + + public Integer getProxyPort() { + return this.proxyPort; + } + + public void setProxyPort(final Integer proxyPort) { + this.proxyPort = proxyPort; + } + + public String getProxyHost() { + return this.proxyHost; + } + + public void setProxyHost(final String proxyHost) { + this.proxyHost = proxyHost; + } + public String[] getIds() { return this.ids; } diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/AetherStubDownloaderSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/AetherStubDownloaderSpec.groovy new file mode 100644 index 0000000000..5001a9f792 --- /dev/null +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/AetherStubDownloaderSpec.groovy @@ -0,0 +1,30 @@ +package org.springframework.cloud.contract.stubrunner + +import io.specto.hoverfly.junit.HoverflyRule +import org.junit.Rule + +import spock.lang.Specification + +class AetherStubDownloaderSpec extends Specification { + + @Rule + HoverflyRule hoverflyRule = HoverflyRule.buildFromClassPathResource("simulation.json").build() + + def 'Should be able to download from a repository using username and password authentication'() { + given: + final StubRunnerOptions stubRunnerOptions = new StubRunnerOptionsBuilder() + .withUsername("andrew.morgan") + .withPassword("k+hbZp8rpolRucXB09dGE/CxPXxidQryQUYSGbeo6JE=") + .withProxy("localhost", hoverflyRule.proxyPort) + .withStubRepositoryRoot("https://test.jfrog.io/test/libs-snapshot-local") + .build() + + final AetherStubDownloader aetherStubDownloader = new AetherStubDownloader(stubRunnerOptions) + + when: + final def jar = aetherStubDownloader.downloadAndUnpackStubJar(new StubConfiguration("io.test", "test-simulations-svc", "1.0-SNAPSHOT")) + + then: + jar != null + } +} diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/ContractDownloaderSpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/ContractDownloaderSpec.groovy index b715e5062b..e88b7df793 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/ContractDownloaderSpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/ContractDownloaderSpec.groovy @@ -19,7 +19,7 @@ class ContractDownloaderSpec extends Specification { stubConfiguration, contractPath, '', '') ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties() and: - stubDownloader.downloadAndUnpackStubJar(_, _) >> new AbstractMap.SimpleEntry(stubConfiguration, file) + stubDownloader.downloadAndUnpackStubJar(_) >> new AbstractMap.SimpleEntry(stubConfiguration, file) when: contractDownloader.unpackedDownloadedContracts(properties) then: @@ -35,7 +35,7 @@ class ContractDownloaderSpec extends Specification { stubConfiguration, contractPath, '', '') ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties() and: - stubDownloader.downloadAndUnpackStubJar(_, _) >> new AbstractMap.SimpleEntry(stubConfiguration, file) + stubDownloader.downloadAndUnpackStubJar(_) >> new AbstractMap.SimpleEntry(stubConfiguration, file) when: contractDownloader.unpackedDownloadedContracts(properties) then: diff --git a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerFactorySpec.groovy b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerFactorySpec.groovy index 24776bcf00..cec3e255e9 100644 --- a/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerFactorySpec.groovy +++ b/spring-cloud-contract-stub-runner/src/test/groovy/org/springframework/cloud/contract/stubrunner/StubRunnerFactorySpec.groovy @@ -42,8 +42,8 @@ class StubRunnerFactorySpec extends Specification { def "Should download stub definitions many times"() { given: folder.newFolder("mappings") - 1 * downloader.downloadAndUnpackStubJar(_, _) >> new AbstractMap.SimpleEntry(new StubConfiguration('a:b'), folder.root) - 1 * downloader.downloadAndUnpackStubJar(_, _) >> new AbstractMap.SimpleEntry(new StubConfiguration('c:d'), folder.root) + 1 * downloader.downloadAndUnpackStubJar(_) >> new AbstractMap.SimpleEntry(new StubConfiguration('a:b'), folder.root) + 1 * downloader.downloadAndUnpackStubJar(_) >> new AbstractMap.SimpleEntry(new StubConfiguration('c:d'), folder.root) when: Collection stubRunners = collectOnlyPresentValues(factory.createStubsFromServiceConfiguration()) then: diff --git a/spring-cloud-contract-stub-runner/src/test/resources/simulation.json b/spring-cloud-contract-stub-runner/src/test/resources/simulation.json new file mode 100644 index 0000000000..20d8c8239c --- /dev/null +++ b/spring-cloud-contract-stub-runner/src/test/resources/simulation.json @@ -0,0 +1,313 @@ +{ + "data": [ + { + "response": { + "status": 200, + "body": "\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\n\u003cmetadata\u003e\n \u003cgroupId\u003eio.test\u003c/groupId\u003e\n \u003cartifactId\u003etest-simulations-svc\u003c/artifactId\u003e\n \u003cversion\u003e1.0-SNAPSHOT\u003c/version\u003e\n \u003cversioning\u003e\n \u003csnapshot\u003e\n \u003cbuildNumber\u003e1\u003c/buildNumber\u003e\n \u003c/snapshot\u003e\n \u003clastUpdated\u003e20161102142105\u003c/lastUpdated\u003e\n \u003c/versioning\u003e\n\u003c/metadata\u003e\n", + "encodedBody": false, + "headers": { + "Accept-Ranges": [ + "bytes" + ], + "Cache-Control": [ + "no-store" + ], + "Connection": [ + "keep-alive" + ], + "Content-Disposition": [ + "attachment; filename=\"maven-metadata.xml\"; filename*=UTF-8''maven-metadata.xml" + ], + "Content-Length": [ + "320" + ], + "Content-Type": [ + "application/xml" + ], + "Date": [ + "Tue, 08 Nov 2016 15:22:26 GMT" + ], + "Etag": [ + "9a318b6f330aaf4b7088c3018e31dd4bb11b18f1" + ], + "Hoverfly": [ + "Was-Here" + ], + "Last-Modified": [ + "Wed, 02 Nov 2016 14:21:05 GMT" + ], + "Server": [ + "Artifactory/4.14.0" + ], + "X-Artifactory-Filename": [ + "maven-metadata.xml" + ], + "X-Artifactory-Id": [ + "aolprod1b-test" + ], + "X-Checksum-Md5": [ + "baa7a56ac80111c29099f9076e6a136f" + ], + "X-Checksum-Sha1": [ + "9a318b6f330aaf4b7088c3018e31dd4bb11b18f1" + ], + "X-Node": [ + "nginx-shared2b" + ] + } + }, + "request": { + "requestType": "template", + "path": "/test/libs-snapshot-local/io/test/test-simulations-svc/1.0-SNAPSHOT/maven-metadata.xml", + "method": "GET", + "destination": "test.jfrog.io", + "scheme": "https", + "query": "", + "body": "", + "headers": { + "Authorization": [ + "Basic dXNlcm5hbWU6cGFzc3dvcmQK==" + ] + } + } + }, + { + "response": { + "status": 401 + }, + "request": { + "requestType": "template", + "path": "/test/libs-snapshot-local/io/test/test-simulations-svc/1.0-SNAPSHOT/maven-metadata.xml", + "method": "GET", + "destination": "test.jfrog.io", + "scheme": "https", + "query": "", + "body": "" + } + }, + { + "response": { + "status": 401 + }, + "request": { + "requestType": "template", + "path": "/test/libs-snapshot-local/io/test/test-simulations-svc/1.0-SNAPSHOT/test-simulations-svc-1.0-SNAPSHOT-stubs.jar.sha1", + "method": "GET", + "destination": "test.jfrog.io", + "scheme": "https", + "query": "", + "body": "" + } + }, + { + "response": { + "status": 401 + }, + "request": { + "requestType": "template", + "path": "/test/libs-snapshot-local/io/test/test-simulations-svc/1.0-SNAPSHOT/test-simulations-svc-1.0-SNAPSHOT-stubs.jar.md5", + "method": "GET", + "destination": "test.jfrog.io", + "scheme": "https", + "query": "", + "body": "" + } + }, + { + "response": { + "status": 200, + "body": "9a318b6f330aaf4b7088c3018e31dd4bb11b18f1", + "encodedBody": false, + "headers": { + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "40" + ], + "Content-Type": [ + "application/x-checksum" + ], + "Date": [ + "Tue, 08 Nov 2016 15:22:26 GMT" + ], + "Hoverfly": [ + "Was-Here" + ], + "Last-Modified": [ + "Tue, 08 Nov 2016 15:22:26 GMT" + ], + "Server": [ + "Artifactory/4.14.0" + ], + "X-Artifactory-Id": [ + "aolprod1b-test" + ], + "X-Node": [ + "nginx-shared2b" + ] + } + }, + "request": { + "requestType": "template", + "path": "/test/libs-snapshot-local/io/test/test-simulations-svc/1.0-SNAPSHOT/maven-metadata.xml.sha1", + "method": "GET", + "destination": "test.jfrog.io", + "scheme": "https", + "query": "", + "body": "", + "headers": { + "Accept-Encoding": [ + "gzip,deflate" + ], + "Authorization": [ + "Basic dXNlcm5hbWU6cGFzc3dvcmQK==" + ], + "Cache-Control": [ + "no-cache, no-store" + ], + "Connection": [ + "Keep-Alive" + ], + "Pragma": [ + "no-cache" + ], + "User-Agent": [ + "Aether" + ] + } + } + }, + { + "response": { + "status": 200, + "body": "UEsDBAoAAAgIAJxyYkkAAAAAAgAAAAAAAAAJAAAATUVUQS1JTkYvAwBQSwMECgAACAgAnHJiSbJ/Au4bAAAAGQAAABQAAABNRVRBLUlORi9NQU5JRkVTVC5NRvNNzMtMSy0u0Q1LLSrOzM+zUjDUM+Dl4uUCAFBLAwQKAAAICACccmJJAAAAAAIAAAAAAAAACgAAAGNvbnRyYWN0cy8DAFBLAwQKAAAICACccmJJkcxXfpgAAADnAAAALwAAAGNvbnRyYWN0cy9zaG91bGRCZUFibGVUb0NyZWF0ZVNpbXVsYXRpb24uZ3Jvb3Z5VY1BDsIgFET3nIIdbUxo9QpewIUe4Id+kRQE/4dqYnp30bRGZzdvJjMJzAgWpYnXTGAyCxHJak7krvZMEPAeadTGxzLotVRjNHq/ugAjyqeQVYS3gpwX91bAfImDVIfTUX1hIS8n8AWbOsklIDWEFh+N6tiF4iG7yjtIrpu2f0hvVNu2n6F5eeRUA/y55Ay5sNz1/dKbxQtQSwMECgAACAgAnHJiSQAAAAACAAAAAAAAAAkAAABtYXBwaW5ncy8DAFBLAwQKAAAICACccmJJT/HGJIsAAADBAAAALAAAAG1hcHBpbmdzL3Nob3VsZEJlQWJsZVRvQ3JlYXRlU2ltdWxhdGlvbi5qc29uVY07DsIwEET7nMJyC8ZOlI/gFCngAGtlIyzl613TRLk7dqCAcp7mzWyZEDIE10lxExLLyti+QdUAWFX2plC2rq3CCK9VZ9DmvTwnxeMakDhZW8xpxA8tMKOfjilNbgwDsJsn0rA4/cr/0OV0DEVxRH7On//2cZcR7t8LWmITfz6IgQMlUBiTitn+BlBLAQIUAwoAAAgIAJxyYkkAAAAAAgAAAAAAAAAJAAAAAAAAAAAAEADtQQAAAABNRVRBLUlORi9QSwECFAMKAAAICACccmJJsn8C7hsAAAAZAAAAFAAAAAAAAAAAAAAApIEpAAAATUVUQS1JTkYvTUFOSUZFU1QuTUZQSwECFAMKAAAICACccmJJAAAAAAIAAAAAAAAACgAAAAAAAAAAABAA/UF2AAAAY29udHJhY3RzL1BLAQIUAwoAAAgIAJxyYkmRzFd+mAAAAOcAAAAvAAAAAAAAAAAAAAC0gaAAAABjb250cmFjdHMvc2hvdWxkQmVBYmxlVG9DcmVhdGVTaW11bGF0aW9uLmdyb292eVBLAQIUAwoAAAgIAJxyYkkAAAAAAgAAAAAAAAAJAAAAAAAAAAAAEAD9QYUBAABtYXBwaW5ncy9QSwECFAMKAAAICACccmJJT/HGJIsAAADBAAAALAAAAAAAAAAAAAAAtIGuAQAAbWFwcGluZ3Mvc2hvdWxkQmVBYmxlVG9DcmVhdGVTaW11bGF0aW9uLmpzb25QSwUGAAAAAAYABgCfAQAAgwIAAAAA", + "encodedBody": true, + "headers": { + "Accept-Ranges": [ + "bytes" + ], + "Cache-Control": [ + "no-store" + ], + "Connection": [ + "keep-alive" + ], + "Content-Disposition": [ + "attachment; filename=\"test-simulations-svc-1.0-SNAPSHOT-stubs.jar\"; filename*=UTF-8''test-simulations-svc-1.0-SNAPSHOT-stubs.jar" + ], + "Content-Length": [ + "1080" + ], + "Content-Type": [ + "application/java-archive" + ], + "Date": [ + "Tue, 08 Nov 2016 15:22:26 GMT" + ], + "Etag": [ + "6c40475c58db6745b860e7b3f0ffa1c87faa83ce" + ], + "Hoverfly": [ + "Was-Here" + ], + "Last-Modified": [ + "Wed, 02 Nov 2016 14:21:06 GMT" + ], + "Server": [ + "Artifactory/4.14.0" + ], + "X-Artifactory-Filename": [ + "test-simulations-svc-1.0-SNAPSHOT-stubs.jar" + ], + "X-Artifactory-Id": [ + "aolprod1b-test" + ], + "X-Checksum-Md5": [ + "0b8c9e9880654871beaf1cf40cd07aaf" + ], + "X-Checksum-Sha1": [ + "6c40475c58db6745b860e7b3f0ffa1c87faa83ce" + ], + "X-Node": [ + "nginx-shared2b" + ] + } + }, + "request": { + "requestType": "template", + "path": "/test/libs-snapshot-local/io/test/test-simulations-svc/1.0-SNAPSHOT/test-simulations-svc-1.0-SNAPSHOT-stubs.jar", + "method": "GET", + "destination": "test.jfrog.io", + "scheme": "https", + "query": "", + "body": "" + } + }, + { + "response": { + "status": 200, + "body": "6c40475c58db6745b860e7b3f0ffa1c87faa83ce", + "encodedBody": false, + "headers": { + "Connection": [ + "keep-alive" + ], + "Content-Length": [ + "40" + ], + "Content-Type": [ + "application/x-checksum" + ], + "Date": [ + "Tue, 08 Nov 2016 15:22:26 GMT" + ], + "Hoverfly": [ + "Was-Here" + ], + "Last-Modified": [ + "Tue, 08 Nov 2016 15:22:26 GMT" + ], + "Server": [ + "Artifactory/4.14.0" + ], + "X-Artifactory-Id": [ + "aolprod1b-test" + ], + "X-Node": [ + "nginx-shared2b" + ] + } + }, + "request": { + "requestType": "recording", + "path": "/test/libs-snapshot-local/io/test/test-simulations-svc/1.0-SNAPSHOT/test-simulations-svc-1.0-SNAPSHOT-stubs.jar.sha1", + "method": "GET", + "destination": "test.jfrog.io", + "scheme": "https", + "query": "", + "body": "", + "headers": { + "Accept-Encoding": [ + "gzip,deflate" + ], + "Authorization": [ + "Basic dXNlcm5hbWU6cGFzc3dvcmQK==" + ], + "Cache-Control": [ + "no-cache, no-store" + ], + "Connection": [ + "Keep-Alive" + ], + "Pragma": [ + "no-cache" + ], + "User-Agent": [ + "Aether" + ] + } + } + } + ] +} \ No newline at end of file