Merge branch 'master' into 2.0.x
This commit is contained in:
@@ -1339,6 +1339,12 @@ The generated tests all boil down to RestAssured in some form or fashion which r
|
||||
logging.level.org.apache.http.wire=DEBUG
|
||||
----
|
||||
|
||||
==== How can I see what got registered in the HTTP server stub?
|
||||
|
||||
You can use the `mappingsOutputFolder` property on `@AutoConfigureStubRunner` or `StubRunnerRule`
|
||||
to dump all mappings per artifact id. Also the port at which the given stub server was
|
||||
started will be attached.
|
||||
|
||||
==== Can I reference the request from the response?
|
||||
|
||||
Yes! With version 1.1.0 we've added such a possibility. On the HTTP stub server side we're providing support
|
||||
|
||||
19
docs/src/main/asciidoc/migrations.adoc
Normal file
19
docs/src/main/asciidoc/migrations.adoc
Normal file
@@ -0,0 +1,19 @@
|
||||
:core_path: ../../../..
|
||||
:doc_samples: {core_path}/samples/wiremock-jetty
|
||||
:wiremock_tests: {core_path}/spring-cloud-contract-wiremock
|
||||
|
||||
In the following document we will write about necessary migration steps between versions.
|
||||
|
||||
=== 1.0.x -> 1.1.x
|
||||
|
||||
TODO: https://github.com/spring-cloud/spring-cloud-contract/issues/350
|
||||
|
||||
=== 1.1.x -> 1.2.x
|
||||
|
||||
==== Custom `HttpServerStub`
|
||||
|
||||
By introducing a method `String registeredMappings()` in the public interface
|
||||
`HttpServerStub`, if you wrote a custom `HttpServerStub` implementation, you'll
|
||||
have to implement that method too. It should return a `String` representing
|
||||
all mappings available in a single `HttpServerStub`. Related to
|
||||
https://github.com/spring-cloud/spring-cloud-contract/issues/355[issue 355].
|
||||
@@ -23,3 +23,7 @@ include::verifier/spring-cloud-contract-verifier.adoc[]
|
||||
== Spring Cloud Contract WireMock
|
||||
|
||||
include::spring-cloud-wiremock.adoc[]
|
||||
|
||||
== Migrations
|
||||
|
||||
include::migrations.adoc[]
|
||||
@@ -868,6 +868,12 @@ The generated tests all boil down to RestAssured in some form or fashion which r
|
||||
logging.level.org.apache.http.wire=DEBUG
|
||||
----
|
||||
|
||||
==== How can I see what got registered in the HTTP server stub?
|
||||
|
||||
You can use the `mappingsOutputFolder` property on `@AutoConfigureStubRunner` or `StubRunnerRule`
|
||||
to dump all mappings per artifact id. Also the port at which the given stub server was
|
||||
started will be attached.
|
||||
|
||||
==== Can I reference the request from the response?
|
||||
|
||||
Yes! With version 1.1.0 we've added such a possibility. On the HTTP stub server side we're providing support
|
||||
|
||||
@@ -215,6 +215,57 @@ Example:
|
||||
|
||||
Every stubbed collaborator exposes list of defined mappings under `__/admin/` endpoint.
|
||||
|
||||
You can also use the `mappingsOutputFolder` property to dump the mappings to files.
|
||||
For annotation based approach it would look like this
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@AutoConfigureStubRunner(ids="a.b.c:loanIssuance,a.b.c:fraudDetectionServer",
|
||||
mappingsOutputFolder = "target/outputmappings/")
|
||||
----
|
||||
|
||||
and for the JUnit approach like this:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@ClassRule @Shared StubRunnerRule rule = new StubRunnerRule()
|
||||
.repoRoot("http://some_url")
|
||||
.downloadStub("a.b.c", "loanIssuance")
|
||||
.downloadStub("a.b.c:fraudDetectionServer")
|
||||
.withMappingsOutputFolder("target/outputmappings")
|
||||
----
|
||||
|
||||
Then if you check out the folder `target/outputmappings` you would see the following structure
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
.
|
||||
├── fraudDetectionServer_13705
|
||||
└── loanIssuance_12255
|
||||
----
|
||||
|
||||
That means that there were two stubs registered. `fraudDetectionServer` was registered at port `13705`
|
||||
and `loanIssuance` at port `12255`. If we take a look at one of the files we would see (for WireMock)
|
||||
mappings available for the given server:
|
||||
|
||||
[souce,json]
|
||||
----
|
||||
[{
|
||||
"id" : "f9152eb9-bf77-4c38-8289-90be7d10d0d7",
|
||||
"request" : {
|
||||
"url" : "/name",
|
||||
"method" : "GET"
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"body" : "fraudDetectionServer"
|
||||
},
|
||||
"uuid" : "f9152eb9-bf77-4c38-8289-90be7d10d0d7"
|
||||
},
|
||||
...
|
||||
]
|
||||
----
|
||||
|
||||
===== Messaging Stubs
|
||||
|
||||
Depending on the provided Stub Runner dependency and the DSL the messaging routes are automatically set up.
|
||||
|
||||
@@ -43,6 +43,11 @@ public interface HttpServerStub {
|
||||
*/
|
||||
HttpServerStub registerMappings(Collection<File> stubFiles);
|
||||
|
||||
/**
|
||||
* Returns a collection of registered mappings
|
||||
*/
|
||||
String registeredMappings();
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the file is a valid stub mapping
|
||||
*/
|
||||
|
||||
@@ -37,6 +37,10 @@ class NoOpHttpServerStub implements HttpServerStub {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public String registeredMappings() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override public boolean isAccepted(File file) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -19,14 +19,19 @@ package org.springframework.cloud.contract.stubrunner;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.contract.spec.Contract;
|
||||
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier;
|
||||
import org.springframework.cloud.contract.verifier.messaging.noop.NoOpStubMessages;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Represents a single instance of ready-to-run stubs. Can run the stubs and then will
|
||||
@@ -35,6 +40,8 @@ import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
*/
|
||||
public class StubRunner implements StubRunning {
|
||||
|
||||
private static final Log log = LogFactory.getLog(StubRunner.class);
|
||||
|
||||
private final StubRepository stubRepository;
|
||||
private final StubConfiguration stubsConfiguration;
|
||||
private final StubRunnerOptions stubRunnerOptions;
|
||||
@@ -61,8 +68,32 @@ public class StubRunner implements StubRunning {
|
||||
@Override
|
||||
public RunningStubs runStubs() {
|
||||
registerShutdownHook();
|
||||
return this.localStubRunner.runStubs(this.stubRunnerOptions, this.stubRepository,
|
||||
RunningStubs stubs = this.localStubRunner.runStubs(this.stubRunnerOptions, this.stubRepository,
|
||||
this.stubsConfiguration);
|
||||
if (this.stubRunnerOptions.hasMappingsOutputFolder()) {
|
||||
String registeredMappings = this.localStubRunner.registeredMappings();
|
||||
if (StringUtils.hasText(registeredMappings)) {
|
||||
File outputMappings = new File(this.stubRunnerOptions.getMappingsOutputFolder(),
|
||||
this.stubsConfiguration.artifactId + "_"
|
||||
+ stubs.getPort(this.stubsConfiguration.toColonSeparatedDependencyNotation()));
|
||||
try {
|
||||
if (outputMappings.exists()) {
|
||||
outputMappings.delete();
|
||||
}
|
||||
outputMappings.getParentFile().mkdirs();
|
||||
outputMappings.createNewFile();
|
||||
Files.write(Paths.get(outputMappings.toURI()), registeredMappings.getBytes());
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Stored the mappings for artifactid [" + this.stubsConfiguration.artifactId + " at [" + outputMappings + "] location");
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("Exception occurred while trying to store the mappings", e);
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return stubs;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.contract.stubrunner;
|
||||
|
||||
import groovy.json.JsonOutput;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
@@ -28,6 +26,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import groovy.json.JsonOutput;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cloud.contract.spec.Contract;
|
||||
@@ -91,6 +90,10 @@ class StubRunnerExecutor implements StubFinder {
|
||||
}
|
||||
}
|
||||
|
||||
String registeredMappings() {
|
||||
return this.stubServer.registeredMappings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL findStubUrl(String groupId, String artifactId) {
|
||||
URL url = null;
|
||||
|
||||
@@ -19,6 +19,8 @@ package org.springframework.cloud.contract.stubrunner;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.contract.stubrunner.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Technical options related to running StubRunner
|
||||
*
|
||||
@@ -85,12 +87,18 @@ public class StubRunnerOptions {
|
||||
*/
|
||||
private String consumerName;
|
||||
|
||||
/**
|
||||
* For debugging purposes you can output the registered mappings to a given folder. Each HTTP server
|
||||
* stub will have its own subfolder where all the mappings will get stored.
|
||||
*/
|
||||
private String mappingsOutputFolder;
|
||||
|
||||
StubRunnerOptions(Integer minPortValue, Integer maxPortValue,
|
||||
String stubRepositoryRoot, boolean workOffline, String stubsClassifier,
|
||||
Collection<StubConfiguration> dependencies,
|
||||
Map<StubConfiguration, Integer> stubIdsToPortMapping,
|
||||
String username, String password, final StubRunnerProxyOptions stubRunnerProxyOptions,
|
||||
boolean stubsPerConsumer, String consumerName) {
|
||||
boolean stubsPerConsumer, String consumerName, String mappingsOutputFolder) {
|
||||
this.minPortValue = minPortValue;
|
||||
this.maxPortValue = maxPortValue;
|
||||
this.stubRepositoryRoot = stubRepositoryRoot;
|
||||
@@ -103,6 +111,7 @@ public class StubRunnerOptions {
|
||||
this.stubRunnerProxyOptions = stubRunnerProxyOptions;
|
||||
this.stubsPerConsumer = stubsPerConsumer;
|
||||
this.consumerName = consumerName;
|
||||
this.mappingsOutputFolder = mappingsOutputFolder;
|
||||
}
|
||||
|
||||
public Integer port(StubConfiguration stubConfiguration) {
|
||||
@@ -174,6 +183,18 @@ public class StubRunnerOptions {
|
||||
this.consumerName = consumerName;
|
||||
}
|
||||
|
||||
public boolean hasMappingsOutputFolder() {
|
||||
return StringUtils.hasText(this.mappingsOutputFolder);
|
||||
}
|
||||
|
||||
public String getMappingsOutputFolder() {
|
||||
return this.mappingsOutputFolder;
|
||||
}
|
||||
|
||||
public void setMappingsOutputFolder(String mappingsOutputFolder) {
|
||||
this.mappingsOutputFolder = mappingsOutputFolder;
|
||||
}
|
||||
|
||||
public static class StubRunnerProxyOptions {
|
||||
|
||||
private final String proxyHost;
|
||||
|
||||
@@ -43,6 +43,7 @@ public class StubRunnerOptionsBuilder {
|
||||
private StubRunnerOptions.StubRunnerProxyOptions stubRunnerProxyOptions;
|
||||
private boolean stubPerConsumer = false;
|
||||
private String consumerName;
|
||||
private String mappingsOutputFolder;
|
||||
|
||||
public StubRunnerOptionsBuilder() {
|
||||
}
|
||||
@@ -109,10 +110,16 @@ public class StubRunnerOptionsBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public StubRunnerOptionsBuilder withMappingsOutputFolder(String mappingsOutputFolder) {
|
||||
this.mappingsOutputFolder = mappingsOutputFolder;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StubRunnerOptions build() {
|
||||
return new StubRunnerOptions(this.minPortValue, this.maxPortValue, this.stubRepositoryRoot,
|
||||
this.workOffline, this.stubsClassifier, buildDependencies(), this.stubIdsToPortMapping,
|
||||
this.username, this.password, this.stubRunnerProxyOptions, this.stubPerConsumer, this.consumerName);
|
||||
this.username, this.password, this.stubRunnerProxyOptions, this.stubPerConsumer, this.consumerName,
|
||||
this.mappingsOutputFolder);
|
||||
}
|
||||
|
||||
private Collection<StubConfiguration> buildDependencies() {
|
||||
|
||||
@@ -91,5 +91,8 @@ class StubServer {
|
||||
return this.contracts;
|
||||
}
|
||||
|
||||
String registeredMappings() {
|
||||
return this.httpServerStub.registeredMappings();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -219,6 +219,14 @@ public class StubRunnerRule implements TestRule, StubFinder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows setting the output folder for mappings
|
||||
*/
|
||||
public StubRunnerRule withMappingsOutputFolder(String mappingsOutputFolder) {
|
||||
this.stubRunnerOptionsBuilder.withMappingsOutputFolder(mappingsOutputFolder);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL findStubUrl(String groupId, String artifactId) {
|
||||
return this.stubFinder.findStubUrl(groupId, artifactId);
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
package org.springframework.cloud.contract.stubrunner.provider.wiremock;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.github.jknack.handlebars.Helper;
|
||||
import com.github.tomakehurst.wiremock.WireMockServer;
|
||||
import com.github.tomakehurst.wiremock.client.WireMock;
|
||||
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
|
||||
import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer;
|
||||
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.cloud.contract.stubrunner.HttpServerStub;
|
||||
@@ -24,6 +24,7 @@ import org.springframework.cloud.contract.wiremock.WireMockSpring;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.SocketUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Abstraction over WireMock as a HTTP Server Stub
|
||||
@@ -109,6 +110,18 @@ public class WireMockHttpServerStub implements HttpServerStub {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override public String registeredMappings() {
|
||||
Collection<String> mappings = new ArrayList<>();
|
||||
for (StubMapping stubMapping : this.wireMockServer.getStubMappings()) {
|
||||
mappings.add(stubMapping.toString());
|
||||
}
|
||||
return jsonArrayOfMappings(mappings);
|
||||
}
|
||||
|
||||
private String jsonArrayOfMappings(Collection<String> mappings) {
|
||||
return "[" + StringUtils.collectionToDelimitedString(mappings, ",\n") + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccepted(File file) {
|
||||
return file.getName().endsWith(".json");
|
||||
|
||||
@@ -98,4 +98,12 @@ public @interface AutoConfigureStubRunner {
|
||||
* @see <a href="https://github.com/spring-cloud/spring-cloud-contract/issues/224">issue 224</a>
|
||||
*/
|
||||
String consumerName() default "";
|
||||
|
||||
/**
|
||||
* For debugging purposes you can output the registered mappings to a given folder. Each HTTP server
|
||||
* stub will have its own subfolder where all the mappings will get stored.
|
||||
*
|
||||
* @see <a href="https://github.com/spring-cloud/spring-cloud-contract/issues/355">issue 355</a>
|
||||
*/
|
||||
String mappingsOutputFolder() default "";
|
||||
}
|
||||
|
||||
@@ -92,7 +92,8 @@ public class StubRunnerConfiguration {
|
||||
.withUsername(this.props.getUsername())
|
||||
.withPassword(this.props.getPassword())
|
||||
.withStubPerConsumer(this.props.isStubsPerConsumer())
|
||||
.withConsumerName(consumerName());
|
||||
.withConsumerName(consumerName())
|
||||
.withMappingsOutputFolder(this.props.getMappingsOutputFolder());
|
||||
}
|
||||
|
||||
private String consumerName() {
|
||||
|
||||
@@ -90,6 +90,11 @@ public class StubRunnerProperties {
|
||||
*/
|
||||
private String consumerName;
|
||||
|
||||
/**
|
||||
* Dumps the mappings of each HTTP server to the selected folder
|
||||
*/
|
||||
private String mappingsOutputFolder;
|
||||
|
||||
public int getMinPort() {
|
||||
return this.minPort;
|
||||
}
|
||||
@@ -186,6 +191,18 @@ public class StubRunnerProperties {
|
||||
this.consumerName = consumerName;
|
||||
}
|
||||
|
||||
public void setRepositoryRoot(Resource repositoryRoot) {
|
||||
this.repositoryRoot = repositoryRoot;
|
||||
}
|
||||
|
||||
public String getMappingsOutputFolder() {
|
||||
return this.mappingsOutputFolder;
|
||||
}
|
||||
|
||||
public void setMappingsOutputFolder(String mappingsOutputFolder) {
|
||||
this.mappingsOutputFolder = mappingsOutputFolder;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "StubRunnerProperties{" + "minPort=" + this.minPort + ", maxPort=" + this.maxPort
|
||||
+ ", workOffline=" + this.workOffline + ", repositoryRoot=" + this.repositoryRoot
|
||||
|
||||
@@ -39,6 +39,8 @@ class StubRunnerRuleSpec extends Specification {
|
||||
.repoRoot(StubRunnerRuleSpec.getResource("/m2repo/repository").toURI().toString())
|
||||
.downloadStub("org.springframework.cloud.contract.verifier.stubs", "loanIssuance")
|
||||
.downloadStub("org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer")
|
||||
.withMappingsOutputFolder("target/outputmappingsforule")
|
||||
|
||||
|
||||
def 'should start WireMock servers'() {
|
||||
expect: 'WireMocks are running'
|
||||
@@ -54,5 +56,12 @@ class StubRunnerRuleSpec extends Specification {
|
||||
"${rule.findStubUrl('loanIssuance').toString()}/name".toURL().text == 'loanIssuance'
|
||||
"${rule.findStubUrl('fraudDetectionServer').toString()}/name".toURL().text == 'fraudDetectionServer'
|
||||
}
|
||||
|
||||
def 'should output mappings to output folder'() {
|
||||
when:
|
||||
def url = rule.findStubUrl('fraudDetectionServer')
|
||||
then:
|
||||
new File("target/outputmappingsforule", "fraudDetectionServer_${url.port}").exists()
|
||||
}
|
||||
// end::classrule[]
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ import spock.lang.Specification
|
||||
@SpringBootTest(properties = [" stubrunner.cloud.enabled=false",
|
||||
"stubrunner.camel.enabled=false",
|
||||
'foo=${stubrunner.runningstubs.fraudDetectionServer.port}'])
|
||||
@AutoConfigureStubRunner
|
||||
@AutoConfigureStubRunner(mappingsOutputFolder = "target/outputmappings/")
|
||||
@DirtiesContext
|
||||
@ActiveProfiles("test")
|
||||
class StubRunnerConfigurationSpec extends Specification {
|
||||
@@ -103,6 +103,13 @@ class StubRunnerConfigurationSpec extends Specification {
|
||||
foo == fraudPort
|
||||
}
|
||||
|
||||
def 'should dump all mappings to a file'() {
|
||||
when:
|
||||
def url = stubFinder.findStubUrl("fraudDetectionServer")
|
||||
then:
|
||||
new File("target/outputmappings/", "fraudDetectionServer_${url.port}").exists()
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
static class Config {}
|
||||
|
||||
@@ -82,6 +82,11 @@ class MocoHttpServerStub implements HttpServerStub {
|
||||
return this
|
||||
}
|
||||
|
||||
@Override
|
||||
String registeredMappings() {
|
||||
return ""
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isAccepted(File file) {
|
||||
return file.name.endsWith(".json")
|
||||
|
||||
Reference in New Issue
Block a user