From ad243b05f7623d8bc8d07c3eb6e4ed324c1132c0 Mon Sep 17 00:00:00 2001 From: buildmaster Date: Mon, 6 Feb 2017 21:43:06 +0000 Subject: [PATCH] Sync docs from master to gh-pages --- spring-cloud-contract.html | 1274 ++++++++++++++++++------------------ 1 file changed, 630 insertions(+), 644 deletions(-) diff --git a/spring-cloud-contract.html b/spring-cloud-contract.html index f26bd2365c..bd696d8c9c 100644 --- a/spring-cloud-contract.html +++ b/spring-cloud-contract.html @@ -785,39 +785,16 @@ $(addBlockSwitches);
  • Extending the DSL
  • -
  • Pluggable architecture - -
  • -
  • Links
  • - - +
  • Converters
  • +
  • Stub converters
  • +
  • Example of a custom HTTP Server Stub
  • +
  • Example of a custom Stub Downloader Provider
  • @@ -7577,78 +7554,22 @@ public class PatternUtils {
    -
    package com.example;
    +
    Unresolved directive in verifier/contract.adoc - include::https://raw.githubusercontent.com/spring-cloud-samples/spring-cloud-contract-samples/master/common/src/main/java/com/example/ConsumerUtils.java[]
     
    -import org.springframework.cloud.contract.spec.internal.ClientDslProperty;
    -import org.springframework.cloud.contract.spec.internal.DslProperty;
    +*ProducerUtils* contains functions used by the **producer**.
     
    -/**
    - * DSL Properties passed to the DSL from the consumer's perspective.
    - * That means that on the input side {@code Request} for HTTP
    - * or {@code Input} for messaging you can have a regular expression.
    - * On the {@code Response} for HTTP or {@code Output} for messaging
    - * you have to have a concrete value.
    - *
    - * @author Marcin Grzejszczak
    - */
    -public class ConsumerUtils {
    -	/**
    -	 * Consumer side property. By using the {@link ClientDslProperty}
    -	 * you can omit most of boilerplate code from the perspective
    -	 * of dynamic values. Example
    -	 *
    -	 * <pre>
    -	 * {@code
    -	 * request {
    -	 *     body(
    -	 *         [ age: $(ConsumerUtils.oldEnough())]
    -	 *     )
    -	 * }
    -	 * </pre>
    -	 *
    -	 * That way the consumer side value of age field will be
    -	 * a regular expression and the producer side will be generated.
    -	 *
    -	 * @author Marcin Grzejszczak
    -	 */
    -	public static ClientDslProperty oldEnough() {
    -		return new ClientDslProperty(PatternUtils.oldEnough());
    -	}
    -
    -	/**
    -	 * Consumer side property. By using the {@link ClientDslProperty}
    -	 * you can omit most of boilerplate code from the perspective
    -	 * of dynamic values. Example
    -	 *
    -	 * <pre>
    -	 * {@code
    -	 * request {
    -	 *     body(
    -	 *         [ name: $(ConsumerUtils.anyName())]
    -	 *     )
    -	 * }
    -	 * </pre>
    -	 *
    -	 * That way the consumer will be a regular expression and the
    -	 * producer side value will be equal to {@code marcin}
    -	 */
    -	public static DslProperty anyName() {
    -		return new DslProperty<>(PatternUtils.anyName(), "marcin");
    -	}
    -}
    +[source,java]
    -

    ProducerUtils contains functions used by the producer.

    +

    package com.example;

    -
    -
    -
    package com.example;
    -
    -import org.springframework.cloud.contract.spec.internal.ServerDslProperty;
    -
    -/**
    - * DSL Properties passed to the DSL from the producer's perspective.
    +
    +

    import org.springframework.cloud.contract.spec.internal.ServerDslProperty;

    +
    +
    +

    /** + * DSL Properties passed to the DSL from the producer’s perspective. * That means that on the input side {@code Request} for HTTP * or {@code Input} for messaging you have to have a concrete value. * On the {@code Response} for HTTP or {@code Output} for messaging @@ -7656,9 +7577,11 @@ import org.springframework.cloud.contract.spec.internal.ServerDslProperty; * * @author Marcin Grzejszczak */ -public class ProducerUtils { - - /** +public class ProducerUtils {

    +
    +
    +
    +
    	/**
     	 * Producer side property. By using the {@link ProducerUtils}
     	 * you can omit most of boilerplate code from the perspective
     	 * of dynamic values. Example
    @@ -7678,50 +7601,55 @@ public class ProducerUtils {
     	public static ServerDslProperty ok() {
     		return new ServerDslProperty(PatternUtils.ok());
     	}
    -}
    +}
    - -
    -
    Adding the dependency to project
    -
    -

    In order for the plugins and IDE to be able to reference the common JAR classes you need -to pass the dependency to your project.

    -
    -
    -
    Test dependency in project’s dependencies
    -
    -

    First add the common jar dependency as a test dependency. That way since your -contracts files are available at test resources path, automatically the -common jar classes will be visible in your Groovy files.

    -
    -
    -
    Maven
    +
    -
    <dependency>
    +
    ===== Adding the dependency to project
    +
    +In order for the plugins and IDE to be able to reference the common JAR classes you need
    +to pass the dependency to your project.
    +
    +====== Test dependency in project's dependencies
    +
    +First add the common jar dependency as a test dependency. That way since your
    +contracts files are available at test resources path, automatically the
    +common jar classes will be visible in your Groovy files.
    +
    +[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
    +.Maven
    +
    +
    +
    +

    <dependency> <groupId>com.example</groupId> <artifactId>beer-common</artifactId> <version>${project.version}</version> <scope>test</scope> -</dependency> +</dependency>

    -
    -
    -
    Gradle
    +
    -
    testCompile("com.example:beer-common:0.0.1-SNAPSHOT")
    +
    [source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
    +.Gradle
    -
    -
    -
    Test dependency in plugin’s dependencies
    -

    Now you have to add the dependency for the plugin to reuse at runtime.

    +

    testCompile("com.example:beer-common:0.0.1-SNAPSHOT")

    -
    -
    Maven
    +
    -
    <plugin>
    +
    ====== Test dependency in plugin's dependencies
    +
    +Now you have to add the dependency for the plugin to reuse at runtime.
    +
    +[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
    +.Maven
    +
    +
    +
    +

    <plugin> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-contract-maven-plugin</artifactId> <version>${spring-cloud-contract.version}</version> @@ -7742,41 +7670,49 @@ common jar classes will be visible in your Groovy files.

    <scope>compile</scope> </dependency> </dependencies> -</plugin>
    -
    -
    -
    -
    Gradle
    -
    -
    classpath "com.example:beer-common:0.0.1-SNAPSHOT"
    -
    -
    -
    -
    -
    Referencing classes in DSLs
    -
    -

    Now you can reference your classes in your DSL. Example:

    +</plugin>

    -
    package contracts.beer.rest
    +
    [source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
    +.Gradle
    +
    +
    +
    +

    classpath "com.example:beer-common:0.0.1-SNAPSHOT"

    +
    +
    +
    +
    ====== Referencing classes in DSLs
     
    -import org.springframework.cloud.contract.spec.Contract
    +Now you can reference your classes in your DSL. Example:
     
    -import static com.example.ConsumerUtils.oldEnough
    -import static com.example.ProducerUtils.ok
    -
    -Contract.make {
    +[source,groovy]
    +
    +
    +
    +

    package contracts.beer.rest

    +
    +
    +

    import org.springframework.cloud.contract.spec.Contract

    +
    +
    +

    import static com.example.ConsumerUtils.oldEnough +import static com.example.ProducerUtils.ok

    +
    +
    +

    Contract.make { request { description(""" -Represents a successful scenario of getting a beer - -given: +Represents a successful scenario of getting a beer

    +
    +
    +

    given: client is old enough when: he applies for a beer then: - we'll grant him the beer + we’ll grant him the beer """) method 'POST' url '/check' @@ -7798,31 +7734,28 @@ then: contentType(applicationJson()) } } -} -

    -
    -
    -
    - - -
    -

    Pluggable architecture

    -
    -

    There are cases where you have your contracts defined in other formats -like YAML, RAML or PACT. On the other hand you’d like to profit from -the test and stubs generation. It’s really easy to add your own implementation -of either of those. Also you can customize the way tests are generated (for example you can generate -tests for other languages) and you can do the same for stubs generation (you can generate -stubs for other stub http server implementations).

    -
    -
    -

    Custom contract converter

    -
    -

    Let’s assume that your contract is written in a YAML file like this:

    +}

    -
    request:
    +
    === Pluggable architecture
    +
    +There are cases where you have your contracts defined in other formats
    +like YAML, RAML or PACT. On the other hand you'd like to profit from
    +the test and stubs generation. It's really easy to add your own implementation
    +of either of those. Also you can customize the way tests are generated (for example you can generate
    +tests for other languages) and you can do the same for stubs generation (you can generate
    +stubs for other stub http server implementations).
    +
    +==== Custom contract converter
    +
    +Let's assume that your contract is written in a YAML file like this:
    +
    +[source,yml]
    +
    +
    +
    +

    request: url: /foo method: PUT headers: @@ -7834,17 +7767,20 @@ response: headers: foo2: bar body: - foo2: bar -

    -
    -
    -

    Thanks to the interface

    + foo2: bar

    -
    package org.springframework.cloud.contract.spec
    +
    Thanks to the interface
     
    -/**
    +[source,groovy]
    +
    +
    +
    +

    package org.springframework.cloud.contract.spec

    +
    +
    +

    /** * Converter to be used to convert FROM {@link File} TO {@link Contract} * and from {@link Contract} to {@code T} * @@ -7853,120 +7789,137 @@ response: * @author Marcin Grzejszczak * @since 1.1.0 */ -interface ContractConverter<T> { - - /** - * Should this file be accepted by the converter. Can use the file extension - * to check if the conversion is possible. - * - * @param file - file to be considered for conversion - * @return - {@code true} if the given implementation can convert the file - */ - boolean isAccepted(File file) - - /** - * Converts the given {@link File} to its {@link Contract} representation - * - * @param file - file to convert - * @return - {@link Contract} representation of the file - */ - Collection<Contract> convertFrom(File file) - - /** +interface ContractConverter<T> {

    +
    +
    +
    +
    /**
    + * Should this file be accepted by the converter. Can use the file extension
    + * to check if the conversion is possible.
    + *
    + * @param file - file to be considered for conversion
    + * @return - {@code true} if the given implementation can convert the file
    + */
    +boolean isAccepted(File file)
    +
    +
    +
    +
    +
    /**
    + * Converts the given {@link File} to its {@link Contract} representation
    + *
    + * @param file - file to convert
    + * @return - {@link Contract} representation of the file
    + */
    +Collection<Contract> convertFrom(File file)
    +
    +
    +
    +
    +
    	/**
     	 * Converts the given {@link Contract} to a {@link T} representation
     	 *
     	 * @param contract - the parsed contract
     	 * @return - {@link T} the type to which we do the conversion
     	 */
     	T convertTo(Collection<Contract> contract)
    -}
    +}
    -
    -

    you can register your own implementation of a contract structure converter. +

    +
    +
    you can register your own implementation of a contract structure converter.
     Your implementation needs to state the condition on which it should start the
    -conversion. Also you have to define how to perform that conversion in both ways.

    -
    -
    - - - - - -
    -
    Important
    -
    -Once you create your implementation you have to create a /META-INF/spring.factories -file in which you provide the fully qualified name of your implementation. -
    -
    -
    -

    Example of a spring.factories file

    -
    -
    -
    -
    # Converters
    -org.springframework.cloud.contract.spec.ContractConverter=\
    -org.springframework.cloud.contract.verifier.converter.YamlContractConverter
    -
    -
    -
    -

    and the YAML implementation

    -
    -
    -
    -
    package org.springframework.cloud.contract.verifier.converter
    +conversion. Also you have to define how to perform that conversion in both ways.
     
    -import groovy.transform.CompileStatic
    +IMPORTANT: Once you create your implementation you have to create a `/META-INF/spring.factories`
    +file in which you provide the fully qualified name of your implementation.
    +
    +Example of a `spring.factories` file
    +
    +[source]
    +
    +
    +
    +
    +
    + + +

    Converters

    +
    +

    org.springframework.cloud.contract.spec.ContractConverter=\ +org.springframework.cloud.contract.verifier.converter.YamlContractConverter

    +
    +
    +
    +
    and the YAML implementation
    +
    +[source,groovy]
    +
    +
    +
    +

    package org.springframework.cloud.contract.verifier.converter

    +
    +
    +

    import groovy.transform.CompileStatic import org.springframework.cloud.contract.spec.Contract import org.springframework.cloud.contract.spec.ContractConverter import org.springframework.cloud.contract.spec.internal.Headers -import org.yaml.snakeyaml.Yaml - -/** +import org.yaml.snakeyaml.Yaml

    +
    +
    +

    /** * Simple converter from and to a {@link YamlContract} to a collection of {@link Contract} */ @CompileStatic -class YamlContractConverter implements ContractConverter<List<YamlContract>> { - - @Override - public boolean isAccepted(File file) { - String name = file.getName() - return name.endsWith(".yml") || name.endsWith(".yaml") - } - - @Override - public Collection<Contract> convertFrom(File file) { - try { - YamlContract yamlContract = new Yaml().loadAs(new FileInputStream(file), YamlContract.class) - return [Contract.make { - request { - method(yamlContract?.request?.method) - url(yamlContract?.request?.url) - headers { - yamlContract?.request?.headers?.each { String key, Object value -> - header(key, value) - } +class YamlContractConverter implements ContractConverter<List<YamlContract>> {

    +
    +
    +
    +
    @Override
    +public boolean isAccepted(File file) {
    +	String name = file.getName()
    +	return name.endsWith(".yml") || name.endsWith(".yaml")
    +}
    +
    +
    +
    +
    +
    @Override
    +public Collection<Contract> convertFrom(File file) {
    +	try {
    +		YamlContract yamlContract = new Yaml().loadAs(new FileInputStream(file), YamlContract.class)
    +		return [Contract.make {
    +			request {
    +				method(yamlContract?.request?.method)
    +				url(yamlContract?.request?.url)
    +				headers {
    +					yamlContract?.request?.headers?.each { String key, Object value ->
    +						header(key, value)
     					}
    -					body(yamlContract?.request?.body)
     				}
    -				response {
    -					status(yamlContract?.response?.status)
    -					headers {
    -						yamlContract?.response?.headers?.each { String key, Object value ->
    -							header(key, value)
    -						}
    +				body(yamlContract?.request?.body)
    +			}
    +			response {
    +				status(yamlContract?.response?.status)
    +				headers {
    +					yamlContract?.response?.headers?.each { String key, Object value ->
    +						header(key, value)
     					}
    -					body(yamlContract?.response?.body)
     				}
    -			}]
    -		}
    -		catch (FileNotFoundException e) {
    -			throw new IllegalStateException(e)
    -		}
    +				body(yamlContract?.response?.body)
    +			}
    +		}]
     	}
    -
    -	@Override
    +	catch (FileNotFoundException e) {
    +		throw new IllegalStateException(e)
    +	}
    +}
    +
    +
    +
    +
    +
    	@Override
     	public List<YamlContract> convertTo(Collection<Contract> contracts) {
     		return contracts.collect { Contract contract ->
     			YamlContract yamlContract = new YamlContract()
    @@ -7984,25 +7937,27 @@ class YamlContractConverter implements ContractConverter<List<YamlContract
     			return yamlContract
     		}
     	}
    -}
    +}
    -
    -
    Pact converter
    -
    -

    Spring Cloud Contract comes with an out of the box support for Pact representation of contracts. -In other words instead of using the Groovy DSL you can use Pact files. In this section -we will present how to add such a support for your project.

    -
    -
    -
    Pact contract
    -
    -

    We will be working on the following example of a Pact contract. We’ve placed this file under -the src/test/resources/contracts folder.

    -
    -
    {
    +
    ===== Pact converter
    +
    +Spring Cloud Contract comes with an out of the box support for https://docs.pact.io/[Pact] representation of contracts.
    +In other words instead of using the Groovy DSL you can use Pact files. In this section
    +we will present how to add such a support for your project.
    +
    +====== Pact contract
    +
    +We will be working on the following example of a Pact contract. We've placed this file under
    +the `src/test/resources/contracts` folder.
    +
    +[source,javascript,indent=0]
    +
    +
    +
    +

    { "provider": { "name": "Provider" }, @@ -8055,21 +8010,22 @@ the src/test/resources/contracts folder.

    "version": "2.4.18" } } -}
    +}

    -
    -
    -
    -
    Pact for producers
    -
    -

    On the producer side you have add to your plugin configuration two additional dependencies. -One is the Spring Cloud Contract Pact support and the other represents the current -Pact version that you’re using.

    -
    -
    -
    Maven
    +
    -
    <plugin>
    +
    ====== Pact for producers
    +
    +On the producer side you have add to your plugin configuration two additional dependencies.
    +One is the Spring Cloud Contract Pact support and the other represents the current
    +Pact version that you're using.
    +
    +[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
    +.Maven
    +
    +
    +
    +

    <plugin> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-contract-maven-plugin</artifactId> <version>${spring-cloud-contract.version}</version> @@ -8089,33 +8045,45 @@ Pact version that you’re using.

    <version>2.4.18</version> </dependency> </dependencies> -</plugin>
    -
    -
    -
    -
    Gradle
    -
    -
    classpath "org.springframework.cloud:spring-cloud-contract-spec-pact:${findProperty('verifierVersion') ?: verifierVersion}"
    -classpath 'au.com.dius:pact-jvm-model:2.4.18'
    -
    -
    -
    -

    When you execute the build of your application a test, looking more or less like this, will be generated

    +</plugin>

    -
    @Test
    +
    [source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
    +.Gradle
    +
    +
    +
    +

    classpath "org.springframework.cloud:spring-cloud-contract-spec-pact:${findProperty('verifierVersion') ?: verifierVersion}" +classpath 'au.com.dius:pact-jvm-model:2.4.18'

    +
    +
    +
    +
    When you execute the build of your application a test, looking more or less like this, will be generated
    +
    +[source,java,indent=0]
    +
    +
    +
    +
    +
    @Test
     public void validate_shouldMarkClientAsFraud() throws Exception {
     	// given:
     		MockMvcRequestSpecification request = given()
     				.header("Content-Type", "application/vnd.fraud.v1+json")
    -				.body("{\"clientId\":\"1234567890\",\"loanAmount\":99999}");
    -
    -	// when:
    -		ResponseOptions response = given().spec(request)
    -				.put("/fraudcheck");
    -
    -	// then:
    +				.body("{\"clientId\":\"1234567890\",\"loanAmount\":99999}");
    +
    +
    +
    +
    +
    // when:
    +	ResponseOptions response = given().spec(request)
    +			.put("/fraudcheck");
    +
    +
    +
    +
    +
    	// then:
     		assertThat(response.statusCode()).isEqualTo(200);
     		assertThat(response.header("Content-Type")).isEqualTo("application/vnd.fraud.v1+json;charset=UTF-8");
     	// and:
    @@ -8123,15 +8091,18 @@ public void validate_shouldMarkClientAsFraud() throws Exception {
     		assertThatJson(parsedJson).field("rejectionReason").isEqualTo("Amount too high");
     	// and:
     		assertThat(parsedJson.read("$.fraudCheckStatus", String.class)).matches("FRAUD");
    -}
    +}
    -
    -

    and the stub looking like this

    -
    -
    {
    +
    and the stub looking like this
    +
    +[source,javascript,indent=0]
    +
    +
    +
    +

    { "uuid" : "996ae5ae-6834-4db6-8fac-358ca187ab62", "request" : { "url" : "/fraudcheck", @@ -8154,21 +8125,22 @@ public void validate_shouldMarkClientAsFraud() throws Exception { "Content-Type" : "application/vnd.fraud.v1+json;charset=UTF-8" } } -} +}

    -
    - -
    -
    Pact for consumers
    -
    -

    On the producer side you have add to your project dependencies two additional dependencies. -One is the Spring Cloud Contract Pact support and the other represents the current -Pact version that you’re using.

    -
    -
    -
    Maven
    +
    -
    <dependency>
    +
    ====== Pact for consumers
    +
    +On the producer side you have add to your project dependencies two additional dependencies.
    +One is the Spring Cloud Contract Pact support and the other represents the current
    +Pact version that you're using.
    +
    +[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
    +.Maven
    +
    +
    +
    +

    <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-contract-spec-pact</artifactId> <scope>test</scope> @@ -8178,46 +8150,51 @@ Pact version that you’re using.

    <artifactId>pact-jvm-model</artifactId> <version>2.4.18</version> <scope>test</scope> -</dependency>
    -
    -
    -
    -
    Gradle
    -
    -
    testCompile "org.springframework.cloud:spring-cloud-contract-spec-pact"
    -testCompile 'au.com.dius:pact-jvm-model:2.4.18'
    -
    -
    -
    - - -
    -

    Custom test generator

    -
    -

    If you want to generate tests for different languages than Java or you’re -not happy with the way we’re building Java tests for you then you can register -your own implementation to do that.

    -
    -
    -

    Thanks to the interface

    +</dependency>

    -
    package org.springframework.cloud.contract.verifier.builder
    +
    [source,groovy,indent=0,subs="verbatim,attributes",role="secondary"]
    +.Gradle
    +
    +
    +
    +

    testCompile "org.springframework.cloud:spring-cloud-contract-spec-pact" +testCompile 'au.com.dius:pact-jvm-model:2.4.18'

    +
    +
    +
    +
    ==== Custom test generator
     
    -import groovy.transform.CompileStatic
    +If you want to generate tests for different languages than Java or you're
    +not happy with the way we're building Java tests for you then you can register
    +your own implementation to do that.
    +
    +Thanks to the interface
    +
    +[source,groovy]
    +
    +
    +
    +

    package org.springframework.cloud.contract.verifier.builder

    +
    +
    +

    import groovy.transform.CompileStatic import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties -import org.springframework.cloud.contract.verifier.file.ContractMetadata - -/** +import org.springframework.cloud.contract.verifier.file.ContractMetadata

    +
    +
    +

    /** * Builds a single test. * * @since 1.1.0 */ @CompileStatic -interface SingleTestGenerator { - - /** +interface SingleTestGenerator {

    +
    +
    +
    +
    	/**
     	 * Creates contents of a single test class in which all test scenarios from
     	 * the contract metadata should be placed.
     	 *
    @@ -8230,54 +8207,68 @@ interface SingleTestGenerator {
     	 */
     	String buildClass(ContractVerifierConfigProperties properties, Collection<ContractMetadata> listOfFiles,
     					  String className, String classPackage, String includedDirectoryRelativePath)
    -}
    +}
    -
    -

    you can register your own implementation that generates a test. Again, it’s enough to provide -a proper spring.factories file. Example:

    -
    -
    org.springframework.cloud.contract.verifier.builder.SingleTestGenerator=/
    -com.example.MyGenerator
    -
    -
    -
    -
    -

    Custom stub generator

    -
    -

    If you want to generate stubs for other stub server than WireMock it’s enough to - plug in your own implementation of this interface:

    -
    -
    -
    -
    package org.springframework.cloud.contract.verifier.converter
    +
    you can register your own implementation that generates a test. Again, it's enough to provide
    +a proper `spring.factories` file. Example:
     
    -import groovy.transform.CompileStatic
    +[source]
    +
    +
    +
    +

    org.springframework.cloud.contract.verifier.builder.SingleTestGenerator=/ +com.example.MyGenerator

    +
    +
    +
    +
    ==== Custom stub generator
    +
    +If you want to generate stubs for other stub server than WireMock it's enough to
    + plug in your own implementation of this interface:
    +
    +[source,groovy]
    +
    +
    +
    +

    package org.springframework.cloud.contract.verifier.converter

    +
    +
    +

    import groovy.transform.CompileStatic import org.springframework.cloud.contract.spec.Contract -import org.springframework.cloud.contract.verifier.file.ContractMetadata - -/** +import org.springframework.cloud.contract.verifier.file.ContractMetadata

    +
    +
    +

    /** * Converts contracts into their stub representation. * * @since 1.1.0 */ @CompileStatic -interface StubGenerator { - - /** - * Returns {@code true} if the converter can handle the file to convert it into a stub. - */ - boolean canHandleFileName(String fileName) - - /** - * Returns the collection of converted contracts into stubs. One contract can - * result in multiple stubs. - */ - Map<Contract, String> convertContents(String rootName, ContractMetadata content) - - /** +interface StubGenerator {

    +
    +
    +
    +
    /**
    + * Returns {@code true} if the converter can handle the file to convert it into a stub.
    + */
    +boolean canHandleFileName(String fileName)
    +
    +
    +
    +
    +
    /**
    + * Returns the collection of converted contracts into stubs. One contract can
    + * result in multiple stubs.
    + */
    +Map<Contract, String> convertContents(String rootName, ContractMetadata content)
    +
    +
    +
    +
    +
    	/**
     	 * Returns the name of the converted stub file. If you have multiple contracts
     	 * in a single file then a prefix will be added to the generated file. If you
     	 * provide the {@link Contract#name} field then that field will override the
    @@ -8288,220 +8279,246 @@ interface StubGenerator {
     	 * converter will create two files {@code 0_foo.json} and {@code 1_foo.json}
     	 */
     	String generateOutputFileNameForInput(String inputFileName)
    -}
    +}
    -
    -

    you can register your own implementation that generate Stubs. Again, it’s enough to provide -a proper spring.factories file. Example:

    -
    -
    # Stub converters
    -org.springframework.cloud.contract.verifier.converter.StubGenerator=\
    -org.springframework.cloud.contract.verifier.wiremock.DslToWireMockClientConverter
    -
    -
    -
    -

    The default implementation is the WireMock stub generation.

    -
    -
    - - - - - -
    -
    Tip
    -
    -You can provide multiple stub generator implementations. That way for example from a single -DSL as input you can e.g. produce WireMock stubs and Pact files too! -
    -
    -
    -
    -

    Custom Stub Runner

    -
    -

    If you decide to have a custom stub generation you also need a custom way of running -stubs with your different stub provider.

    -
    -
    -

    Let us assume that you’re using Moco to build your stubs. -You wrote a proper stub generator and your stubs got placed in a JAR file.

    -
    -
    -

    In order for Stub Runner to know how to run your stubs you have to define a custom - HTTP Stub server implementation. It can look like this:

    -
    -
    -
    -
    package org.springframework.cloud.contract.stubrunner.provider.moco
    +
    you can register your own implementation that generate Stubs. Again, it's enough to provide
    +a proper `spring.factories` file. Example:
     
    -import com.github.dreamhead.moco.bootstrap.arg.HttpArgs
    +[source]
    +
    +
    +

    Stub converters

    +
    +

    org.springframework.cloud.contract.verifier.converter.StubGenerator=\ +org.springframework.cloud.contract.verifier.wiremock.DslToWireMockClientConverter

    +
    +
    +
    +
    The default implementation is the WireMock stub generation.
    +
    +TIP: You can provide multiple stub generator implementations. That way for example from a single
    +DSL as input you can e.g. produce WireMock stubs and Pact files too!
    +
    +==== Custom Stub Runner
    +
    +If you decide to have a custom stub generation you also need a custom way of running
    +stubs with your different stub provider.
    +
    +Let us assume that you're using https://github.com/dreamhead/moco[Moco] to build your stubs.
    +You wrote a proper stub generator and your stubs got placed in a JAR file.
    +
    +In order for Stub Runner to know how to run your stubs you have to define a custom
    + HTTP Stub server implementation. It can look like this:
    +
    +[source,groovy]
    +
    +
    +
    +

    package org.springframework.cloud.contract.stubrunner.provider.moco

    +
    +
    +

    import com.github.dreamhead.moco.bootstrap.arg.HttpArgs import com.github.dreamhead.moco.runner.JsonRunner import org.springframework.cloud.contract.stubrunner.HttpServerStub -import org.springframework.util.SocketUtils - -class MocoHttpServerStub implements HttpServerStub { - - private boolean started - private JsonRunner runner - private int port - - @Override - int port() { - if (!isRunning()) { - return -1 - } - return port +import org.springframework.util.SocketUtils

    +
    +
    +

    class MocoHttpServerStub implements HttpServerStub {

    +
    +
    +
    +
    private boolean started
    +private JsonRunner runner
    +private int port
    +
    +
    +
    +
    +
    @Override
    +int port() {
    +	if (!isRunning()) {
    +		return -1
     	}
    -
    -	@Override
    -	boolean isRunning() {
    -		return started
    -	}
    -
    -	@Override
    -	HttpServerStub start() {
    -		return start(SocketUtils.findAvailableTcpPort())
    -	}
    -
    -	@Override
    -	HttpServerStub start(int port) {
    -		this.port = port
    +	return port
    +}
    +
    +
    +
    +
    +
    @Override
    +boolean isRunning() {
    +	return started
    +}
    +
    +
    +
    +
    +
    @Override
    +HttpServerStub start() {
    +	return start(SocketUtils.findAvailableTcpPort())
    +}
    +
    +
    +
    +
    +
    @Override
    +HttpServerStub start(int port) {
    +	this.port = port
    +	return this
    +}
    +
    +
    +
    +
    +
    @Override
    +HttpServerStub stop() {
    +	if (!isRunning()) {
     		return this
     	}
    -
    -	@Override
    -	HttpServerStub stop() {
    -		if (!isRunning()) {
    -			return this
    -		}
    -		this.runner.stop()
    -		return this
    -	}
    -
    -	@Override
    -	HttpServerStub registerMappings(Collection<File> stubFiles) {
    -		List<InputStream> streams = stubFiles.collect { it.newInputStream() }
    -		this.runner = JsonRunner.newJsonRunnerWithStreams(streams,
    -				HttpArgs.httpArgs().withPort(this.port).build())
    -		this.runner.run()
    -		this.started = true
    -		return this
    -	}
    -
    -	@Override
    +	this.runner.stop()
    +	return this
    +}
    +
    +
    +
    +
    +
    @Override
    +HttpServerStub registerMappings(Collection<File> stubFiles) {
    +	List<InputStream> streams = stubFiles.collect { it.newInputStream() }
    +	this.runner = JsonRunner.newJsonRunnerWithStreams(streams,
    +			HttpArgs.httpArgs().withPort(this.port).build())
    +	this.runner.run()
    +	this.started = true
    +	return this
    +}
    +
    +
    +
    +
    +
    	@Override
     	boolean isAccepted(File file) {
     		return file.name.endsWith(".json")
     	}
    -}
    +}
    -
    -

    and just register it in your spring.factories file

    -
    -
    # Example of a custom HTTP Server Stub
    -org.springframework.cloud.contract.stubrunner.HttpServerStub=\
    -org.springframework.cloud.contract.stubrunner.provider.moco.MocoHttpServerStub
    -
    -
    -
    -

    that way you’ll be able to run stubs using Moco.

    -
    -
    - - - - - -
    -
    Important
    -
    -If you don’t provide any implementation then the default one - WireMock based -will be picked. If you provide more than one then the first one on the list will be picked. -
    -
    -
    -
    -

    Custom Stub Downloader

    -
    -

    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 json 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.

    -
    -
    -
    -
    package org.springframework.cloud.contract.stubrunner.provider.moco
    +
    and just register it in your `spring.factories` file
     
    -import org.springframework.cloud.contract.stubrunner.StubConfiguration
    +[source]
    +
    +
    +

    Example of a custom HTTP Server Stub

    +
    +

    org.springframework.cloud.contract.stubrunner.HttpServerStub=\ +org.springframework.cloud.contract.stubrunner.provider.moco.MocoHttpServerStub

    +
    +
    +
    +
    that way you'll be able to run stubs using Moco.
    +
    +IMPORTANT: If you don't provide any implementation then the default one - WireMock based
    +will be picked. If you provide more than one then the first one on the list will be picked.
    +
    +==== Custom Stub Downloader
    +
    +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 `json` 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.
    +
    +[source,java]
    +
    +
    +
    +

    package org.springframework.cloud.contract.stubrunner.provider.moco

    +
    +
    +

    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 +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 - - @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()) - } - return new StubDownloader() { - @Override - public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar( - StubConfiguration stubConfiguration) { - return new AbstractMap.SimpleEntry(configuration, tmp) - } - } - } catch (IOException e) { - throw new IllegalStateException(e) +class ClasspathStubProvider implements StubDownloaderBuilder {

    +
    +
    +
    +
    private static final int TEMP_DIR_ATTEMPTS = 10000
    +
    +
    +
    +
    +
    @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())
     		}
    -	}
    -
    -	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"
    +		return new StubDownloader() {
    +			@Override
    +			public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
    +					StubConfiguration stubConfiguration) {
    +				return new AbstractMap.SimpleEntry(configuration, tmp)
    +			}
     		}
    +	} catch (IOException e) {
    +		throw new IllegalStateException(e)
     	}
    -
    -	private String separatedArtifact(StubConfiguration configuration) {
    -		return configuration.getGroupId().replace(".", File.separator) +
    -				File.separator + configuration.getArtifactId()
    +}
    +
    +
    +
    +
    +
    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"
     	}
    -
    -	// Taken from Guava
    +}
    +
    +
    +
    +
    +
    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() + "-"
    @@ -8515,71 +8532,40 @@ class ClasspathStubProvider implements StubDownloaderBuilder {
     				"Failed to create directory within " + TEMP_DIR_ATTEMPTS + " attempts (tried " + baseName + "0 to " + baseName + (
     						TEMP_DIR_ATTEMPTS - 1) + ")")
     	}
    -}
    +}
    -
    -

    and just register it in your spring.factories file

    -
    -
    # Example of a custom Stub Downloader Provider
    -org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder=\
    -org.springframework.cloud.contract.stubrunner.provider.moco.ClasspathStubProvider
    +
    and just register it in your `spring.factories` file
    +
    +[source]
    +

    Example of a custom Stub Downloader Provider

    -

    that way you’ll be able to pick a folder with the source of your stubs.

    +

    org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder=\ +org.springframework.cloud.contract.stubrunner.provider.moco.ClasspathStubProvider

    -
    - - - - - -
    -
    Important
    -
    -If you don’t provide any implementation then the default one - Aether based that will download stubs from a remote repo +
    +
    +
    that way you'll be able to pick a folder with the source of your stubs.
    +
    +IMPORTANT: 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.
    -
    -
    -
    - -
    - -
    -

    Here you can find interesting links related to Spring Cloud Contract Verifier:

    -
    - -
    + +=== Links + +Here you can find interesting links related to Spring Cloud Contract Verifier: + +- https://github.com/spring-cloud/spring-cloud-contract/[Spring Cloud Contract Github Repository] +- https://github.com/spring-cloud-samples/spring-cloud-contract-samples/[Spring Cloud Contract Samples] +- https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html[Spring Cloud Contract Documentation] +- https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html/deprecated[Accurest Legacy Documentation] +- https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html/#spring-cloud-contract-stub-runner[Spring Cloud Contract Stub Runner Documentation] +- https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract.html/#stub-runner-for-messaging[Spring Cloud Contract Stub Runner Messaging Documentation] +- https://gitter.im/spring-cloud/spring-cloud-contract[Spring Cloud Contract Gitter] +- https://cloud.spring.io/spring-cloud-contract/spring-cloud-contract-maven-plugin/[Spring Cloud Contract Maven Plugin]