Sync docs from master to gh-pages
This commit is contained in:
@@ -638,6 +638,12 @@ $(addBlockSwitches);
|
||||
</li>
|
||||
<li><a href="#_stub_runner_core">Stub Runner Core</a>
|
||||
<ul class="sectlevel3">
|
||||
<li><a href="#_retrieving_stubs">Retrieving stubs</a>
|
||||
<ul class="sectlevel4">
|
||||
<li><a href="#_stub_downloading">Stub downloading</a></li>
|
||||
<li><a href="#_classpath_scanning">Classpath scanning</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_running_stubs">Running stubs</a>
|
||||
<ul class="sectlevel4">
|
||||
<li><a href="#_limitations">Limitations</a></li>
|
||||
@@ -4225,10 +4231,195 @@ publishing {
|
||||
<a href="http://martinfowler.com/articles/consumerDrivenContracts.html">Consumer Driven Contracts</a>.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Stub Runner allows you to automatically download the stubs of the provided dependencies, start WireMock servers for them and feed them with proper stub definitions.
|
||||
<p>Stub Runner allows you to automatically download the stubs of the provided dependencies (or pick those from the classpath), start WireMock servers for them and feed them with proper stub definitions.
|
||||
For messaging, special stub routes are defined.</p>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_retrieving_stubs">Retrieving stubs</h4>
|
||||
<div class="paragraph">
|
||||
<p>You can pick the following options of acquiring stubs</p>
|
||||
</div>
|
||||
<div class="ulist">
|
||||
<ul>
|
||||
<li>
|
||||
<p>Aether based solution that downloads JARs with stubs from Artifactory / Nexus</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Classpath scanning solution that searches classpath via pattern to retrieve stubs</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Write your own implementation of the <code>org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder</code> for full customization</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The latter example is described in the <a href="#custom_stub_runner">Custom Stub Runner</a> section.</p>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_stub_downloading">Stub downloading</h5>
|
||||
<div class="paragraph">
|
||||
<p>If you provide the <code>stubrunner.repositoryRoot</code> or <code>stubrunner.workOffline</code> flag will be set
|
||||
to <code>true</code> 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.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Example:</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-java" data-lang="java">@AutoConfigureStubRunner(repositoryRoot="http://foo.bar", ids = "com.example:beer-api-producer:+:stubs:8095")</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_classpath_scanning">Classpath scanning</h5>
|
||||
<div class="paragraph">
|
||||
<p>If you <strong>DON’T</strong> provide the <code>stubrunner.repositoryRoot</code> or <code>stubrunner.workOffline</code> flag will
|
||||
be set to <code>false</code> (that’s the default) then classpath will get scanned. Let’s look at the
|
||||
following example:</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-java" data-lang="java">@AutoConfigureStubRunner(ids = {
|
||||
"com.example:beer-api-producer:+:stubs:8095",
|
||||
"com.example.foo:bar:1.0.0:superstubs:8096"
|
||||
})</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>If you’ve added the dependencies to your classpath</p>
|
||||
</div>
|
||||
<div class="listingblock primary">
|
||||
<div class="title">Maven</div>
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-xml" data-lang="xml"><dependency>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>beer-api-producer-restdocs</artifactId>
|
||||
<classifier>stubs</classifier>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.example.foo</groupId>
|
||||
<artifactId>bar</artifactId>
|
||||
<classifier>superstubs</classifier>
|
||||
<version>1.0.0</version>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="listingblock secondary">
|
||||
<div class="title">Gradle</div>
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-groovy" data-lang="groovy">testCompile("com.example:beer-api-producer-restdocs:0.0.1-SNAPSHOT:stubs") {
|
||||
transitive = false
|
||||
}
|
||||
testCompile("com.example.foo:bar:1.0.0:superstubs") {
|
||||
transitive = false
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Then the following locations on your classpath will get scanned. For <code>com.example:beer-api-producer-restdocs</code></p>
|
||||
</div>
|
||||
<div class="ulist">
|
||||
<ul>
|
||||
<li>
|
||||
<p>/META-INF/com.example/beer-api-producer-restdocs/<strong>*/</strong>.*</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>/contracts/com.example/beer-api-producer-restdocs/<strong>*/</strong>.*</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>/mappings/com.example/beer-api-producer-restdocs/<strong>*/</strong>.*</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>and <code>com.example.foo:bar</code></p>
|
||||
</div>
|
||||
<div class="ulist">
|
||||
<ul>
|
||||
<li>
|
||||
<p>/META-INF/com.example.foo/bar/<strong>*/</strong>.*</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>/contracts/com.example.foo/bar/<strong>*/</strong>.*</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>/mappings/com.example.foo/bar/<strong>*/</strong>.*</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="admonitionblock tip">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<div class="title">Tip</div>
|
||||
</td>
|
||||
<td class="content">
|
||||
As you can see you have to explicitly provide the group and artifact ids when packaging the
|
||||
producer stubs.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The producer would setup the contracts like this:</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-bash" data-lang="bash">└── src
|
||||
└── test
|
||||
└── resources
|
||||
└── contracts
|
||||
└── com.example
|
||||
└── beer-api-producer-restdocs
|
||||
└── nested
|
||||
└── contract3.groovy</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>To achieve proper stub packaging.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Or using the <a href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples/blob/master/producer_with_restdocs/pom.xml">Maven <code>assembly</code> plugin</a> or
|
||||
<a href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples/blob/master/producer_with_restdocs/build.gradle">Gradle Jar</a> task you have to create the following
|
||||
structure in your stubs jar.</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-bash" data-lang="bash">└── META-INF
|
||||
└── com.example
|
||||
└── beer-api-producer-restdocs
|
||||
└── 2.0.0
|
||||
├── contracts
|
||||
│ └── nested
|
||||
│ └── contract2.groovy
|
||||
└── mappings
|
||||
└── mapping.json</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>By maintaining this structure classpath gets scanned and you can profit from the messaging /
|
||||
HTTP stubs without the need to download artifacts.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_running_stubs">Running stubs</h4>
|
||||
<div class="sect4">
|
||||
<h5 id="_limitations">Limitations</h5>
|
||||
@@ -8603,8 +8794,7 @@ class MocoHttpServerStub implements HttpServerStub {
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre class="highlight"><code># Example of a custom HTTP Server Stub
|
||||
org.springframework.cloud.contract.stubrunner.HttpServerStub=\
|
||||
<pre class="highlight"><code>org.springframework.cloud.contract.stubrunner.HttpServerStub=\
|
||||
org.springframework.cloud.contract.stubrunner.provider.moco.MocoHttpServerStub</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
@@ -8628,93 +8818,30 @@ will be picked. If you provide more than one then the first one on the list will
|
||||
<div class="sect3">
|
||||
<h4 id="_custom_stub_downloader">Custom Stub Downloader</h4>
|
||||
<div class="paragraph">
|
||||
<p>You can customize the way your stubs are downloaded. If you don’t want to download the JARs
|
||||
from Nexus / Artifactory in the way we do by default you can set your own implementation.
|
||||
Below you can find an example of a Stub Downloader Provider that takes <code>json</code> files from the test resources
|
||||
from classpath, copies them to a temp file and then passes that temporary folder
|
||||
as a root for the stubs.</p>
|
||||
<p>You can customize the way your stubs are downloaded. It’s enough to create an
|
||||
implementation of the <code>StubDownloaderBuilder</code></p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="content">
|
||||
<pre class="highlight"><code class="language-java" data-lang="java">package org.springframework.cloud.contract.stubrunner.provider.moco
|
||||
<pre class="highlight"><code class="language-java" data-lang="java">package com.example;
|
||||
|
||||
import org.springframework.cloud.contract.stubrunner.StubConfiguration
|
||||
import org.springframework.cloud.contract.stubrunner.StubDownloader
|
||||
import org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder
|
||||
import org.springframework.cloud.contract.stubrunner.StubRunnerOptions
|
||||
import org.springframework.core.io.DefaultResourceLoader
|
||||
import org.springframework.core.io.Resource
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver
|
||||
|
||||
import java.nio.file.Files
|
||||
|
||||
/**
|
||||
* Poor man's version of taking stubs from classpath. It needs much more
|
||||
* love and attention to go to the main sources.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
class ClasspathStubProvider implements StubDownloaderBuilder {
|
||||
|
||||
private static final int TEMP_DIR_ATTEMPTS = 10000
|
||||
class CustomStubDownloaderBuilder implements StubDownloaderBuilder {
|
||||
|
||||
@Override
|
||||
public StubDownloader build(StubRunnerOptions stubRunnerOptions) {
|
||||
final StubConfiguration configuration = stubRunnerOptions.getDependencies().first()
|
||||
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(
|
||||
new DefaultResourceLoader())
|
||||
try {
|
||||
String rootFolder = repoRoot(stubRunnerOptions) ?: "**/" + separatedArtifact(configuration) + "/**/*.json"
|
||||
Resource[] resources = resolver.getResources(rootFolder)
|
||||
final File tmp = createTempDir()
|
||||
tmp.deleteOnExit()
|
||||
// you'd have to write an impl to maintain the folder structure
|
||||
// this is just for demo
|
||||
resources.each { Resource resource ->
|
||||
Files.copy(resource.getInputStream(), new File(tmp, resource.getFile().getName()).toPath())
|
||||
public StubDownloader build(final StubRunnerOptions stubRunnerOptions) {
|
||||
return new StubDownloader() {
|
||||
@Override
|
||||
public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
|
||||
StubConfiguration config) {
|
||||
File unpackedStubs = retrieveStubs();
|
||||
return new AbstractMap.SimpleEntry<>(
|
||||
new StubConfiguration(config.getGroupId(), config.getArtifactId(), version,
|
||||
config.getClassifier()), unpackedStubs);
|
||||
}
|
||||
return new StubDownloader() {
|
||||
@Override
|
||||
public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
|
||||
StubConfiguration stubConfiguration) {
|
||||
return new AbstractMap.SimpleEntry(configuration, tmp)
|
||||
}
|
||||
|
||||
File retrieveStubs() {
|
||||
// here goes your custom logic to provide a folder where all the stubs reside
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException(e)
|
||||
}
|
||||
}
|
||||
|
||||
private String repoRoot(StubRunnerOptions stubRunnerOptions) {
|
||||
switch (stubRunnerOptions.stubRepositoryRoot) {
|
||||
case { !it }:
|
||||
return ""
|
||||
case { String root -> root.endsWith("**/*.json") }:
|
||||
return stubRunnerOptions.stubRepositoryRoot
|
||||
default:
|
||||
return stubRunnerOptions.stubRepositoryRoot + "/**/*.json"
|
||||
}
|
||||
}
|
||||
|
||||
private String separatedArtifact(StubConfiguration configuration) {
|
||||
return configuration.getGroupId().replace(".", File.separator) +
|
||||
File.separator + configuration.getArtifactId()
|
||||
}
|
||||
|
||||
// Taken from Guava
|
||||
private File createTempDir() {
|
||||
File baseDir = new File(System.getProperty("java.io.tmpdir"))
|
||||
String baseName = System.currentTimeMillis() + "-"
|
||||
for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
|
||||
File tempDir = new File(baseDir, baseName + counter)
|
||||
if (tempDir.mkdir()) {
|
||||
return tempDir
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"Failed to create directory within " + TEMP_DIR_ATTEMPTS + " attempts (tried " + baseName + "0 to " + baseName + (
|
||||
TEMP_DIR_ATTEMPTS - 1) + ")")
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
@@ -8725,7 +8852,7 @@ class ClasspathStubProvider implements StubDownloaderBuilder {
|
||||
<div class="content">
|
||||
<pre class="highlight"><code># Example of a custom Stub Downloader Provider
|
||||
org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder=\
|
||||
org.springframework.cloud.contract.stubrunner.provider.moco.ClasspathStubProvider</code></pre>
|
||||
com.example.CustomStubDownloaderBuilder</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
@@ -8738,8 +8865,11 @@ org.springframework.cloud.contract.stubrunner.provider.moco.ClasspathStubProvide
|
||||
<div class="title">Important</div>
|
||||
</td>
|
||||
<td class="content">
|
||||
If you don’t provide any implementation then the default one - Aether based that will download stubs from a remote repo
|
||||
will be picked. If you provide more than one then the first one on the list will be picked.
|
||||
If you don’t provide any implementation then the default one will be picked.
|
||||
If you provide <code>repositoryRoot</code> property or <code>workOffline</code> flag then Aether based
|
||||
that will download stubs from a remote repo will be picked. If you don’t provide these
|
||||
values then the <code>ClasspathStubProvider</code> will be picked that will scan the classpath.
|
||||
If you provide more than one, then the first one on the list will be picked.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Reference in New Issue
Block a user