Skip to content

Commit

Permalink
Introduces explicit mode of stub downloading
Browse files Browse the repository at this point in the history
you have to explicitly provide the mode [CLASSPATH, REMOTE, LOCAL] of how you want to fetch and register stubs

fixes gh-287
  • Loading branch information
marcingrzejszczak committed Dec 27, 2017
1 parent d6871be commit d156987
Show file tree
Hide file tree
Showing 164 changed files with 4,314 additions and 163 deletions.
9 changes: 5 additions & 4 deletions README.adoc
Expand Up @@ -213,7 +213,7 @@ Annotate your test class with `@AutoConfigureStubRunner`. In the annotation prov
----
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.NONE)
@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, workOffline = true)
@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL)
@DirtiesContext
public class LoanApplicationServiceTests {
----
Expand Down Expand Up @@ -640,7 +640,7 @@ can also provide the offline work switch.
----
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.NONE)
@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, workOffline = true)
@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL)
@DirtiesContext
public class LoanApplicationServiceTests {
----
Expand Down Expand Up @@ -861,13 +861,14 @@ git merge --no-ff contract-change-pr

Now you can disable the offline work for Spring Cloud Contract Stub Runner and indicate
where the repository with your stubs is located. At this moment the stubs of the server
side are automatically downloaded from Nexus/Artifactory. You can switch off the value of
the `workOffline` parameter in your annotation. The following code shows an example of
side are automatically downloaded from Nexus/Artifactory. You can set the value of
`stubsMode` to `REMOTE`. The following code shows an example of
achieving the same thing by changing the properties.

[source,yaml,indent=0]
----
stubrunner:
stubsMode: REMOTE
ids: 'com.example:http-server-dsl:+:stubs:8080'
repositoryRoot: http://repo.spring.io/libs-snapshot
----
Expand Down
9 changes: 4 additions & 5 deletions docs/src/main/asciidoc/verifier_contract.adoc
Expand Up @@ -1367,8 +1367,7 @@ com.example.CustomStubDownloaderBuilder

Now you can pick a folder with the source of your stubs.

IMPORTANT: If you do not provide any implementation, then the default is used.
If you use the `repositoryRoot` property or the `workOffline` flag, then an Aether-based
implementation that downloads stubs from a remote repository is used. If you do not
provide these values, the `ClasspathStubProvider` (which will scan the classpath) is
used. If you provide more than one, then the first one on the list is used.
IMPORTANT: If you do not provide any implementation, then the default is used (scan classpath).
If you provide the `stubsMode = StubRunnerProperties.StubsMode.LOCAL` or
`, stubsMode = StubRunnerProperties.StubsMode.REMOTE` then the Aether implementation will be used
If you provide more than one, then the first one on the list is used.
4 changes: 2 additions & 2 deletions docs/src/main/asciidoc/verifier_introduction.adoc
Expand Up @@ -567,8 +567,8 @@ git merge --no-ff contract-change-pr

Now you can disable the offline work for Spring Cloud Contract Stub Runner and indicate
where the repository with your stubs is located. At this moment the stubs of the server
side are automatically downloaded from Nexus/Artifactory. You can switch off the value of
the `workOffline` parameter in your annotation. The following code shows an example of
side are automatically downloaded from Nexus/Artifactory. You can set the value of
`stubsMode` to `REMOTE`. The following code shows an example of
achieving the same thing by changing the properties.

[source,yaml,indent=0]
Expand Down
3 changes: 1 addition & 2 deletions docs/src/main/asciidoc/verifier_stubrunner.adoc
Expand Up @@ -74,8 +74,7 @@ properties. Here are their names with their default values:
|stubrunner.maxPort|15000| Maximum value of a port for a started WireMock with stubs.
|stubrunner.repositoryRoot|| Maven repo URL. If blank, then call the local maven repo.
|stubrunner.classifier|stubs| Default classifier for the stub artifacts.
|stubrunner.workOffline|false| If true, then do not contact any remote repositories to
download stubs.
|stubrunner.stubsMode|CLASSPATH| The way you want to fetch and register the stubs
|stubrunner.ids|| Array of Ivy notation stubs to download.
|stubrunner.username|| Optional username to access the tool that stores the JARs with
stubs.
Expand Down
5 changes: 2 additions & 3 deletions samples/standalone/contracts/README.adoc
Expand Up @@ -43,8 +43,7 @@ Example for Maven
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<configuration>
<!-- url not required for working locally -->
<contractsWorkOffline>true<contractsWorkOffline>
<stubsMode>LOCAL</stubsMode>
<contractDependency>
<groupId>com.example.standalone</groupId>
<artifactId>contracts</artifactId>
Expand All @@ -61,7 +60,7 @@ contracts {
targetFramework = 'Spock'
testMode = 'JaxRsClient'
baseClassForTests = 'org.springframework.cloud.MvcSpec'
contractsWorkOffline = true
stubsMode = 'LOCAL'
contractDependency {
stringNotation = "com.example:contracts"
}
Expand Down
Expand Up @@ -8,6 +8,7 @@
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;

Expand All @@ -18,7 +19,8 @@

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.NONE, properties="server.context-path=/app")
@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, workOffline = true)
@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"},
stubsMode = StubRunnerProperties.StubsMode.LOCAL)
@DirtiesContext
public class LoanApplicationServiceContextPathTests {

Expand Down
Expand Up @@ -6,6 +6,7 @@
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;

Expand All @@ -19,7 +20,8 @@
// tag::autoconfigure_stubrunner[]
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.NONE)
@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"}, workOffline = true)
@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"},
stubsMode = StubRunnerProperties.StubsMode.LOCAL)
@DirtiesContext
public class LoanApplicationServiceTests {
// end::autoconfigure_stubrunner[]
Expand Down
Expand Up @@ -25,6 +25,7 @@
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.contract.stubrunner.StubTrigger;
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.integration.support.management.MessageChannelMetrics;
import org.springframework.messaging.SubscribableChannel;
Expand All @@ -36,7 +37,9 @@
* @author Marius Bogoevici
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.NONE, properties = "spring.cloud.stream.bindings.input.destination=sensor-data")
@SpringBootTest(webEnvironment = WebEnvironment.NONE,
properties = "spring.cloud.stream.bindings.input.destination=sensor-data",
stubsMode = StubRunnerProperties.StubsMode.LOCAL)
@AutoConfigureStubRunner
public class MessageConsumedTests {

Expand Down
Expand Up @@ -9,6 +9,7 @@
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import org.springframework.core.env.Environment;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
Expand All @@ -20,7 +21,8 @@

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.NONE)
@AutoConfigureStubRunner(ids = {"com.example:pact-http-server:+:stubs"}, workOffline = true)
@AutoConfigureStubRunner(ids = {"com.example:pact-http-server:+:stubs"},
stubsMode = StubRunnerProperties.StubsMode.LOCAL)
@DirtiesContext
public class LoanApplicationServiceTests {

Expand Down
24 changes: 13 additions & 11 deletions spring-cloud-contract-stub-runner/README.adoc
Expand Up @@ -18,23 +18,25 @@ The latter example is described in the <<custom_stub_runner, Custom Stub Runner>

===== Stub downloading

If you provide the `stubrunner.repositoryRoot` or `stubrunner.workOffline` flag will be set
to `true` then Stub Runner will connect to the given server and download the required jars.
It will then unpack the JAR to a temporary folder and reference those files in further
contract processing.
You can control the stub downloading via the `stubsMode` switch. It picks value from the
`StubRunnerProperties.StubsMode` enum. You can use the following options

- `StubRunnerProperties.StubsMode.CLASSPATH` (default value) - will pick stubs from the classpath
- `StubRunnerProperties.StubsMode.LOCAL` - will pick stubs from a local storage (e.g. `.m2`)
- `StubRunnerProperties.StubsMode.REMOTE` - will pick stubs from a remote location

Example:

[source,java]
----
@AutoConfigureStubRunner(repositoryRoot="http://foo.bar", ids = "com.example:beer-api-producer:+:stubs:8095")
@AutoConfigureStubRunner(repositoryRoot="http://foo.bar", ids = "com.example:beer-api-producer:+:stubs:8095", stubsMode = StubRunnerProperties.StubsMode.LOCAL)
----

===== Classpath scanning

If you *DON'T* provide the `stubrunner.repositoryRoot` and `stubrunner.workOffline` flag will
be set to `false` (that's the default) then classpath will get scanned. Let's look at the
following example:
If you set the `stubsMode` property to `StubRunnerProperties.StubsMode.CLASSPATH`
(or set nothing since `CLASSPATH` is the default value) then classpath will get scanned.
Let's look at the following example:

[source,java]
----
Expand Down Expand Up @@ -182,10 +184,10 @@ You can set the following options to the main class:
representation of jars with stubs.
Eg. groupid:artifactid1,groupid2:
artifactid2:classifier
--sm, --stubsMode Stubs mode to be used. Acceptable values
[CLASSPATH, LOCAL, REMOTE]
-u, --username Username to user when connecting to
repository
--wo, --workOffline Switch to work offline. Defaults to
'false'
----

===== HTTP Stubs
Expand Down Expand Up @@ -483,7 +485,7 @@ or a subdirectory called `config` or in `~/.spring-cloud`. The file could look l
[source,yml,indent=0]
----
stubrunner:
workOffline: true
stubsMode: LOCAL
ids:
- com.example:beer-api-producer:+:9876
----
Expand Down
Expand Up @@ -47,6 +47,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.contract.stubrunner.StubRunnerOptions.StubRunnerProxyOptions;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import org.springframework.util.StringUtils;

import static java.nio.file.Files.createTempDirectory;
Expand Down Expand Up @@ -84,20 +85,20 @@ public AetherStubDownloader(StubRunnerOptions stubRunnerOptions) {
}
this.remoteRepos = remoteRepositories(stubRunnerOptions);
boolean remoteReposMissing = remoteReposMissing();
if (remoteReposMissing && stubRunnerOptions.workOffline) {
log.info("Remote repos not passed but the switch to work offline was set. "
+ "Stubs will be used from your local Maven repository.");
}
if (remoteReposMissing && !stubRunnerOptions.workOffline) {
throw new IllegalStateException("Remote repositories for stubs are not specified and work offline flag wasn't passed");
}
if (!remoteReposMissing && stubRunnerOptions.workOffline) {
throw new IllegalStateException("Remote repositories for stubs are specified and work offline flag is set. "
+ "You have to provide one of them.");
switch (stubRunnerOptions.stubsMode) {
case LOCAL:
log.info("Remote repos not passed but the switch to work offline was set. "
+ "Stubs will be used from your local Maven repository.");
break;
case REMOTE:
if (remoteReposMissing) throw new IllegalStateException("Remote repositories for stubs are not specified and work offline flag wasn't passed");
break;
case CLASSPATH:
throw new UnsupportedOperationException("You can't use Aether downloader when you use classpath to find stubs");
}
this.repositorySystem = newRepositorySystem();
this.session = newSession(this.repositorySystem, stubRunnerOptions.workOffline);
this.workOffline = stubRunnerOptions.workOffline;
this.workOffline = stubRunnerOptions.stubsMode == StubRunnerProperties.StubsMode.LOCAL;
this.session = newSession(this.repositorySystem, this.workOffline);
registerShutdownHook();
}

Expand Down
Expand Up @@ -25,8 +25,9 @@

/**
* Stub downloader that picks stubs and contracts from the provided resource.
* If no {@link org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties#repositoryRoot}
* is provided then by default classpath is searched according to what has been passed in
* If {@link org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties#stubsMode} is set
* to {@link org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties.StubsMode#CLASSPATH}
* then classpath is searched according to what has been passed in
* {@link org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties#ids}. The
* pattern to search for stubs looks like this
*
Expand Down
Expand Up @@ -6,8 +6,8 @@

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.util.StringUtils;

/**
* Provider for {@link StubDownloaderBuilder}. It can also pick a default
Expand Down Expand Up @@ -40,8 +40,8 @@ public StubDownloader getOrDefaultDownloader(StubRunnerOptions stubRunnerOptions
log.info("A custom Stub Downloader was passed - will pick [" + get() + "]");
return get().build(stubRunnerOptions);
}
if (!stubRunnerOptions.isWorkOffline() && StringUtils.isEmpty(stubRunnerOptions.getStubRepositoryRoot())) {
log.info("Classpath scanning will be used due to passed propreties");
if (stubRunnerOptions.stubsMode == StubRunnerProperties.StubsMode.CLASSPATH) {
log.info("Classpath scanning will be used due to passed properties");
return new ClasspathStubProvider().build(stubRunnerOptions);
}
log.info("Will download stubs using Aether");
Expand Down
Expand Up @@ -25,6 +25,7 @@
import joptsimple.ArgumentAcceptingOptionSpec;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;

public class StubRunnerMain {

Expand Down Expand Up @@ -67,11 +68,14 @@ private StubRunnerMain(String[] args) throws Exception {
.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'");
ArgumentAcceptingOptionSpec<String> stubsMode = parser
.acceptsAll(Arrays.asList("sm", "stubsMode"),"Stubs mode to be used. Acceptable values " + Arrays
.toString(StubRunnerProperties.StubsMode.values()))
.withRequiredArg().defaultsTo(StubRunnerProperties.StubsMode.CLASSPATH.toString());
OptionSet options = parser.parse(args);
String stubs = options.valueOf(stubsOpt);
boolean workOffline = options.has("wo");
StubRunnerProperties.StubsMode stubsModeValue = StubRunnerProperties.StubsMode.valueOf(
options.valueOf(stubsMode));
Integer minPortValue = options.valueOf(minPortValueOpt);
Integer maxPortValue = options.valueOf(maxPortValueOpt);
String stubRepositoryRoot= options.valueOf(rootOpt);
Expand All @@ -83,7 +87,7 @@ private StubRunnerMain(String[] args) throws Exception {
final StubRunnerOptionsBuilder builder = new StubRunnerOptionsBuilder()
.withMinMaxPort(minPortValue, maxPortValue)
.withStubRepositoryRoot(stubRepositoryRoot)
.withWorkOffline(workOffline).withStubsClassifier(stubsSuffix)
.withStubsMode(stubsModeValue).withStubsClassifier(stubsSuffix)
.withUsername(username)
.withPassword(password)
.withStubs(stubs);
Expand Down
Expand Up @@ -19,6 +19,7 @@
import java.util.Collection;
import java.util.Map;

import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties;
import org.springframework.cloud.contract.stubrunner.util.StringUtils;

/**
Expand All @@ -45,11 +46,6 @@ public class StubRunnerOptions {
*/
final String stubRepositoryRoot;

/**
* avoids local repository in dependency resolution
*/
final boolean workOffline;

/**
* stub definition classifier
*/
Expand Down Expand Up @@ -93,16 +89,18 @@ public class StubRunnerOptions {
*/
private String mappingsOutputFolder;

final StubRunnerProperties.StubsMode stubsMode;

StubRunnerOptions(Integer minPortValue, Integer maxPortValue,
String stubRepositoryRoot, boolean workOffline, String stubsClassifier,
String stubRepositoryRoot, StubRunnerProperties.StubsMode stubsMode, String stubsClassifier,
Collection<StubConfiguration> dependencies,
Map<StubConfiguration, Integer> stubIdsToPortMapping,
String username, String password, final StubRunnerProxyOptions stubRunnerProxyOptions,
boolean stubsPerConsumer, String consumerName, String mappingsOutputFolder) {
this.minPortValue = minPortValue;
this.maxPortValue = maxPortValue;
this.stubRepositoryRoot = stubRepositoryRoot;
this.workOffline = workOffline;
this.stubsMode = stubsMode != null ? stubsMode : StubRunnerProperties.StubsMode.CLASSPATH;
this.stubsClassifier = stubsClassifier;
this.dependencies = dependencies;
this.stubIdsToPortMapping = stubIdsToPortMapping;
Expand All @@ -128,7 +126,7 @@ public static StubRunnerOptions fromSystemProps() {
.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")))
.withStubsMode(System.getProperty("stubrunner.stubs-mode", "CLASSPATH"))
.withStubsClassifier(System.getProperty("stubrunner.classifier", "stubs"))
.withStubs(System.getProperty("stubrunner.ids", ""))
.withUsername(System.getProperty("stubrunner.username"))
Expand Down Expand Up @@ -163,8 +161,8 @@ public String getStubRepositoryRoot() {
return this.stubRepositoryRoot;
}

public boolean isWorkOffline() {
return this.workOffline;
public StubRunnerProperties.StubsMode getStubsMode() {
return this.stubsMode;
}

public String getStubsClassifier() {
Expand Down Expand Up @@ -242,7 +240,7 @@ public int getProxyPort() {
@Override public String toString() {
return "StubRunnerOptions{" + "minPortValue=" + this.minPortValue + ", maxPortValue="
+ this.maxPortValue + ", stubRepositoryRoot='" + this.stubRepositoryRoot + '\''
+ ", workOffline=" + this.workOffline + ", stubsClassifier='" + this.stubsClassifier
+ ", stubsMode='" + this.stubsMode + "', stubsClassifier='" + this.stubsClassifier
+ '\'' + ", dependencies=" + this.dependencies + ", stubIdsToPortMapping="
+ this.stubIdsToPortMapping + ", username='" + obfuscate(this.username) + '\'' + ", password='"
+ obfuscate(this.password) + '\'' + ", stubRunnerProxyOptions='" + this.stubRunnerProxyOptions + "', stubsPerConsumer='"
Expand Down

0 comments on commit d156987

Please sign in to comment.