Added option to provide full string notation to find stub

without this change you could use only `artifactid` or `groupid:artifactid`
without this change when a stub is not found a null was returned

with this change you can use `groupid:artifactid:version` or `groupid:artifactid:version:classifier`
with this change when a stub is not found an exception is thrown

fixes #102
This commit is contained in:
Marcin Grzejszczak
2016-10-11 16:08:23 +02:00
parent 1a88cbd40a
commit 0fc5642ddb
12 changed files with 100 additions and 41 deletions

View File

@@ -52,24 +52,21 @@ public class BatchStubRunner implements StubRunning {
@Override
public URL findStubUrl(String groupId, String artifactId) {
for (StubRunner stubRunner : this.stubRunners) {
URL url = stubRunner.findStubUrl(groupId, artifactId);
if (url != null) {
return url;
}
try {
return stubRunner.findStubUrl(groupId, artifactId);
} catch (StubNotFoundException e) {}
}
return null;
throw new StubNotFoundException(groupId, artifactId);
}
@Override
public URL findStubUrl(String ivyNotation) {
String[] splitString = ivyNotation.split(":");
if (splitString.length > 3) {
throw new IllegalArgumentException(ivyNotation + " is invalid");
for (StubRunner stubRunner : this.stubRunners) {
try {
return stubRunner.findStubUrl(ivyNotation);
} catch (StubNotFoundException e) {}
}
else if (splitString.length == 2) {
return findStubUrl(splitString[0], splitString[1]);
}
return findStubUrl(null, splitString[0]);
throw new StubNotFoundException(ivyNotation);
}
@Override

View File

@@ -20,7 +20,7 @@ import org.springframework.util.StringUtils;
/**
* Represents a configuration of a single stub. The stub can be described by
* groupId:artifactId:classifier notation
* groupId:artifactId:version:classifier notation
*/
public class StubConfiguration {
private static final String STUB_COLON_DELIMITER = ":";

View File

@@ -28,18 +28,18 @@ public interface StubFinder extends StubTrigger {
* URL of the running stub.
*
* @param groupId - might be null. In that case a search only via artifactId takes place
* @return URL of a running stub or null if not found
* @return URL of a running stub or throws exception if not found
*/
URL findStubUrl(String groupId, String artifactId);
URL findStubUrl(String groupId, String artifactId) throws StubNotFoundException;
/**
* For the given Ivy notation {@code groupId:artifactId} tries to find the matching
* URL of the running stub. You can also pass only {@code artifactId}.
* For the given Ivy notation {@code [groupId]:artifactId:[version]:[classifier]} tries to
* find the matching URL of the running stub. You can also pass only {@code artifactId}.
*
* @param ivyNotation - Ivy representation of the Maven artifact
* @return URL of a running stub or null if not found
* @return URL of a running stub or throws exception if not found
*/
URL findStubUrl(String ivyNotation);
URL findStubUrl(String ivyNotation) throws StubNotFoundException;
/**
* Returns all running stubs

View File

@@ -0,0 +1,18 @@
package org.springframework.cloud.contract.stubrunner;
/**
* Exception thrown when a stub was not found
*
* @author Marcin Grzejszczak
* @since 1.0.1
*/
public class StubNotFoundException extends RuntimeException {
public StubNotFoundException(String groupId, String artifactId) {
super("Stub not found for groupid [" + groupId + "] and artifactid [" + artifactId + "]");
}
public StubNotFoundException(String ivyNotation) {
super("Stub not found for stub with notation [" + ivyNotation + "]");
}
}

View File

@@ -75,12 +75,7 @@ public class StubRunner implements StubRunning {
@Override
public URL findStubUrl(String ivyNotation) {
String[] splitString = ivyNotation.split(":");
if (splitString.length == 1) {
// assuming that ivy notation represents artifactId only
return findStubUrl(null, splitString[0]);
}
return findStubUrl(splitString[0], splitString[1]);
return this.localStubRunner.findStubUrl(ivyNotation);
}
@Override

View File

@@ -85,22 +85,48 @@ class StubRunnerExecutor implements StubFinder {
@Override
public URL findStubUrl(String groupId, String artifactId) {
URL url = null;
if (groupId == null) {
return returnStubUrlIfMatches(
artifactId.equals(this.stubServer.stubConfiguration.artifactId));
url = findStubUrl(
this.stubServer.stubConfiguration.artifactId.equals(artifactId));
}
return returnStubUrlIfMatches(
artifactId.equals(this.stubServer.stubConfiguration.artifactId)
&& groupId.equals(this.stubServer.stubConfiguration.groupId));
if (url == null) {
url = findStubUrl(this.stubServer.stubConfiguration.artifactId.equals(artifactId)
&& this.stubServer.stubConfiguration.groupId.equals(groupId));
}
if (url == null) {
throw new StubNotFoundException(groupId, artifactId);
}
return url;
}
@Override
public URL findStubUrl(String ivyNotation) {
String[] splitString = ivyNotation.split(":");
if (splitString.length == 1) {
throw new IllegalArgumentException("$ivyNotation is invalid");
if (splitString.length > 4) {
throw new IllegalArgumentException("[" + ivyNotation + "] is an invalid notation. Pass [groupId]:artifactId[:version][:classifier].");
} else if (splitString.length == 1) {
return findStubUrl(null, splitString[0]);
} else if (splitString.length == 2) {
return findStubUrl(splitString[0], splitString[1]);
} else if (splitString.length == 3) {
return findStubUrl(groupIdArtifactVersionMatches(splitString));
}
return findStubUrl(splitString[0], splitString[1]);
return findStubUrl(groupIdArtifactVersionMatches(splitString) && classifierMatches(splitString));
}
private boolean classifierMatches(String[] splitString) {
return this.stubServer.stubConfiguration.classifier.equals(splitString[3]);
}
private boolean groupIdArtifactVersionMatches(String[] splitString) {
return this.stubServer.stubConfiguration.artifactId.equals(splitString[0])
&& this.stubServer.stubConfiguration.groupId.equals(splitString[1])
&& this.stubServer.stubConfiguration.version.equals(splitString[2]);
}
private URL findStubUrl(boolean condition) {
return returnStubUrlIfMatches(condition);
}
@Override

View File

@@ -60,7 +60,8 @@ public @interface AutoConfigureStubRunner {
String repositoryRoot() default "";
/**
* The ids of the stubs to run in "ivy" notation (groupId:artifactId[:classifier]:version[:port]).
* The ids of the stubs to run in "ivy" notation ([groupId]:artifactId:[classifier]:[version][:port]).
* {@code groupId}, {@code classifier}, {@code version} and {@code port} can be optional.
*/
String[] ids() default {};

View File

@@ -50,7 +50,8 @@ public class StubRunnerProperties {
private Resource repositoryRoot;
/**
* The ids of the stubs to run in "ivy" notation (groupId:artifactId[:classifier]:version[:port]).
* The ids of the stubs to run in "ivy" notation ([groupId]:artifactId:[classifier]:[version][:port]).
* {@code groupId}, {@code classifier}, {@code version} and {@code port} can be optional.
*/
private String[] ids = new String[0];

View File

@@ -34,7 +34,9 @@ public class StubsParser {
* that contains a list of Strings in the format
*
* <ul>
* <li>groupid:artifactid:classifier</li>
* <li>groupid:artifactid:version:classifier:port</li>
* <li>groupid:artifactid:version:classifier</li>
* <li>groupid:artifactid:version</li>
* <li>groupid:artifactid</li>
* </ul>
*

View File

@@ -31,11 +31,13 @@ class BatchStubRunnerSpec extends Specification {
batchStubRunner.findStubUrl(KNOWN_STUB_PATH) == KNOWN_STUB_URL
}
def 'should return empty optional for unknown stub path'() {
def 'should throw an exception for unknown stub path'() {
given:
BatchStubRunner batchStubRunner = new BatchStubRunner(runners())
expect:
!batchStubRunner.findStubUrl(UNKNOWN_STUB_PATH)
when:
batchStubRunner.findStubUrl(UNKNOWN_STUB_PATH)
then:
thrown(StubNotFoundException)
}
def 'should throw exception if trying to execute not available trigger'() {
@@ -51,7 +53,8 @@ class BatchStubRunnerSpec extends Specification {
Collection<StubRunner> runners() {
StubRunner runner = Mock(StubRunner)
runner.findStubUrl("group", "knownArtifact") >> KNOWN_STUB_URL
runner.findStubUrl("group", "unknownArtifact") >> null
runner.findStubUrl("group:knownArtifact") >> KNOWN_STUB_URL
runner.findStubUrl("group:unknownArtifact") >> { throw new StubNotFoundException(UNKNOWN_STUB_PATH) }
runner.labels() >> ['a:b:c' : ['foo']]
return [runner]
}

View File

@@ -62,8 +62,10 @@ class StubRunnerExecutorSpec extends Specification {
StubRunnerExecutor executor = new StubRunnerExecutor(portScanner)
when:
executor.runStubs(stubRunnerOptions, repository, stub)
and:
executor.findStubUrl("unkowngroup", "unknownartifact")
then:
!executor.findStubUrl("unkowngroup", "unknownartifact")
thrown(StubNotFoundException)
cleanup:
executor.shutdown()
}

View File

@@ -23,6 +23,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.cloud.contract.stubrunner.StubFinder
import org.springframework.cloud.contract.stubrunner.StubNotFoundException
import org.springframework.context.annotation.Configuration
import org.springframework.test.annotation.DirtiesContext
import org.springframework.test.context.ActiveProfiles
@@ -57,6 +58,8 @@ class StubRunnerConfigurationSpec extends Specification {
stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs', 'loanIssuance') != null
stubFinder.findStubUrl('loanIssuance') != null
stubFinder.findStubUrl('loanIssuance') == stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs', 'loanIssuance')
stubFinder.findStubUrl('loanIssuance') == stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:loanIssuance')
stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT') == stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT:stubs')
stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer') != null
and:
stubFinder.findAllRunningStubs().isPresent('loanIssuance')
@@ -67,6 +70,17 @@ class StubRunnerConfigurationSpec extends Specification {
"${stubFinder.findStubUrl('fraudDetectionServer').toString()}/name".toURL().text == 'fraudDetectionServer'
}
def 'should throw an exception when stub is not found'() {
when:
stubFinder.findStubUrl('nonExistingService')
then:
thrown(StubNotFoundException)
when:
stubFinder.findStubUrl('nonExistingGroupId', 'nonExistingArtifactId')
then:
thrown(StubNotFoundException)
}
@Configuration
@EnableAutoConfiguration
static class Config {}