Adding option to dump mappings to files (#356)
without this feature we're missing an option to print all mappings registered for servers with this feature it will be much easier to debug what was registered in which http server stub. The mappings will be dumped to files with name artifactName_registeredPort fixes #355
This commit is contained in:
committed by
GitHub
parent
654127302e
commit
0c234e089c
@@ -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() {
|
||||
|
||||
@@ -95,6 +95,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;
|
||||
}
|
||||
@@ -199,6 +204,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