From 6ed344e976f9ccb21e858476c4e36e26c86dbf61 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 27 Dec 2017 13:15:14 +0100 Subject: [PATCH 01/14] Added WireMockConfigurationCustomizer with this change it's possible to customize the WireMockConfiguration set up by Spring Cloud Contract fixes gh-502 --- .../main/asciidoc/spring-cloud-wiremock.adoc | 13 +++++ .../wiremock/WireMockConfiguration.java | 6 ++ .../WireMockConfigurationCustomizer.java | 12 ++++ ...eWireMockConfigurationCustomizerTests.java | 58 +++++++++++++++++++ .../wiremock/WiremockTestsApplication.java | 4 ++ 5 files changed, 93 insertions(+) create mode 100644 spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockConfigurationCustomizer.java create mode 100644 spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockConfigurationCustomizerTests.java diff --git a/docs/src/main/asciidoc/spring-cloud-wiremock.adoc b/docs/src/main/asciidoc/spring-cloud-wiremock.adoc index 8630a1bca3..49069facb7 100644 --- a/docs/src/main/asciidoc/spring-cloud-wiremock.adoc +++ b/docs/src/main/asciidoc/spring-cloud-wiremock.adoc @@ -160,6 +160,19 @@ Spring Boot embedded servers, and Wiremock itself has "native" support for a par version of Jetty (currently 9.2). To use the native Jetty, you need to add the native Wiremock dependencies and exclude the Spring Boot container (if there is one). +=== Customization of WireMock configuration + +You can register a bean of `org.springframework.cloud.contract.wiremock.WireMockConfigurationCustomizer` type +in order to customize the WireMock configuration (e.g. add custom transformers). +Example: + +[source,java,indent=0] +---- +include::{wiremock_tests}/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockConfigurationCustomizerTests.java[tags=customizer_1] +// perform your customization here +include::{wiremock_tests}/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockConfigurationCustomizerTests.java[tags=customizer_2] +---- + === Generating Stubs using REST Docs https://projects.spring.io/spring-restdocs[Spring REST Docs] can be used to generate diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockConfiguration.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockConfiguration.java index 93271f7b3e..cbc51c365c 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockConfiguration.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockConfiguration.java @@ -62,6 +62,9 @@ public class WireMockConfiguration implements SmartLifecycle { @Autowired(required = false) private Options options; + @Autowired(required = false) + private WireMockConfigurationCustomizer customizer; + @Autowired private DefaultListableBeanFactory beanFactory; @@ -84,6 +87,9 @@ public class WireMockConfiguration implements SmartLifecycle { registerFiles(factory); factory.notifier(new Slf4jNotifier(true)); this.options = factory; + if (this.customizer != null) { + this.customizer.customize(factory); + } } this.server = new WireMockServer(this.options); registerStubs(); diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockConfigurationCustomizer.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockConfigurationCustomizer.java new file mode 100644 index 0000000000..709e0405d8 --- /dev/null +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/WireMockConfigurationCustomizer.java @@ -0,0 +1,12 @@ +package org.springframework.cloud.contract.wiremock; + +/** + * Allows customization of {@link com.github.tomakehurst.wiremock.core.WireMockConfiguration} + * + * @author Marcin Grzejszczak + * @since 1.2.2 + */ +public interface WireMockConfigurationCustomizer { + + void customize(com.github.tomakehurst.wiremock.core.WireMockConfiguration config); +} diff --git a/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockConfigurationCustomizerTests.java b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockConfigurationCustomizerTests.java new file mode 100644 index 0000000000..b79c65f012 --- /dev/null +++ b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockConfigurationCustomizerTests.java @@ -0,0 +1,58 @@ +package org.springframework.cloud.contract.wiremock; + +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes={WiremockTestsApplication.class, + AutoConfigureWireMockConfigurationCustomizerTests.Config.class}, + properties="app.baseUrl=http://localhost:${wiremock.server.port}", + webEnvironment=WebEnvironment.NONE) +@DirtiesContext +@AutoConfigureWireMock(port=0, stubs="file:src/test/resources/io.stubs/mappings") +public class AutoConfigureWireMockConfigurationCustomizerTests { + + @Autowired + private Service service; + @Autowired + private Config config; + + @Test + public void contextLoads() throws Exception { + assertThat(this.service.go()).isEqualTo("Hello World"); + assertThat(this.config.isExecuted()).isTrue(); + } + + @Configuration + protected static class Config { + + boolean executed = false; + + // tag::customizer_1[] + @Bean WireMockConfigurationCustomizer optionsCustomizer() { + return new WireMockConfigurationCustomizer() { + @Override public void customize(WireMockConfiguration options) { + // end::customizer_1[] + assertThat(options.portNumber()).isGreaterThan(0); + Config.this.executed = true; + // tag::customizer_2[] + } + }; + } + // end::customizer_2[] + + public boolean isExecuted() { + return executed; + } + } +} diff --git a/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/WiremockTestsApplication.java b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/WiremockTestsApplication.java index 4c9740c839..df1a184074 100644 --- a/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/WiremockTestsApplication.java +++ b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/WiremockTestsApplication.java @@ -57,4 +57,8 @@ class Service { public String go() { return this.restTemplate.getForEntity(this.base + "/test", String.class).getBody(); } + + public void setBase(String base) { + this.base = base; + } } From 6e65f36c3140bebfc66f7a53840b68adf3a40b02 Mon Sep 17 00:00:00 2001 From: Tom Hombergs Date: Thu, 11 Jan 2018 10:18:37 +0100 Subject: [PATCH 02/14] replaced ".class" with ".getClass()" (#513) ... so that the actual class of the object is returned instead of potentially an element with key "class" if object is a Map --- .../verifier/builder/MethodBodyBuilder.groovy | 2 +- .../builder/MethodBodyBuilderSpec.groovy | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy index f77f2ea466..8b5e4bc016 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilder.groovy @@ -468,7 +468,7 @@ abstract class MethodBodyBuilder { // we want to make the type more generic (e.g. not ArrayList but List) protected Class classToCheck(Object elementFromBody) { - switch (elementFromBody.class) { + switch (elementFromBody.getClass()) { case List: return List case Set: diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy index 7c48bfb88e..37d9a2038b 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBodyBuilderSpec.groovy @@ -487,4 +487,35 @@ DocumentContext parsedJson = JsonPath.parse(json); "JaxRsClientJUnitMethodBodyBuilder" | { Contract dsl -> new JaxRsClientJUnitMethodBodyBuilder(dsl, properties) } } + @Issue("#509") + def "classToCheck() should return class of object"() { + given: + Contract contractDsl = Contract.make { + request { + method 'POST' + url '/api/users' + } + response { + status 200 + } + } + MethodBodyBuilder builder = methodBuilder(contractDsl) + when: + Map map = new LinkedHashMap<>() + Integer number = Integer.valueOf(42) + List list = new ArrayList<>() + Set set = new HashSet<>() + then: + builder.classToCheck(map) == Map.class + and: + builder.classToCheck(number) == Integer.class + and: + builder.classToCheck(list) == List.class + and: + builder.classToCheck(set) == Set.class + where: + methodBuilderName | methodBuilder + "MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } + } + } From 709df3e291f2b6088365d70aff15b16f04a95308 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Fri, 12 Jan 2018 12:56:09 +0100 Subject: [PATCH 03/14] Unescaping any json body entries without this change we're escaping the json body response and then wiremock is escaping it again with this change we're unescaping the first json body escaping so that wiremock applies it only once fixes gh-515 --- .../contracts/com/example/server/pom.xml | 2 +- samples/standalone/dsl/http-client/pom.xml | 2 +- samples/standalone/dsl/http-server/pom.xml | 2 +- .../standalone/messaging/stream-sink/pom.xml | 2 +- .../messaging/stream-source/pom.xml | 2 +- .../standalone/pact/pact-http-client/pom.xml | 2 +- .../standalone/pact/pact-http-server/pom.xml | 2 +- .../standalone/restdocs/http-client/pom.xml | 2 +- .../standalone/restdocs/http-server/pom.xml | 2 +- samples/wiremock-jetty/pom.xml | 2 +- samples/wiremock-native/pom.xml | 2 +- samples/wiremock-tomcat/pom.xml | 2 +- samples/wiremock-undertow-ssl/pom.xml | 2 +- samples/wiremock-undertow/pom.xml | 2 +- .../DslToWireMockClientConverterSpec.groovy | 348 +++++++++++------- .../wiremock/BaseWireMockStubStrategy.groovy | 3 +- .../dsl/wiremock/WireMockStubStrategy.groovy | 1 - 17 files changed, 239 insertions(+), 141 deletions(-) diff --git a/samples/standalone/contracts/com/example/server/pom.xml b/samples/standalone/contracts/com/example/server/pom.xml index 9df3c430cf..755c0832fd 100644 --- a/samples/standalone/contracts/com/example/server/pom.xml +++ b/samples/standalone/contracts/com/example/server/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.8.RELEASE + 1.5.9.RELEASE diff --git a/samples/standalone/dsl/http-client/pom.xml b/samples/standalone/dsl/http-client/pom.xml index 20d4bd48f9..caf1dc1ec3 100644 --- a/samples/standalone/dsl/http-client/pom.xml +++ b/samples/standalone/dsl/http-client/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.8.RELEASE + 1.5.9.RELEASE diff --git a/samples/standalone/dsl/http-server/pom.xml b/samples/standalone/dsl/http-server/pom.xml index 222b5b7c73..a9b142a766 100644 --- a/samples/standalone/dsl/http-server/pom.xml +++ b/samples/standalone/dsl/http-server/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.8.RELEASE + 1.5.9.RELEASE diff --git a/samples/standalone/messaging/stream-sink/pom.xml b/samples/standalone/messaging/stream-sink/pom.xml index 73c3e5d34c..e51b7e50b0 100644 --- a/samples/standalone/messaging/stream-sink/pom.xml +++ b/samples/standalone/messaging/stream-sink/pom.xml @@ -14,7 +14,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.8.RELEASE + 1.5.9.RELEASE diff --git a/samples/standalone/messaging/stream-source/pom.xml b/samples/standalone/messaging/stream-source/pom.xml index a84336623b..e4d0bc5e7d 100644 --- a/samples/standalone/messaging/stream-source/pom.xml +++ b/samples/standalone/messaging/stream-source/pom.xml @@ -14,7 +14,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.8.RELEASE + 1.5.9.RELEASE diff --git a/samples/standalone/pact/pact-http-client/pom.xml b/samples/standalone/pact/pact-http-client/pom.xml index 7811daf6bd..d41415837e 100644 --- a/samples/standalone/pact/pact-http-client/pom.xml +++ b/samples/standalone/pact/pact-http-client/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.8.RELEASE + 1.5.9.RELEASE diff --git a/samples/standalone/pact/pact-http-server/pom.xml b/samples/standalone/pact/pact-http-server/pom.xml index d0d8769c63..c1f7757c22 100644 --- a/samples/standalone/pact/pact-http-server/pom.xml +++ b/samples/standalone/pact/pact-http-server/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.8.RELEASE + 1.5.9.RELEASE diff --git a/samples/standalone/restdocs/http-client/pom.xml b/samples/standalone/restdocs/http-client/pom.xml index 20fdc253a2..8d49ee2ae4 100644 --- a/samples/standalone/restdocs/http-client/pom.xml +++ b/samples/standalone/restdocs/http-client/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.8.RELEASE + 1.5.9.RELEASE diff --git a/samples/standalone/restdocs/http-server/pom.xml b/samples/standalone/restdocs/http-server/pom.xml index ac17ca768c..30d397def8 100644 --- a/samples/standalone/restdocs/http-server/pom.xml +++ b/samples/standalone/restdocs/http-server/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.8.RELEASE + 1.5.9.RELEASE diff --git a/samples/wiremock-jetty/pom.xml b/samples/wiremock-jetty/pom.xml index 93b783e9cf..73d9cc3b16 100644 --- a/samples/wiremock-jetty/pom.xml +++ b/samples/wiremock-jetty/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.8.RELEASE + 1.5.9.RELEASE diff --git a/samples/wiremock-native/pom.xml b/samples/wiremock-native/pom.xml index e75600c5b1..585eed522c 100644 --- a/samples/wiremock-native/pom.xml +++ b/samples/wiremock-native/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.8.RELEASE + 1.5.9.RELEASE diff --git a/samples/wiremock-tomcat/pom.xml b/samples/wiremock-tomcat/pom.xml index bddaebc37b..f6a56b68e9 100644 --- a/samples/wiremock-tomcat/pom.xml +++ b/samples/wiremock-tomcat/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.8.RELEASE + 1.5.9.RELEASE diff --git a/samples/wiremock-undertow-ssl/pom.xml b/samples/wiremock-undertow-ssl/pom.xml index 9634487f65..1593507618 100644 --- a/samples/wiremock-undertow-ssl/pom.xml +++ b/samples/wiremock-undertow-ssl/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.8.RELEASE + 1.5.9.RELEASE diff --git a/samples/wiremock-undertow/pom.xml b/samples/wiremock-undertow/pom.xml index db5cb533a4..c226c4646d 100644 --- a/samples/wiremock-undertow/pom.xml +++ b/samples/wiremock-undertow/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.8.RELEASE + 1.5.9.RELEASE diff --git a/spring-cloud-contract-tools/spring-cloud-contract-converters/src/test/groovy/org/springframework/cloud/contract/verifier/wiremock/DslToWireMockClientConverterSpec.groovy b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/test/groovy/org/springframework/cloud/contract/verifier/wiremock/DslToWireMockClientConverterSpec.groovy index dc56aebd3b..7a8dbccb4a 100755 --- a/spring-cloud-contract-tools/spring-cloud-contract-converters/src/test/groovy/org/springframework/cloud/contract/verifier/wiremock/DslToWireMockClientConverterSpec.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/test/groovy/org/springframework/cloud/contract/verifier/wiremock/DslToWireMockClientConverterSpec.groovy @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -85,18 +85,18 @@ class DslToWireMockClientConverterSpec extends Specification { File file = tmpFolder.newFile("dsl1_list.groovy") file.write(''' (1..2).collect { int index -> - org.springframework.cloud.contract.spec.Contract.make { - request { - method(PUT()) - headers { - contentType(applicationJson()) - } - url "/${index}" - } - response { - status 200 - } - } + org.springframework.cloud.contract.spec.Contract.make { + request { + method(PUT()) + headers { + contentType(applicationJson()) + } + url "/${index}" + } + response { + status 200 + } + } } ''') when: @@ -242,40 +242,40 @@ class DslToWireMockClientConverterSpec extends Specification { { "request" : { "url" : "/api/12", - "method" : "PUT", - "bodyPatterns" : [ { - "matchesJsonPath" : "$[*].['place'].['bounding_box'].['coordinates'][*][*][?(@ == -77.119759)]" - }, { - "matchesJsonPath" : "$[*][?(@.['text'] == 'Gonna see you at Warsaw')]" - }, { - "matchesJsonPath" : "$[*].['place'][?(@.['place_type'] == 'city')]" - }, { - "matchesJsonPath" : "$[*][?(@.['id'] == 492967299297845248)]" - }, { - "matchesJsonPath" : "$[*].['place'].['bounding_box'].['coordinates'][*][*][?(@ == 38.791645)]" - }, { - "matchesJsonPath" : "$[*].['place'][?(@.['country'] == 'United States')]" - }, { - "matchesJsonPath" : "$[*][?(@.['id_str'] == '492967299297845248')]" - }, { - "matchesJsonPath" : "$[*].['place'].['bounding_box'].['coordinates'][*][*][?(@ == -76.909393)]" - }, { - "matchesJsonPath" : "$[*].['place'][?(@.['name'] == 'Washington')]" - }, { - "matchesJsonPath" : "$[*].['place'].['bounding_box'][?(@.['type'] == 'Polygon')]" - }, { - "matchesJsonPath" : "$[*].['place'][?(@.['url'] == 'http://api.twitter.com/1/geo/id/01fbe706f872cb32.json')]" - }, { - "matchesJsonPath" : "$[*].['place'].['bounding_box'].['coordinates'][*][*][?(@ == 38.995548)]" - }, { - "matchesJsonPath" : "$[*].['place'][?(@.['country_code'] == 'US')]" - }, { - "matchesJsonPath" : "$[*].['place'][?(@.['full_name'] == 'Washington, DC')]" - }, { - "matchesJsonPath" : "$[*][?(@.['created_at'] == 'Sat Jul 26 09:38:57 +0000 2014')]" - }, { - "matchesJsonPath" : "$[*].['place'][?(@.['id'] == '01fbe706f872cb32')]" - } ], + "method" : "PUT", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[*].['place'].['bounding_box'].['coordinates'][*][*][?(@ == -77.119759)]" + }, { + "matchesJsonPath" : "$[*][?(@.['text'] == 'Gonna see you at Warsaw')]" + }, { + "matchesJsonPath" : "$[*].['place'][?(@.['place_type'] == 'city')]" + }, { + "matchesJsonPath" : "$[*][?(@.['id'] == 492967299297845248)]" + }, { + "matchesJsonPath" : "$[*].['place'].['bounding_box'].['coordinates'][*][*][?(@ == 38.791645)]" + }, { + "matchesJsonPath" : "$[*].['place'][?(@.['country'] == 'United States')]" + }, { + "matchesJsonPath" : "$[*][?(@.['id_str'] == '492967299297845248')]" + }, { + "matchesJsonPath" : "$[*].['place'].['bounding_box'].['coordinates'][*][*][?(@ == -76.909393)]" + }, { + "matchesJsonPath" : "$[*].['place'][?(@.['name'] == 'Washington')]" + }, { + "matchesJsonPath" : "$[*].['place'].['bounding_box'][?(@.['type'] == 'Polygon')]" + }, { + "matchesJsonPath" : "$[*].['place'][?(@.['url'] == 'http://api.twitter.com/1/geo/id/01fbe706f872cb32.json')]" + }, { + "matchesJsonPath" : "$[*].['place'].['bounding_box'].['coordinates'][*][*][?(@ == 38.995548)]" + }, { + "matchesJsonPath" : "$[*].['place'][?(@.['country_code'] == 'US')]" + }, { + "matchesJsonPath" : "$[*].['place'][?(@.['full_name'] == 'Washington, DC')]" + }, { + "matchesJsonPath" : "$[*][?(@.['created_at'] == 'Sat Jul 26 09:38:57 +0000 2014')]" + }, { + "matchesJsonPath" : "$[*].['place'][?(@.['id'] == '01fbe706f872cb32')]" + } ], "headers" : { "Content-Type" : { "equalTo" : "application/vnd.org.springframework.cloud.contract.verifier.twitter-places-analyzer.v1+json" @@ -334,22 +334,22 @@ class DslToWireMockClientConverterSpec extends Specification { file.write(""" org.springframework.cloud.contract.spec.Contract.make { request { - method 'GET' - urlPath '/foos' - } - response { - status 200 - body([[id: value( - consumer('123'), - producer(regex('[0-9]+')) - )], [id: value( - consumer('567'), - producer(regex('[0-9]+')) - )]]) - headers { + method 'GET' + urlPath '/foos' + } + response { + status 200 + body([[id: value( + consumer('123'), + producer(regex('[0-9]+')) + )], [id: value( + consumer('567'), + producer(regex('[0-9]+')) + )]]) + headers { header 'Content-Type': 'application/json' } - } + } } """) when: @@ -379,10 +379,10 @@ class DslToWireMockClientConverterSpec extends Specification { file.write(""" org.springframework.cloud.contract.spec.Contract.make { request { - method 'GET' - urlPath '/foos' - } - response { + method 'GET' + urlPath '/foos' + } + response { status 200 body( digit: \$(producer(regex('[0-9]{1}'))), @@ -446,25 +446,25 @@ class DslToWireMockClientConverterSpec extends Specification { ''' { "request" : { - "url" : "/users/password", - "method" : "POST", - "bodyPatterns" : [ { - "matchesJsonPath" : "$[?(@.['email'] =~ /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,6})?/)]" - }, { - "matchesJsonPath" : "$[?(@.['callback_url'] =~ /((http[s]?|ftp):\\\\/)\\\\/?([^:\\\\/\\\\s]+)(:[0-9]{1,5})?/)]" - } ], - "headers" : { - "Content-Type" : { - "equalTo" : "application/json" - } - } + "url" : "/users/password", + "method" : "POST", + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.['email'] =~ /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,6})?/)]" + }, { + "matchesJsonPath" : "$[?(@.['callback_url'] =~ /((http[s]?|ftp):\\\\/)\\\\/?([^:\\\\/\\\\s]+)(:[0-9]{1,5})?/)]" + } ], + "headers" : { + "Content-Type" : { + "equalTo" : "application/json" + } + } }, "response" : { - "status" : 404, - "body" : "{\\"code\\":\\"123123\\",\\"message\\":\\"User not found by email == [not.existing@user.com]\\"}", - "headers" : { - "Content-Type" : "application/json" - } + "status" : 404, + "body" : "{\\"code\\":\\"123123\\",\\"message\\":\\"User not found by email == [not.existing@user.com]\\"}", + "headers" : { + "Content-Type" : "application/json" + } }, "priority" : 1 } @@ -601,51 +601,51 @@ class DslToWireMockClientConverterSpec extends Specification { ''' { "request" : { - "urlPath" : "/get", - "method" : "POST", - "headers" : { - "Content-Type" : { - "matches" : "application/json.*" - } - }, - "bodyPatterns" : [ { - "matchesJsonPath" : "$[?(@.['valueWithoutAMatcher'] == 'foo')]" - }, { - "matchesJsonPath" : "$[?(@.['valueWithTypeMatch'] == 'string')]" - }, { - "matchesJsonPath" : "$.['list'].['some'].['nested'][?(@.['anothervalue'] == 4)]" - }, { - "matchesJsonPath" : "$.['list'].['someother'].['nested'][?(@.['anothervalue'] == 4)]" - }, { - "matchesJsonPath" : "$.['list'].['someother'].['nested'][?(@.['json'] == 'with value')]" - }, { - "matchesJsonPath" : "$[?(@.duck =~ /([0-9]{3})/)]" - }, { - "matchesJsonPath" : "$[?(@.duck == 123)]" - }, { - "matchesJsonPath" : "$[?(@.alpha =~ /([\\\\p{L}]*)/)]" - }, { - "matchesJsonPath" : "$[?(@.alpha == 'abc')]" - }, { - "matchesJsonPath" : "$[?(@.number =~ /(-?(\\\\d*\\\\.\\\\d+|\\\\d+))/)]" - }, { - "matchesJsonPath" : "$[?(@.aBoolean =~ /((true|false))/)]" - }, { - "matchesJsonPath" : "$[?(@.date =~ /((\\\\d\\\\d\\\\d\\\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]))/)]" - }, { - "matchesJsonPath" : "$[?(@.dateTime =~ /(([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]" - }, { - "matchesJsonPath" : "$[?(@.time =~ /((2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]" - }, { - "matchesJsonPath" : "$.list.some.nested[?(@.json =~ /(.*)/)]" - } ] + "urlPath" : "/get", + "method" : "POST", + "headers" : { + "Content-Type" : { + "matches" : "application/json.*" + } + }, + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.['valueWithoutAMatcher'] == 'foo')]" + }, { + "matchesJsonPath" : "$[?(@.['valueWithTypeMatch'] == 'string')]" + }, { + "matchesJsonPath" : "$.['list'].['some'].['nested'][?(@.['anothervalue'] == 4)]" + }, { + "matchesJsonPath" : "$.['list'].['someother'].['nested'][?(@.['anothervalue'] == 4)]" + }, { + "matchesJsonPath" : "$.['list'].['someother'].['nested'][?(@.['json'] == 'with value')]" + }, { + "matchesJsonPath" : "$[?(@.duck =~ /([0-9]{3})/)]" + }, { + "matchesJsonPath" : "$[?(@.duck == 123)]" + }, { + "matchesJsonPath" : "$[?(@.alpha =~ /([\\\\p{L}]*)/)]" + }, { + "matchesJsonPath" : "$[?(@.alpha == 'abc')]" + }, { + "matchesJsonPath" : "$[?(@.number =~ /(-?(\\\\d*\\\\.\\\\d+|\\\\d+))/)]" + }, { + "matchesJsonPath" : "$[?(@.aBoolean =~ /((true|false))/)]" + }, { + "matchesJsonPath" : "$[?(@.date =~ /((\\\\d\\\\d\\\\d\\\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]))/)]" + }, { + "matchesJsonPath" : "$[?(@.dateTime =~ /(([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]" + }, { + "matchesJsonPath" : "$[?(@.time =~ /((2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]" + }, { + "matchesJsonPath" : "$.list.some.nested[?(@.json =~ /(.*)/)]" + } ] }, "response" : { - "status" : 200, - "body" : "{\\"date\\":\\"2017-01-01\\",\\"dateTime\\":\\"2017-01-01T01:23:45\\",\\"number\\":123,\\"aBoolean\\":true,\\"duck\\":123,\\"alpha\\":\\"abc\\",\\"valueWithMin\\":[1,2,3],\\"time\\":\\"01:02:34\\",\\"valueWithTypeMatch\\":\\"string\\",\\"valueWithMax\\":[1,2,3],\\"valueWithMinMax\\":[1,2,3],\\"valueWithoutAMatcher\\":\\"foo\\"}", - "headers" : { - "Content-Type" : "application/json" - } + "status" : 200, + "body" : "{\\"date\\":\\"2017-01-01\\",\\"dateTime\\":\\"2017-01-01T01:23:45\\",\\"number\\":123,\\"aBoolean\\":true,\\"duck\\":123,\\"alpha\\":\\"abc\\",\\"valueWithMin\\":[1,2,3],\\"time\\":\\"01:02:34\\",\\"valueWithTypeMatch\\":\\"string\\",\\"valueWithMax\\":[1,2,3],\\"valueWithMinMax\\":[1,2,3],\\"valueWithoutAMatcher\\":\\"foo\\"}", + "headers" : { + "Content-Type" : "application/json" + } } } ''' @@ -793,6 +793,104 @@ class DslToWireMockClientConverterSpec extends Specification { JSONAssert.assertEquals('''{"code":"123123","message":"User not found by email == [not.existing@user.com]"}"''', response.body, false) } + @Issue("#515") + def 'should not escape any java chars in the javascript WireMock stub'() { + given: + def converter = new DslToWireMockClientConverter() + and: + File file = tmpFolder.newFile("dsl_from_docs.groovy") + file.write(''' + org.springframework.cloud.contract.spec.Contract.make { + priority 1 + request { + method 'POST' + url '/users/password2' + headers { + header 'Content-Type': 'application/json' + } + body( + email: 'abc@abc.com', + callback_url: 'http://partners.com' + ) + stubMatchers { + jsonPath('$.[\\'email\\']', byRegex(email())) + jsonPath('$.[\\'callback_url\\']', byRegex(hostname())) + } + } + response { + status 400 + headers { + header 'CorrelationID': '11111111-1111-1111-1111-111111111111\' + header 'Content-Type': value(test(regex('application/json(;.*)?')), stub('application/json;charset=UTF-8')) + } + body( + [ + subject: [ + '@type' :'ErrorSubject', + 'oid' :'8.2', + 'description':'Profile' + ], + reason : [ + '@type' :'ErrorReason', + 'oid' :'3.7', + 'description':'Bad Request', + 'httpCode':'400' + ], + message: '[8.2 Profile/3.7 Bad Request]\' + ] + ) + } + } + ''') + when: + String json = converter.convertContents("Test", new ContractMetadata(file.toPath(), false, 0, null, + ContractVerifierDslConverter.convertAsCollection(new File("/"),file))).values().first() + then: + JSONAssert.assertEquals( + ''' + { + "request" : { + "url" : "/users/password2", + "method" : "POST", + "headers" : { + "Content-Type" : { + "equalTo" : "application/json" + } + }, + "bodyPatterns" : [ { + "matchesJsonPath" : "$[?(@.['email'] =~ /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,6})/)]" + }, { + "matchesJsonPath" : "$[?(@.['callback_url'] =~ /(((http[s]?|ftp):\\\\/)\\\\/?([^:\\\\/\\\\s]+)(:[0-9]{1,5})?)/)]" + } ] + }, + "response" : { + "status" : 400, + "body" : "{\\"reason\\":{\\"@type\\":\\"ErrorReason\\",\\"description\\":\\"Bad Request\\",\\"oid\\":\\"3.7\\",\\"httpCode\\":\\"400\\"},\\"subject\\":{\\"@type\\":\\"ErrorSubject\\",\\"description\\":\\"Profile\\",\\"oid\\":\\"8.2\\"},\\"message\\":\\"[8.2 Profile/3.7 Bad Request]\\"}", + "headers" : { + "CorrelationID" : "11111111-1111-1111-1111-111111111111", + "Content-Type" : "application/json;charset=UTF-8" + }, + "transformers" : [ "response-template" ] + }, + "priority" : 1 + } + } + ''' + , json, false) + and: + StubMapping mapping = stubMappingIsValidWireMockStub(json) + and: + wireMockRule.addStubMapping(mapping) + and: + def response = restTemplate.exchange(RequestEntity.post("${url}/users/password2".toURI()) + .header("Content-Type", "application/json") + .body('''{"email":"abc@abc.com", "callback_url":"http://partners.com"}''') + , String) + response.headers.get('Content-Type') == ['application/json;charset=UTF-8'] + response.statusCodeValue == 400 + JSONAssert.assertEquals('''{"message":"[8.2 Profile/3.7 Bad Request]"}"''', response.body, false) + } + StubMapping stubMappingIsValidWireMockStub(String mappingDefinition) { StubMapping stubMapping = WireMockStubMapping.buildFrom(mappingDefinition) stubMapping.request.bodyPatterns.findAll { it.isPresent() && it instanceof RegexPattern }.every { diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/BaseWireMockStubStrategy.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/BaseWireMockStubStrategy.groovy index d7aecf5570..c95f1c0d6b 100755 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/BaseWireMockStubStrategy.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/BaseWireMockStubStrategy.groovy @@ -103,7 +103,8 @@ abstract class BaseWireMockStubStrategy { // the space is important cause at the end of the json body you also have a } // you can't have 4 } next to each other String unquotedJson = json.replace('"' + WRAPPER, '').replace(WRAPPER + '"', ' ') - return parseBody(unquotedJson, contentType) + String unescapedJson = unquotedJson.replace("\\/", "/") + return parseBody(unescapedJson, contentType) } private Object processEntriesForTemplating(transformedMap, DocumentContext context) { diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockStubStrategy.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockStubStrategy.groovy index 49f9b8e409..a55941eae4 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockStubStrategy.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockStubStrategy.groovy @@ -82,7 +82,6 @@ class WireMockStubStrategy { stubMapping.newScenarioState = STEP_PREFIX + (contract.order + 1) } } - return StubMapping.buildJsonStringFor(stubMapping) } } \ No newline at end of file From bea0d4f78d44150f7348623b21b4afae51ab50b5 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Fri, 12 Jan 2018 15:24:15 +0100 Subject: [PATCH 04/14] Added pact evaluation of `$.body` type matcher fixes #511 --- .../spec/pact/PactContractConverter.groovy | 25 +++++++++-- .../pact/PactContractConverterSpec.groovy | 33 ++++++++++++++ .../src/test/resources/pact/pact_509.json | 44 +++++++++++++++++++ .../util/DelegatingJsonVerifiable.java | 28 ++++++++---- .../FinishedDelegatingJsonVerifiable.java | 12 +++++ .../util/MethodBufferingJsonVerifiable.java | 2 + 6 files changed, 132 insertions(+), 12 deletions(-) create mode 100644 spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/test/resources/pact/pact_509.json diff --git a/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/main/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverter.groovy b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/main/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverter.groovy index 2060abdd65..173b70f901 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/main/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverter.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/main/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverter.groovy @@ -21,8 +21,9 @@ import org.springframework.cloud.contract.spec.internal.ExecutionProperty import org.springframework.cloud.contract.spec.internal.Headers import org.springframework.cloud.contract.spec.internal.MatchingType import org.springframework.cloud.contract.spec.internal.QueryParameters +import org.springframework.cloud.contract.verifier.util.JsonPaths +import org.springframework.cloud.contract.verifier.util.JsonToJsonPathsConverter import org.springframework.cloud.contract.verifier.util.MapConverter - /** * Converter of JSON PACT file * @@ -36,6 +37,7 @@ class PactContractConverter implements ContractConverter { private static final String REGEX_KEY = "regex" private static final String MAX_KEY = "max" private static final String MIN_KEY = "min" + private static final String FULL_BODY = '$.body' @Override boolean isAccepted(File file) { @@ -90,7 +92,8 @@ class PactContractConverter implements ContractConverter { } if (requestResponseInteraction.request?.matchingRules) { stubMatchers { - requestResponseInteraction.request.matchingRules.each { String key, Map value -> + Map> rules = requestResponseInteraction.request.matchingRules + rules.each { String key, Map value -> String keyFromBody = toKeyStartingFromBody(key) if (value.containsKey(MATCH_KEY)) { MatchingType matchingType = MatchingType.valueOf((value.get(MATCH_KEY) as String).toUpperCase()) @@ -132,8 +135,19 @@ class PactContractConverter implements ContractConverter { } if (requestResponseInteraction.response?.matchingRules) { testMatchers { - requestResponseInteraction.response.matchingRules.each { String key, Map value -> + Map> rules = requestResponseInteraction.response.matchingRules + Map fullBodyCheck = rules.get(FULL_BODY) + if (fullBodyCheck != null) { + JsonPaths jsonPaths = JsonToJsonPathsConverter.transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck(requestResponseInteraction.request?.body?.value) + jsonPaths.each { + jsonPath(it.keyBeforeChecking(), byType()) + } + } + rules.each { String key, Map value -> String keyFromBody = toKeyStartingFromBody(key) + if (!keyFromBody) { + return + } if (value.containsKey(MATCH_KEY)) { MatchingType matchingType = MatchingType.valueOf((value.get(MATCH_KEY) as String).toUpperCase()) switch (matchingType) { @@ -185,7 +199,10 @@ class PactContractConverter implements ContractConverter { } protected String toKeyStartingFromBody(String key) { - return key.replace('$.body', '$') + if (key == FULL_BODY) { + return "" + } + return key.replace(FULL_BODY, '$') } @Override diff --git a/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/test/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverterSpec.groovy b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/test/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverterSpec.groovy index 0f6a5529b4..09531cd0e2 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/test/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverterSpec.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/test/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverterSpec.groovy @@ -8,6 +8,7 @@ import org.springframework.cloud.contract.spec.Contract import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter import org.springframework.core.io.Resource import org.springframework.core.io.support.PathMatchingResourcePatternResolver +import spock.lang.Issue import spock.lang.Specification import spock.lang.Subject /** @@ -16,6 +17,7 @@ import spock.lang.Subject class PactContractConverterSpec extends Specification { File pactJson = new File(PactContractConverterSpec.getResource("/pact/pact.json").toURI()) + File pact509Json = new File(PactContractConverterSpec.getResource("/pact/pact_509.json").toURI()) @Subject PactContractConverter converter = new PactContractConverter() def "should accept json files that are pact files"() { @@ -76,6 +78,37 @@ class PactContractConverterSpec extends Specification { contracts == [expectedContract] } + @Issue("#509") + def "should convert from pact with matching rules to whole body to contract"() { + given: + Contract expectedContract = Contract.make { + description("a request to POST a person provider accepts a new person") + request { + method(POST()) + url("/user-service/users") + headers { + contentType(applicationJson()) + } + body(firstName: "Arthur", lastName: "Dent") + } + response { + status(201) + headers { + contentType(applicationJson()) + } + body(id: 42) + testMatchers { + jsonPath('''$.['lastName']''', byType()) + jsonPath('''$.['firstName']''', byType()) + } + } + } + when: + Collection contracts = converter.convertFrom(pact509Json) + then: + contracts == [expectedContract] + } + def "should convert from contract to pact"() { given: Collection inputContracts = [ diff --git a/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/test/resources/pact/pact_509.json b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/test/resources/pact/pact_509.json new file mode 100644 index 0000000000..fb090543a0 --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/test/resources/pact/pact_509.json @@ -0,0 +1,44 @@ +{ + "consumer": { + "name": "ui" + }, + "provider": { + "name": "userservice" + }, + "interactions": [ + { + "description": "a request to POST a person", + "providerState": "provider accepts a new person", + "request": { + "method": "POST", + "path": "/user-service/users", + "headers": { + "Content-Type": "application/json" + }, + "body": { + "firstName": "Arthur", + "lastName": "Dent" + } + }, + "response": { + "status": 201, + "headers": { + "Content-Type": "application/json;charset=UTF-8" + }, + "body": { + "id": 42 + }, + "matchingRules": { + "$.body": { + "match": "type" + } + } + } + } + ], + "metadata": { + "pactSpecification": { + "version": "2.0.0" + } + } +} \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/DelegatingJsonVerifiable.java b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/DelegatingJsonVerifiable.java index 58ef30623d..3d73fc6e6e 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/DelegatingJsonVerifiable.java +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/DelegatingJsonVerifiable.java @@ -71,7 +71,8 @@ class DelegatingJsonVerifiable implements MethodBufferingJsonVerifiable { @Override public MethodBufferingJsonVerifiable contains(Object value) { - DelegatingJsonVerifiable verifiable = new FinishedDelegatingJsonVerifiable(this.delegate.contains(value), this.methodsBuffer); + DelegatingJsonVerifiable verifiable = new FinishedDelegatingJsonVerifiable(this.delegate.jsonPath(), + this.delegate.contains(value), this.methodsBuffer); verifiable.appendMethodWithQuotedValue("contains", value); if (isAssertingAValueInArray()) { verifiable.methodsBuffer.offer(".value()"); @@ -145,7 +146,8 @@ class DelegatingJsonVerifiable implements MethodBufferingJsonVerifiable { @Override public MethodBufferingJsonVerifiable isEqualTo(String value) { - DelegatingJsonVerifiable readyToCheck = new FinishedDelegatingJsonVerifiable(this.delegate.isEqualTo(value), this.methodsBuffer); + DelegatingJsonVerifiable readyToCheck = new FinishedDelegatingJsonVerifiable(this.delegate.jsonPath(), + this.delegate.isEqualTo(value), this.methodsBuffer); if (this.delegate.isAssertingAValueInArray() && readyToCheck.methodsBuffer.peekLast().equals(".arrayField()")) { readyToCheck.appendMethodWithQuotedValue("isEqualTo", escapeJava(value)); readyToCheck.methodsBuffer.offer(".value()"); @@ -167,7 +169,8 @@ class DelegatingJsonVerifiable implements MethodBufferingJsonVerifiable { @Override public MethodBufferingJsonVerifiable isEqualTo(Number value) { - DelegatingJsonVerifiable readyToCheck = new FinishedDelegatingJsonVerifiable(this.delegate.isEqualTo(value), this.methodsBuffer); + DelegatingJsonVerifiable readyToCheck = new FinishedDelegatingJsonVerifiable(this.delegate.jsonPath(), + this.delegate.isEqualTo(value), this.methodsBuffer); // related to #271 - the problem is with asserting arrays of maps vs arrays of primitives String last = readyToCheck.methodsBuffer.peekLast(); boolean containsAMatcher = containsAnyMatcher(last); @@ -185,20 +188,23 @@ class DelegatingJsonVerifiable implements MethodBufferingJsonVerifiable { @Override public MethodBufferingJsonVerifiable isNull() { - DelegatingJsonVerifiable readyToCheck = new FinishedDelegatingJsonVerifiable(this.delegate.isNull(), this.methodsBuffer); + DelegatingJsonVerifiable readyToCheck = new FinishedDelegatingJsonVerifiable( + this.delegate.jsonPath(), this.delegate.isNull(), this.methodsBuffer); readyToCheck.methodsBuffer.offer(".isNull()"); return readyToCheck; } @Override public MethodBufferingJsonVerifiable isEmpty() { - DelegatingJsonVerifiable readyToCheck = new FinishedDelegatingJsonVerifiable(this.delegate.isEmpty(), this.methodsBuffer); + DelegatingJsonVerifiable readyToCheck = new FinishedDelegatingJsonVerifiable( + this.delegate.jsonPath(), this.delegate.isEmpty(), this.methodsBuffer); readyToCheck.methodsBuffer.offer(".isEmpty()"); return readyToCheck; } @Override public MethodBufferingJsonVerifiable matches(String value) { - DelegatingJsonVerifiable readyToCheck = new FinishedDelegatingJsonVerifiable(this.delegate.matches(value), this.methodsBuffer); + DelegatingJsonVerifiable readyToCheck = new FinishedDelegatingJsonVerifiable( + this.delegate.jsonPath(), this.delegate.matches(value), this.methodsBuffer); if (this.delegate.isAssertingAValueInArray()) { readyToCheck.appendMethodWithQuotedValue("matches", escapedHackedJavaText(value)); readyToCheck.methodsBuffer.offer(".value()"); @@ -221,7 +227,8 @@ class DelegatingJsonVerifiable implements MethodBufferingJsonVerifiable { @Override public MethodBufferingJsonVerifiable isEqualTo(Boolean value) { - DelegatingJsonVerifiable readyToCheck = new FinishedDelegatingJsonVerifiable(this.delegate.isEqualTo(value), this.methodsBuffer); + DelegatingJsonVerifiable readyToCheck = new FinishedDelegatingJsonVerifiable( + this.delegate.jsonPath(), this.delegate.isEqualTo(value), this.methodsBuffer); if (this.delegate.isAssertingAValueInArray()) { readyToCheck.methodsBuffer.offer(".value()"); } else { @@ -235,6 +242,10 @@ class DelegatingJsonVerifiable implements MethodBufferingJsonVerifiable { return new FinishedDelegatingJsonVerifiable(this.delegate, this.methodsBuffer); } + @Override public String keyBeforeChecking() { + return this.delegate.jsonPath(); + } + @Override public boolean assertsSize() { for (String s : this.methodsBuffer) { @@ -272,7 +283,8 @@ class DelegatingJsonVerifiable implements MethodBufferingJsonVerifiable { @Override public JsonVerifiable hasSize(int size) { - FinishedDelegatingJsonVerifiable verifiable = new FinishedDelegatingJsonVerifiable(this.delegate.hasSize(size), this.methodsBuffer); + FinishedDelegatingJsonVerifiable verifiable = new FinishedDelegatingJsonVerifiable( + this.delegate.jsonPath(), this.delegate.hasSize(size), this.methodsBuffer); verifiable.methodsBuffer.offer(".hasSize(" + size + ")"); return verifiable; } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/FinishedDelegatingJsonVerifiable.java b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/FinishedDelegatingJsonVerifiable.java index 6c3a3be898..ef8c79859b 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/FinishedDelegatingJsonVerifiable.java +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/FinishedDelegatingJsonVerifiable.java @@ -30,9 +30,21 @@ import com.toomuchcoding.jsonassert.JsonVerifiable; */ class FinishedDelegatingJsonVerifiable extends DelegatingJsonVerifiable { + final String keyBeforeChecking; + + FinishedDelegatingJsonVerifiable(String keyBeforeChecking, JsonVerifiable delegate, + LinkedList methodsBuffer) { + super(delegate, methodsBuffer); + this.keyBeforeChecking = keyBeforeChecking; + } + FinishedDelegatingJsonVerifiable(JsonVerifiable delegate, LinkedList methodsBuffer) { super(delegate, methodsBuffer); + this.keyBeforeChecking = delegate.jsonPath(); } + @Override public String keyBeforeChecking() { + return this.keyBeforeChecking; + } } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/MethodBufferingJsonVerifiable.java b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/MethodBufferingJsonVerifiable.java index 49e0c82982..384343b6ca 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/MethodBufferingJsonVerifiable.java +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/MethodBufferingJsonVerifiable.java @@ -71,6 +71,8 @@ public interface MethodBufferingJsonVerifiable @Override MethodBufferingJsonVerifiable value(); + String keyBeforeChecking(); + boolean assertsSize(); boolean assertsConcreteValue(); From 833a7114abfd0f508f20accfe23d0f359eef1637 Mon Sep 17 00:00:00 2001 From: Tom Hombergs Date: Fri, 12 Jan 2018 22:51:59 +0100 Subject: [PATCH 05/14] matcher now correctly matches response instead of request 511 (#517) --- .../contract/verifier/spec/pact/PactContractConverter.groovy | 2 +- .../verifier/spec/pact/PactContractConverterSpec.groovy | 3 ++- .../src/test/resources/pact/pact_509.json | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/main/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverter.groovy b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/main/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverter.groovy index 173b70f901..1b047d902d 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/main/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverter.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/main/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverter.groovy @@ -138,7 +138,7 @@ class PactContractConverter implements ContractConverter { Map> rules = requestResponseInteraction.response.matchingRules Map fullBodyCheck = rules.get(FULL_BODY) if (fullBodyCheck != null) { - JsonPaths jsonPaths = JsonToJsonPathsConverter.transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck(requestResponseInteraction.request?.body?.value) + JsonPaths jsonPaths = JsonToJsonPathsConverter.transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck(requestResponseInteraction.response?.body?.value) jsonPaths.each { jsonPath(it.keyBeforeChecking(), byType()) } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/test/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverterSpec.groovy b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/test/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverterSpec.groovy index 09531cd0e2..f9a0dae471 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/test/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverterSpec.groovy +++ b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/test/groovy/org/springframework/cloud/contract/verifier/spec/pact/PactContractConverterSpec.groovy @@ -96,8 +96,9 @@ class PactContractConverterSpec extends Specification { headers { contentType(applicationJson()) } - body(id: 42) + body(id: 42, firstName: "Arthur", lastName: "Dent") testMatchers { + jsonPath('''$.['id']''', byType()) jsonPath('''$.['lastName']''', byType()) jsonPath('''$.['firstName']''', byType()) } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/test/resources/pact/pact_509.json b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/test/resources/pact/pact_509.json index fb090543a0..099fa09e6d 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/test/resources/pact/pact_509.json +++ b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/src/test/resources/pact/pact_509.json @@ -26,7 +26,9 @@ "Content-Type": "application/json;charset=UTF-8" }, "body": { - "id": 42 + "id": 42, + "firstName": "Arthur", + "lastName": "Dent" }, "matchingRules": { "$.body": { From 659545a7745d89ebaaa9a5abba63eda01b57799d Mon Sep 17 00:00:00 2001 From: Tom Hombergs Date: Fri, 12 Jan 2018 23:35:03 +0100 Subject: [PATCH 06/14] replace invalid characters in generated method names with underscore (#519) * replace invalid characters in generated method names with underscore fixes #518 --- .../verifier/builder/MethodBuilder.groovy | 4 +-- .../contract/verifier/util/NamesUtil.groovy | 9 +++++ .../verifier/builder/MethodBuilderSpec.groovy | 34 +++++++++++++++++++ .../verifier/util/NamesUtilSpec.groovy | 7 ++++ 4 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilderSpec.groovy diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilder.groovy index b6067197b6..66e97b2e83 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilder.groovy @@ -61,7 +61,7 @@ class MethodBuilder { return new MethodBuilder(methodName, stubContent, configProperties, contract.ignored || stubContent.ignored) } - private static String methodName(ContractMetadata contract, File stubsFile, Contract stubContent) { + static String methodName(ContractMetadata contract, File stubsFile, Contract stubContent) { if (stubContent.name) { return NamesUtil.camelCase(NamesUtil.convertIllegalPackageChars(stubContent.name)) } else if (contract.convertedContract.size() > 1) { @@ -72,7 +72,7 @@ class MethodBuilder { } private static String camelCasedMethodFromFileName(File stubsFile) { - return NamesUtil.camelCase(NamesUtil.toLastDot(NamesUtil.afterLast(stubsFile.path, File.separator))) + return NamesUtil.camelCase(NamesUtil.convertIllegalMethodNameChars(NamesUtil.toLastDot(NamesUtil.afterLast(stubsFile.path, File.separator)))) } /** diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/NamesUtil.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/NamesUtil.groovy index 0a80145e4c..7368e51821 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/NamesUtil.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/NamesUtil.groovy @@ -111,4 +111,13 @@ class NamesUtil { static String convertIllegalPackageChars(String packageName) { return packageName.replaceAll('[_\\- .+]', '_') } + + /** + * Converts illegal characters in method names to underscores + */ + static String convertIllegalMethodNameChars(String methodName) { + String result = methodName.replaceAll('^[^a-zA-Z_$]', '_') + result = result.replaceAll('[^a-zA-Z_$0-9]', '_') + return result + } } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilderSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilderSpec.groovy new file mode 100644 index 0000000000..f54214c6b3 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilderSpec.groovy @@ -0,0 +1,34 @@ +package org.springframework.cloud.contract.verifier.builder + +import org.springframework.cloud.contract.spec.Contract +import org.springframework.cloud.contract.verifier.file.ContractMetadata +import spock.lang.Issue +import spock.lang.Specification + +class MethodBuilderSpec extends Specification { + + @Issue('#518') + def "should map create valid method name from file name containing illegal chars"() { + given: + Contract contractDsl = Contract.make { + request { + method 'GET' + urlPath '/foo' + } + response { + status 200 + body(foo: "foo") + headers { + contentType(applicationJson()) + } + } + } + ContractMetadata metadata = new ContractMetadata(null, false, 0, null, contractDsl) + when: + File stubFile = new File("5invalid-method:name.groovy") + String methodName = MethodBuilder.methodName(metadata, stubFile, contractDsl) + then: + methodName.equals("_invalid_method_name") + } + +} diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/NamesUtilSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/NamesUtilSpec.groovy index 23903c89bb..bb3502ab0f 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/NamesUtilSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/NamesUtilSpec.groovy @@ -104,4 +104,11 @@ class NamesUtilSpec extends Specification { expect: NamesUtil.convertIllegalPackageChars(string) == "a_b_c_1_0_x_d1174dd" } + + def "should convert all illegal method chars to legal ones"() { + given: + String string = '10a-b c.1.0.x+d1174$dd' + expect: + NamesUtil.convertIllegalMethodNameChars(string) == '_0a_b_c_1_0_x_d1174$dd' + } } From 470faf75232566233d950be07c1b0e3dd223630f Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Mon, 15 Jan 2018 19:34:44 -0500 Subject: [PATCH 07/14] bumping netflix, commons, and build --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 215bfc9d00..c04d778eb0 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-build - 1.3.6.BUILD-SNAPSHOT + 1.3.7.BUILD-SNAPSHOT @@ -26,12 +26,12 @@ 5.12.1 2.18.3 2.17 - 1.3.6.BUILD-SNAPSHOT + 1.3.7.BUILD-SNAPSHOT 1.2.0.BUILD-SNAPSHOT Ditmars.BUILD-SNAPSHOT - 1.4.0.BUILD-SNAPSHOT + 1.4.2.BUILD-SNAPSHOT 1.2.2.BUILD-SNAPSHOT - 1.3.0.BUILD-SNAPSHOT + 1.3.1.BUILD-SNAPSHOT From 3c0dcad1d6126c0c8ac2c1f8018b46f6fe1d14a9 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 16 Jan 2018 17:19:47 +0100 Subject: [PATCH 08/14] Numbers are acceptable in the method name fixes gh-518 --- .../cloud/contract/verifier/util/NamesUtil.groovy | 5 ++--- .../cloud/contract/verifier/builder/MethodBuilderSpec.groovy | 5 +++-- .../cloud/contract/verifier/util/NamesUtilSpec.groovy | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/NamesUtil.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/NamesUtil.groovy index 7368e51821..4ac2498cbe 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/NamesUtil.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/util/NamesUtil.groovy @@ -116,8 +116,7 @@ class NamesUtil { * Converts illegal characters in method names to underscores */ static String convertIllegalMethodNameChars(String methodName) { - String result = methodName.replaceAll('^[^a-zA-Z_$]', '_') - result = result.replaceAll('[^a-zA-Z_$0-9]', '_') - return result + String result = methodName.replaceAll('^[^a-zA-Z_$0-9]', '_') + return result.replaceAll('[^a-zA-Z_$0-9]', '_') } } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilderSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilderSpec.groovy index f54214c6b3..cc46bb7c24 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilderSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilderSpec.groovy @@ -25,10 +25,11 @@ class MethodBuilderSpec extends Specification { } ContractMetadata metadata = new ContractMetadata(null, false, 0, null, contractDsl) when: - File stubFile = new File("5invalid-method:name.groovy") + File stubFile = new File("invalid-method:name.groovy") + String methodName = MethodBuilder.methodName(metadata, stubFile, contractDsl) then: - methodName.equals("_invalid_method_name") + methodName == "invalid_method_name" } } diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/NamesUtilSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/NamesUtilSpec.groovy index bb3502ab0f..efbc74c4d9 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/NamesUtilSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/util/NamesUtilSpec.groovy @@ -109,6 +109,6 @@ class NamesUtilSpec extends Specification { given: String string = '10a-b c.1.0.x+d1174$dd' expect: - NamesUtil.convertIllegalMethodNameChars(string) == '_0a_b_c_1_0_x_d1174$dd' + NamesUtil.convertIllegalMethodNameChars(string) == '10a_b_c_1_0_x_d1174$dd' } } From 19deb0126938dc4c84d83b97f0921a1e3676283b Mon Sep 17 00:00:00 2001 From: buildmaster Date: Tue, 16 Jan 2018 17:09:51 +0000 Subject: [PATCH 09/14] Update SNAPSHOT to 1.2.2.RELEASE --- README.adoc | 20 ++++++++++++++++++- docs/pom.xml | 2 +- pom.xml | 16 +++++++-------- samples/pom.xml | 4 ++-- samples/standalone/pom.xml | 4 ++-- samples/wiremock-jetty/pom.xml | 4 ++-- samples/wiremock-native/pom.xml | 4 ++-- samples/wiremock-tomcat/pom.xml | 4 ++-- samples/wiremock-undertow-ssl/pom.xml | 4 ++-- samples/wiremock-undertow/pom.xml | 4 ++-- spring-cloud-contract-dependencies/pom.xml | 4 ++-- spring-cloud-contract-shade/pom.xml | 2 +- spring-cloud-contract-spec/pom.xml | 2 +- spring-cloud-contract-starters/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- spring-cloud-contract-stub-runner/pom.xml | 2 +- spring-cloud-contract-tools/pom.xml | 2 +- .../spring-cloud-contract-converters/pom.xml | 2 +- .../gradle.properties | 2 +- .../pom.xml | 2 +- .../bootSimple/gradle.properties | 2 +- .../sampleJerseyProject/gradle.properties | 2 +- .../sampleProject/gradle.properties | 2 +- .../scenarioProject/gradle.properties | 2 +- .../pom.xml | 4 ++-- .../spring-cloud-contract-spec-pact/pom.xml | 2 +- spring-cloud-contract-verifier/pom.xml | 2 +- spring-cloud-contract-wiremock/pom.xml | 2 +- tests/pom.xml | 2 +- tests/samples-messaging-amqp/pom.xml | 2 +- tests/samples-messaging-camel/pom.xml | 2 +- tests/samples-messaging-integration/pom.xml | 2 +- tests/samples-messaging-spring/pom.xml | 2 +- tests/samples-messaging-stream/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- 45 files changed, 79 insertions(+), 61 deletions(-) diff --git a/README.adoc b/README.adoc index 0e9d56353c..5913cbcccc 100644 --- a/README.adoc +++ b/README.adoc @@ -1149,6 +1149,23 @@ Spring Boot embedded servers, and Wiremock itself has "native" support for a par version of Jetty (currently 9.2). To use the native Jetty, you need to add the native Wiremock dependencies and exclude the Spring Boot container (if there is one). +=== Customization of WireMock configuration + +You can register a bean of `org.springframework.cloud.contract.wiremock.WireMockConfigurationCustomizer` type +in order to customize the WireMock configuration (e.g. add custom transformers). +Example: + +[source,java,indent=0] +---- + @Bean WireMockConfigurationCustomizer optionsCustomizer() { + return new WireMockConfigurationCustomizer() { + @Override public void customize(WireMockConfiguration options) { +// perform your customization here + } + }; + } +---- + === Generating Stubs using REST Docs https://projects.spring.io/spring-restdocs[Spring REST Docs] can be used to generate @@ -1300,12 +1317,13 @@ Consider the following test: .accept(MediaType.APPLICATION_PDF) .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) - .content("{\"foo\": 23 }")) + .content("{\"foo\": 23, \"bar\" : \"baz\" }")) .andExpect(status().isOk()) .andExpect(content().string("bar")) // first WireMock .andDo(WireMockRestDocs.verify() .jsonPath("$[?(@.foo >= 20)]") + .jsonPath("$[?(@.bar in ['baz','bazz','bazzz'])]") .contentType(MediaType.valueOf("application/json")) .stub("shouldGrantABeerIfOldEnough")) // then Contract DSL documentation diff --git a/docs/pom.xml b/docs/pom.xml index 1139dc4e22..8b00fd8f0a 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-docs diff --git a/pom.xml b/pom.xml index c04d778eb0..14a1edfe46 100644 --- a/pom.xml +++ b/pom.xml @@ -7,13 +7,13 @@ org.springframework.cloud spring-cloud-build - 1.3.7.BUILD-SNAPSHOT + 1.3.7.RELEASE spring-cloud-contract-parent pom - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE Spring Cloud Contract Spring Cloud Contract @@ -26,12 +26,12 @@ 5.12.1 2.18.3 2.17 - 1.3.7.BUILD-SNAPSHOT - 1.2.0.BUILD-SNAPSHOT - Ditmars.BUILD-SNAPSHOT - 1.4.2.BUILD-SNAPSHOT - 1.2.2.BUILD-SNAPSHOT - 1.3.1.BUILD-SNAPSHOT + 1.3.7.RELEASE + 1.2.0.RELEASE + Ditmars.SR3 + 1.4.2.RELEASE + 1.3.1.RELEASE + 1.3.1.RELEASE diff --git a/samples/pom.xml b/samples/pom.xml index fd8423495f..5c3f403699 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -6,13 +6,13 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-samples pom - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE Spring Cloud Contract Samples Spring Cloud Contract Samples diff --git a/samples/standalone/pom.xml b/samples/standalone/pom.xml index 49f22a073e..84667cb557 100644 --- a/samples/standalone/pom.xml +++ b/samples/standalone/pom.xml @@ -6,13 +6,13 @@ org.springframework.cloud spring-cloud-contract-samples - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-samples-standalone pom - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE Spring Cloud Contract Standalone Test Samples Spring Cloud Contract Standalone Test Samples used for end to end tests diff --git a/samples/wiremock-jetty/pom.xml b/samples/wiremock-jetty/pom.xml index 73d9cc3b16..967c6052fd 100644 --- a/samples/wiremock-jetty/pom.xml +++ b/samples/wiremock-jetty/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-jetty - 0.0.1 + 1.2.2.RELEASE jar wiremock-jetty @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE diff --git a/samples/wiremock-native/pom.xml b/samples/wiremock-native/pom.xml index 585eed522c..d2c252b38e 100644 --- a/samples/wiremock-native/pom.xml +++ b/samples/wiremock-native/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-native - 0.0.1 + 1.2.2.RELEASE jar wiremock-native @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE diff --git a/samples/wiremock-tomcat/pom.xml b/samples/wiremock-tomcat/pom.xml index f6a56b68e9..c3e75b0d22 100644 --- a/samples/wiremock-tomcat/pom.xml +++ b/samples/wiremock-tomcat/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-tomcat - 0.0.1 + 1.2.2.RELEASE jar wiremock-tomcat @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE diff --git a/samples/wiremock-undertow-ssl/pom.xml b/samples/wiremock-undertow-ssl/pom.xml index 1593507618..84abb83a30 100644 --- a/samples/wiremock-undertow-ssl/pom.xml +++ b/samples/wiremock-undertow-ssl/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-undertow-ssl - 0.0.1 + 1.2.2.RELEASE jar wiremock-undertow-ssl @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE diff --git a/samples/wiremock-undertow/pom.xml b/samples/wiremock-undertow/pom.xml index c226c4646d..283be8c8ce 100644 --- a/samples/wiremock-undertow/pom.xml +++ b/samples/wiremock-undertow/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-undertow - 0.0.1 + 1.2.2.RELEASE jar wiremock-undertow @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE diff --git a/spring-cloud-contract-dependencies/pom.xml b/spring-cloud-contract-dependencies/pom.xml index d4afd8f28f..f6ceca1ea2 100644 --- a/spring-cloud-contract-dependencies/pom.xml +++ b/spring-cloud-contract-dependencies/pom.xml @@ -5,11 +5,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 1.3.6.BUILD-SNAPSHOT + 1.3.7.RELEASE spring-cloud-contract-dependencies - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE pom spring-cloud-contract-dependencies Spring Cloud Contract Dependencies diff --git a/spring-cloud-contract-shade/pom.xml b/spring-cloud-contract-shade/pom.xml index 1b678eceba..7ae17608c9 100644 --- a/spring-cloud-contract-shade/pom.xml +++ b/spring-cloud-contract-shade/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-shade diff --git a/spring-cloud-contract-spec/pom.xml b/spring-cloud-contract-spec/pom.xml index 7bc987e897..1be3ebd3ec 100644 --- a/spring-cloud-contract-spec/pom.xml +++ b/spring-cloud-contract-spec/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-spec diff --git a/spring-cloud-contract-starters/pom.xml b/spring-cloud-contract-starters/pom.xml index 3a9f1a8ff8..04431f21df 100644 --- a/spring-cloud-contract-starters/pom.xml +++ b/spring-cloud-contract-starters/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. diff --git a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner-jetty/pom.xml b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner-jetty/pom.xml index 9b899e9e1f..2483d675d1 100644 --- a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner-jetty/pom.xml +++ b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner-jetty/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-starters - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-starter-contract-stub-runner-jetty diff --git a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml index 775e63461c..bb8242292b 100644 --- a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml +++ b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-starters - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-starter-contract-stub-runner diff --git a/spring-cloud-contract-starters/spring-cloud-starter-contract-verifier/pom.xml b/spring-cloud-contract-starters/spring-cloud-starter-contract-verifier/pom.xml index 436bcbac20..70715f0bae 100644 --- a/spring-cloud-contract-starters/spring-cloud-starter-contract-verifier/pom.xml +++ b/spring-cloud-contract-starters/spring-cloud-starter-contract-verifier/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-starters - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-starter-contract-verifier diff --git a/spring-cloud-contract-stub-runner/pom.xml b/spring-cloud-contract-stub-runner/pom.xml index 45707881ec..65a0b92a57 100644 --- a/spring-cloud-contract-stub-runner/pom.xml +++ b/spring-cloud-contract-stub-runner/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-stub-runner diff --git a/spring-cloud-contract-tools/pom.xml b/spring-cloud-contract-tools/pom.xml index 656bd286ac..c82dd25d12 100644 --- a/spring-cloud-contract-tools/pom.xml +++ b/spring-cloud-contract-tools/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. diff --git a/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml index 2efc2d4918..d191d9e383 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tools - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-converters diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties index 6e0df725d8..a6085eb28a 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties @@ -16,6 +16,6 @@ nexusUsername = nexusPassword = -verifierVersion=1.2.2.BUILD-SNAPSHOT +verifierVersion=1.2.2.RELEASE org.gradle.daemon=false aetherVersion=1.0.2.v20150114 \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml index b043a61f4f..c56b3dae32 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-tools - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties index 2f45204455..ed76e2cce6 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties @@ -15,5 +15,5 @@ # wiremockVersion=2.12.0 jsonAssertVersion=0.4.10 -verifierVersion=1.2.2.BUILD-SNAPSHOT +verifierVersion=1.2.2.RELEASE diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties index a979b2c048..0aeed018a2 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties @@ -15,4 +15,4 @@ # wiremockVersion=2.12.0 jsonAssertVersion=0.4.10 -verifierVersion=1.2.2.BUILD-SNAPSHOT +verifierVersion=1.2.2.RELEASE diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties index 887e30a1a5..96cc6ec955 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties @@ -15,4 +15,4 @@ # wiremockVersion=2.12.0 jsonAssertVersion=0.4.10 -verifierVersion=1.2.2.BUILD-SNAPSHOT \ No newline at end of file +verifierVersion=1.2.2.RELEASE \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties index 2f45204455..ed76e2cce6 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties @@ -15,5 +15,5 @@ # wiremockVersion=2.12.0 jsonAssertVersion=0.4.10 -verifierVersion=1.2.2.BUILD-SNAPSHOT +verifierVersion=1.2.2.RELEASE diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml index 47e1578ff7..68ab561563 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-tools - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. @@ -33,7 +33,7 @@ 1.7 - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE 2016 diff --git a/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/pom.xml index 010a145f03..b53bbaa9cd 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tools - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-spec-pact diff --git a/spring-cloud-contract-verifier/pom.xml b/spring-cloud-contract-verifier/pom.xml index 1bfbef1670..1372f62ba0 100644 --- a/spring-cloud-contract-verifier/pom.xml +++ b/spring-cloud-contract-verifier/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-verifier diff --git a/spring-cloud-contract-wiremock/pom.xml b/spring-cloud-contract-wiremock/pom.xml index d8917f583d..210c6c8ef8 100644 --- a/spring-cloud-contract-wiremock/pom.xml +++ b/spring-cloud-contract-wiremock/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-wiremock diff --git a/tests/pom.xml b/tests/pom.xml index 23fc5c696a..4c7e072b6d 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. diff --git a/tests/samples-messaging-amqp/pom.xml b/tests/samples-messaging-amqp/pom.xml index ae65414dcb..751316692e 100644 --- a/tests/samples-messaging-amqp/pom.xml +++ b/tests/samples-messaging-amqp/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-sample-amqp diff --git a/tests/samples-messaging-camel/pom.xml b/tests/samples-messaging-camel/pom.xml index e21d43bcc6..b544cb34ca 100644 --- a/tests/samples-messaging-camel/pom.xml +++ b/tests/samples-messaging-camel/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-sample-camel diff --git a/tests/samples-messaging-integration/pom.xml b/tests/samples-messaging-integration/pom.xml index 61cb05a88b..d46debdb9f 100644 --- a/tests/samples-messaging-integration/pom.xml +++ b/tests/samples-messaging-integration/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-sample-integration diff --git a/tests/samples-messaging-spring/pom.xml b/tests/samples-messaging-spring/pom.xml index 05a2869d61..471629e099 100644 --- a/tests/samples-messaging-spring/pom.xml +++ b/tests/samples-messaging-spring/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-sample-spring diff --git a/tests/samples-messaging-stream/pom.xml b/tests/samples-messaging-stream/pom.xml index 8853057412..8e6ed6e5e5 100644 --- a/tests/samples-messaging-stream/pom.xml +++ b/tests/samples-messaging-stream/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-sample-stream diff --git a/tests/spring-cloud-contract-stub-runner-amqp/pom.xml b/tests/spring-cloud-contract-stub-runner-amqp/pom.xml index 5bdd5137b1..bd54580fbe 100644 --- a/tests/spring-cloud-contract-stub-runner-amqp/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-amqp/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-stub-runner-amqp diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml b/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml index a384873054..6c95a4fccb 100644 --- a/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-stub-runner-boot-eureka diff --git a/tests/spring-cloud-contract-stub-runner-boot-zookeeper/pom.xml b/tests/spring-cloud-contract-stub-runner-boot-zookeeper/pom.xml index f161ddb383..a8e07e27de 100644 --- a/tests/spring-cloud-contract-stub-runner-boot-zookeeper/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-boot-zookeeper/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-stub-runner-boot-zookeeper diff --git a/tests/spring-cloud-contract-stub-runner-camel/pom.xml b/tests/spring-cloud-contract-stub-runner-camel/pom.xml index 43b9e29099..79e0f5274e 100644 --- a/tests/spring-cloud-contract-stub-runner-camel/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-camel/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-stub-runner-camel diff --git a/tests/spring-cloud-contract-stub-runner-context-path/pom.xml b/tests/spring-cloud-contract-stub-runner-context-path/pom.xml index 3d4d50809e..9b45a70ff8 100644 --- a/tests/spring-cloud-contract-stub-runner-context-path/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-context-path/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-stub-runner-context-path diff --git a/tests/spring-cloud-contract-stub-runner-integration/pom.xml b/tests/spring-cloud-contract-stub-runner-integration/pom.xml index 7f160c2f6b..ced313f02d 100644 --- a/tests/spring-cloud-contract-stub-runner-integration/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-integration/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-stub-runner-integration diff --git a/tests/spring-cloud-contract-stub-runner-moco-contract-jar/pom.xml b/tests/spring-cloud-contract-stub-runner-moco-contract-jar/pom.xml index c1914d8fd4..bd695571aa 100644 --- a/tests/spring-cloud-contract-stub-runner-moco-contract-jar/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-moco-contract-jar/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-stub-runner-moco-contract-jar diff --git a/tests/spring-cloud-contract-stub-runner-moco/pom.xml b/tests/spring-cloud-contract-stub-runner-moco/pom.xml index 9e4e1aef0e..ebd4fa1ca7 100644 --- a/tests/spring-cloud-contract-stub-runner-moco/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-moco/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-stub-runner-moco diff --git a/tests/spring-cloud-contract-stub-runner-stream/pom.xml b/tests/spring-cloud-contract-stub-runner-stream/pom.xml index c3ea352ff3..7e64a68fac 100644 --- a/tests/spring-cloud-contract-stub-runner-stream/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-stream/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.2.RELEASE .. spring-cloud-contract-stub-runner-stream From f5561d3f9bf97be55893157cb7ca4deef384d1ed Mon Sep 17 00:00:00 2001 From: buildmaster Date: Tue, 16 Jan 2018 17:20:15 +0000 Subject: [PATCH 10/14] Going back to snapshots --- README.adoc | 20 +------------------ docs/pom.xml | 2 +- pom.xml | 16 +++++++-------- samples/pom.xml | 4 ++-- samples/standalone/pom.xml | 4 ++-- samples/wiremock-jetty/pom.xml | 4 ++-- samples/wiremock-native/pom.xml | 4 ++-- samples/wiremock-tomcat/pom.xml | 4 ++-- samples/wiremock-undertow-ssl/pom.xml | 4 ++-- samples/wiremock-undertow/pom.xml | 4 ++-- spring-cloud-contract-dependencies/pom.xml | 4 ++-- spring-cloud-contract-shade/pom.xml | 2 +- spring-cloud-contract-spec/pom.xml | 2 +- spring-cloud-contract-starters/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- spring-cloud-contract-stub-runner/pom.xml | 2 +- spring-cloud-contract-tools/pom.xml | 2 +- .../spring-cloud-contract-converters/pom.xml | 2 +- .../gradle.properties | 2 +- .../pom.xml | 2 +- .../bootSimple/gradle.properties | 2 +- .../sampleJerseyProject/gradle.properties | 2 +- .../sampleProject/gradle.properties | 2 +- .../scenarioProject/gradle.properties | 2 +- .../pom.xml | 4 ++-- .../spring-cloud-contract-spec-pact/pom.xml | 2 +- spring-cloud-contract-verifier/pom.xml | 2 +- spring-cloud-contract-wiremock/pom.xml | 2 +- tests/pom.xml | 2 +- tests/samples-messaging-amqp/pom.xml | 2 +- tests/samples-messaging-camel/pom.xml | 2 +- tests/samples-messaging-integration/pom.xml | 2 +- tests/samples-messaging-spring/pom.xml | 2 +- tests/samples-messaging-stream/pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- 45 files changed, 61 insertions(+), 79 deletions(-) diff --git a/README.adoc b/README.adoc index 5913cbcccc..0e9d56353c 100644 --- a/README.adoc +++ b/README.adoc @@ -1149,23 +1149,6 @@ Spring Boot embedded servers, and Wiremock itself has "native" support for a par version of Jetty (currently 9.2). To use the native Jetty, you need to add the native Wiremock dependencies and exclude the Spring Boot container (if there is one). -=== Customization of WireMock configuration - -You can register a bean of `org.springframework.cloud.contract.wiremock.WireMockConfigurationCustomizer` type -in order to customize the WireMock configuration (e.g. add custom transformers). -Example: - -[source,java,indent=0] ----- - @Bean WireMockConfigurationCustomizer optionsCustomizer() { - return new WireMockConfigurationCustomizer() { - @Override public void customize(WireMockConfiguration options) { -// perform your customization here - } - }; - } ----- - === Generating Stubs using REST Docs https://projects.spring.io/spring-restdocs[Spring REST Docs] can be used to generate @@ -1317,13 +1300,12 @@ Consider the following test: .accept(MediaType.APPLICATION_PDF) .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) - .content("{\"foo\": 23, \"bar\" : \"baz\" }")) + .content("{\"foo\": 23 }")) .andExpect(status().isOk()) .andExpect(content().string("bar")) // first WireMock .andDo(WireMockRestDocs.verify() .jsonPath("$[?(@.foo >= 20)]") - .jsonPath("$[?(@.bar in ['baz','bazz','bazzz'])]") .contentType(MediaType.valueOf("application/json")) .stub("shouldGrantABeerIfOldEnough")) // then Contract DSL documentation diff --git a/docs/pom.xml b/docs/pom.xml index 8b00fd8f0a..1139dc4e22 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-docs diff --git a/pom.xml b/pom.xml index 14a1edfe46..c04d778eb0 100644 --- a/pom.xml +++ b/pom.xml @@ -7,13 +7,13 @@ org.springframework.cloud spring-cloud-build - 1.3.7.RELEASE + 1.3.7.BUILD-SNAPSHOT spring-cloud-contract-parent pom - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT Spring Cloud Contract Spring Cloud Contract @@ -26,12 +26,12 @@ 5.12.1 2.18.3 2.17 - 1.3.7.RELEASE - 1.2.0.RELEASE - Ditmars.SR3 - 1.4.2.RELEASE - 1.3.1.RELEASE - 1.3.1.RELEASE + 1.3.7.BUILD-SNAPSHOT + 1.2.0.BUILD-SNAPSHOT + Ditmars.BUILD-SNAPSHOT + 1.4.2.BUILD-SNAPSHOT + 1.2.2.BUILD-SNAPSHOT + 1.3.1.BUILD-SNAPSHOT diff --git a/samples/pom.xml b/samples/pom.xml index 5c3f403699..fd8423495f 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -6,13 +6,13 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-samples pom - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT Spring Cloud Contract Samples Spring Cloud Contract Samples diff --git a/samples/standalone/pom.xml b/samples/standalone/pom.xml index 84667cb557..49f22a073e 100644 --- a/samples/standalone/pom.xml +++ b/samples/standalone/pom.xml @@ -6,13 +6,13 @@ org.springframework.cloud spring-cloud-contract-samples - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-samples-standalone pom - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT Spring Cloud Contract Standalone Test Samples Spring Cloud Contract Standalone Test Samples used for end to end tests diff --git a/samples/wiremock-jetty/pom.xml b/samples/wiremock-jetty/pom.xml index 967c6052fd..73d9cc3b16 100644 --- a/samples/wiremock-jetty/pom.xml +++ b/samples/wiremock-jetty/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-jetty - 1.2.2.RELEASE + 0.0.1 jar wiremock-jetty @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT diff --git a/samples/wiremock-native/pom.xml b/samples/wiremock-native/pom.xml index d2c252b38e..585eed522c 100644 --- a/samples/wiremock-native/pom.xml +++ b/samples/wiremock-native/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-native - 1.2.2.RELEASE + 0.0.1 jar wiremock-native @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT diff --git a/samples/wiremock-tomcat/pom.xml b/samples/wiremock-tomcat/pom.xml index c3e75b0d22..f6a56b68e9 100644 --- a/samples/wiremock-tomcat/pom.xml +++ b/samples/wiremock-tomcat/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-tomcat - 1.2.2.RELEASE + 0.0.1 jar wiremock-tomcat @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT diff --git a/samples/wiremock-undertow-ssl/pom.xml b/samples/wiremock-undertow-ssl/pom.xml index 84abb83a30..1593507618 100644 --- a/samples/wiremock-undertow-ssl/pom.xml +++ b/samples/wiremock-undertow-ssl/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-undertow-ssl - 1.2.2.RELEASE + 0.0.1 jar wiremock-undertow-ssl @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT diff --git a/samples/wiremock-undertow/pom.xml b/samples/wiremock-undertow/pom.xml index 283be8c8ce..c226c4646d 100644 --- a/samples/wiremock-undertow/pom.xml +++ b/samples/wiremock-undertow/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-undertow - 1.2.2.RELEASE + 0.0.1 jar wiremock-undertow @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT diff --git a/spring-cloud-contract-dependencies/pom.xml b/spring-cloud-contract-dependencies/pom.xml index f6ceca1ea2..d4afd8f28f 100644 --- a/spring-cloud-contract-dependencies/pom.xml +++ b/spring-cloud-contract-dependencies/pom.xml @@ -5,11 +5,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 1.3.7.RELEASE + 1.3.6.BUILD-SNAPSHOT spring-cloud-contract-dependencies - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT pom spring-cloud-contract-dependencies Spring Cloud Contract Dependencies diff --git a/spring-cloud-contract-shade/pom.xml b/spring-cloud-contract-shade/pom.xml index 7ae17608c9..1b678eceba 100644 --- a/spring-cloud-contract-shade/pom.xml +++ b/spring-cloud-contract-shade/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-shade diff --git a/spring-cloud-contract-spec/pom.xml b/spring-cloud-contract-spec/pom.xml index 1be3ebd3ec..7bc987e897 100644 --- a/spring-cloud-contract-spec/pom.xml +++ b/spring-cloud-contract-spec/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-spec diff --git a/spring-cloud-contract-starters/pom.xml b/spring-cloud-contract-starters/pom.xml index 04431f21df..3a9f1a8ff8 100644 --- a/spring-cloud-contract-starters/pom.xml +++ b/spring-cloud-contract-starters/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. diff --git a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner-jetty/pom.xml b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner-jetty/pom.xml index 2483d675d1..9b899e9e1f 100644 --- a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner-jetty/pom.xml +++ b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner-jetty/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-starters - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-starter-contract-stub-runner-jetty diff --git a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml index bb8242292b..775e63461c 100644 --- a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml +++ b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-starters - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-starter-contract-stub-runner diff --git a/spring-cloud-contract-starters/spring-cloud-starter-contract-verifier/pom.xml b/spring-cloud-contract-starters/spring-cloud-starter-contract-verifier/pom.xml index 70715f0bae..436bcbac20 100644 --- a/spring-cloud-contract-starters/spring-cloud-starter-contract-verifier/pom.xml +++ b/spring-cloud-contract-starters/spring-cloud-starter-contract-verifier/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-starters - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-starter-contract-verifier diff --git a/spring-cloud-contract-stub-runner/pom.xml b/spring-cloud-contract-stub-runner/pom.xml index 65a0b92a57..45707881ec 100644 --- a/spring-cloud-contract-stub-runner/pom.xml +++ b/spring-cloud-contract-stub-runner/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner diff --git a/spring-cloud-contract-tools/pom.xml b/spring-cloud-contract-tools/pom.xml index c82dd25d12..656bd286ac 100644 --- a/spring-cloud-contract-tools/pom.xml +++ b/spring-cloud-contract-tools/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. diff --git a/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml index d191d9e383..2efc2d4918 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tools - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-converters diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties index a6085eb28a..6e0df725d8 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties @@ -16,6 +16,6 @@ nexusUsername = nexusPassword = -verifierVersion=1.2.2.RELEASE +verifierVersion=1.2.2.BUILD-SNAPSHOT org.gradle.daemon=false aetherVersion=1.0.2.v20150114 \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml index c56b3dae32..b043a61f4f 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-tools - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties index ed76e2cce6..2f45204455 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties @@ -15,5 +15,5 @@ # wiremockVersion=2.12.0 jsonAssertVersion=0.4.10 -verifierVersion=1.2.2.RELEASE +verifierVersion=1.2.2.BUILD-SNAPSHOT diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties index 0aeed018a2..a979b2c048 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties @@ -15,4 +15,4 @@ # wiremockVersion=2.12.0 jsonAssertVersion=0.4.10 -verifierVersion=1.2.2.RELEASE +verifierVersion=1.2.2.BUILD-SNAPSHOT diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties index 96cc6ec955..887e30a1a5 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties @@ -15,4 +15,4 @@ # wiremockVersion=2.12.0 jsonAssertVersion=0.4.10 -verifierVersion=1.2.2.RELEASE \ No newline at end of file +verifierVersion=1.2.2.BUILD-SNAPSHOT \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties index ed76e2cce6..2f45204455 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties @@ -15,5 +15,5 @@ # wiremockVersion=2.12.0 jsonAssertVersion=0.4.10 -verifierVersion=1.2.2.RELEASE +verifierVersion=1.2.2.BUILD-SNAPSHOT diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml index 68ab561563..47e1578ff7 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-tools - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. @@ -33,7 +33,7 @@ 1.7 - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT 2016 diff --git a/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/pom.xml index b53bbaa9cd..010a145f03 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tools - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-spec-pact diff --git a/spring-cloud-contract-verifier/pom.xml b/spring-cloud-contract-verifier/pom.xml index 1372f62ba0..1bfbef1670 100644 --- a/spring-cloud-contract-verifier/pom.xml +++ b/spring-cloud-contract-verifier/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-verifier diff --git a/spring-cloud-contract-wiremock/pom.xml b/spring-cloud-contract-wiremock/pom.xml index 210c6c8ef8..d8917f583d 100644 --- a/spring-cloud-contract-wiremock/pom.xml +++ b/spring-cloud-contract-wiremock/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-wiremock diff --git a/tests/pom.xml b/tests/pom.xml index 4c7e072b6d..23fc5c696a 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. diff --git a/tests/samples-messaging-amqp/pom.xml b/tests/samples-messaging-amqp/pom.xml index 751316692e..ae65414dcb 100644 --- a/tests/samples-messaging-amqp/pom.xml +++ b/tests/samples-messaging-amqp/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-sample-amqp diff --git a/tests/samples-messaging-camel/pom.xml b/tests/samples-messaging-camel/pom.xml index b544cb34ca..e21d43bcc6 100644 --- a/tests/samples-messaging-camel/pom.xml +++ b/tests/samples-messaging-camel/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-sample-camel diff --git a/tests/samples-messaging-integration/pom.xml b/tests/samples-messaging-integration/pom.xml index d46debdb9f..61cb05a88b 100644 --- a/tests/samples-messaging-integration/pom.xml +++ b/tests/samples-messaging-integration/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-sample-integration diff --git a/tests/samples-messaging-spring/pom.xml b/tests/samples-messaging-spring/pom.xml index 471629e099..05a2869d61 100644 --- a/tests/samples-messaging-spring/pom.xml +++ b/tests/samples-messaging-spring/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-sample-spring diff --git a/tests/samples-messaging-stream/pom.xml b/tests/samples-messaging-stream/pom.xml index 8e6ed6e5e5..8853057412 100644 --- a/tests/samples-messaging-stream/pom.xml +++ b/tests/samples-messaging-stream/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-sample-stream diff --git a/tests/spring-cloud-contract-stub-runner-amqp/pom.xml b/tests/spring-cloud-contract-stub-runner-amqp/pom.xml index bd54580fbe..5bdd5137b1 100644 --- a/tests/spring-cloud-contract-stub-runner-amqp/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-amqp/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-amqp diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml b/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml index 6c95a4fccb..a384873054 100644 --- a/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-boot-eureka diff --git a/tests/spring-cloud-contract-stub-runner-boot-zookeeper/pom.xml b/tests/spring-cloud-contract-stub-runner-boot-zookeeper/pom.xml index a8e07e27de..f161ddb383 100644 --- a/tests/spring-cloud-contract-stub-runner-boot-zookeeper/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-boot-zookeeper/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-boot-zookeeper diff --git a/tests/spring-cloud-contract-stub-runner-camel/pom.xml b/tests/spring-cloud-contract-stub-runner-camel/pom.xml index 79e0f5274e..43b9e29099 100644 --- a/tests/spring-cloud-contract-stub-runner-camel/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-camel/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-camel diff --git a/tests/spring-cloud-contract-stub-runner-context-path/pom.xml b/tests/spring-cloud-contract-stub-runner-context-path/pom.xml index 9b45a70ff8..3d4d50809e 100644 --- a/tests/spring-cloud-contract-stub-runner-context-path/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-context-path/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-context-path diff --git a/tests/spring-cloud-contract-stub-runner-integration/pom.xml b/tests/spring-cloud-contract-stub-runner-integration/pom.xml index ced313f02d..7f160c2f6b 100644 --- a/tests/spring-cloud-contract-stub-runner-integration/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-integration/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-integration diff --git a/tests/spring-cloud-contract-stub-runner-moco-contract-jar/pom.xml b/tests/spring-cloud-contract-stub-runner-moco-contract-jar/pom.xml index bd695571aa..c1914d8fd4 100644 --- a/tests/spring-cloud-contract-stub-runner-moco-contract-jar/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-moco-contract-jar/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-moco-contract-jar diff --git a/tests/spring-cloud-contract-stub-runner-moco/pom.xml b/tests/spring-cloud-contract-stub-runner-moco/pom.xml index ebd4fa1ca7..9e4e1aef0e 100644 --- a/tests/spring-cloud-contract-stub-runner-moco/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-moco/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-moco diff --git a/tests/spring-cloud-contract-stub-runner-stream/pom.xml b/tests/spring-cloud-contract-stub-runner-stream/pom.xml index 7e64a68fac..c3ea352ff3 100644 --- a/tests/spring-cloud-contract-stub-runner-stream/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-stream/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.RELEASE + 1.2.2.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-stream From b2428c5f13d9657e6e588c5d63e218847b895298 Mon Sep 17 00:00:00 2001 From: buildmaster Date: Tue, 16 Jan 2018 17:20:16 +0000 Subject: [PATCH 11/14] Bumping versions to 1.2.3.BUILD-SNAPSHOT after release --- docs/pom.xml | 2 +- pom.xml | 2 +- samples/pom.xml | 4 ++-- samples/standalone/contracts/com/example/server/pom.xml | 2 +- samples/standalone/dsl/http-server/gradle.properties | 2 +- samples/standalone/dsl/http-server/pom.xml | 2 +- samples/standalone/dsl/pom.xml | 4 ++-- samples/standalone/messaging/pom.xml | 4 ++-- samples/standalone/messaging/stream-sink/gradle.properties | 2 +- samples/standalone/messaging/stream-sink/pom.xml | 2 +- samples/standalone/messaging/stream-source/gradle.properties | 2 +- samples/standalone/messaging/stream-source/pom.xml | 2 +- samples/standalone/pact/pact-http-server/gradle.properties | 2 +- samples/standalone/pact/pact-http-server/pom.xml | 2 +- samples/standalone/pact/pom.xml | 4 ++-- samples/standalone/pom.xml | 4 ++-- samples/standalone/restdocs/http-client/gradle.properties | 2 +- samples/standalone/restdocs/http-client/pom.xml | 2 +- samples/standalone/restdocs/http-server/gradle.properties | 2 +- samples/standalone/restdocs/http-server/pom.xml | 2 +- samples/standalone/restdocs/pom.xml | 4 ++-- samples/wiremock-jetty/pom.xml | 4 ++-- samples/wiremock-native/pom.xml | 4 ++-- samples/wiremock-tomcat/pom.xml | 4 ++-- samples/wiremock-undertow-ssl/pom.xml | 4 ++-- samples/wiremock-undertow/pom.xml | 4 ++-- spring-cloud-contract-dependencies/pom.xml | 2 +- spring-cloud-contract-shade/pom.xml | 2 +- spring-cloud-contract-spec/pom.xml | 2 +- spring-cloud-contract-starters/pom.xml | 2 +- .../spring-cloud-starter-contract-stub-runner-jetty/pom.xml | 2 +- .../spring-cloud-starter-contract-stub-runner/pom.xml | 2 +- .../spring-cloud-starter-contract-verifier/pom.xml | 2 +- spring-cloud-contract-stub-runner/pom.xml | 2 +- spring-cloud-contract-tools/pom.xml | 2 +- .../spring-cloud-contract-converters/pom.xml | 2 +- .../spring-cloud-contract-gradle-plugin/gradle.properties | 2 +- .../spring-cloud-contract-gradle-plugin/pom.xml | 2 +- .../resources/functionalTest/bootSimple/gradle.properties | 2 +- .../functionalTest/sampleJerseyProject/gradle.properties | 2 +- .../resources/functionalTest/sampleProject/gradle.properties | 2 +- .../functionalTest/scenarioProject/gradle.properties | 2 +- .../spring-cloud-contract-maven-plugin/pom.xml | 4 ++-- .../spring-cloud-contract-spec-pact/pom.xml | 2 +- spring-cloud-contract-verifier/pom.xml | 2 +- spring-cloud-contract-wiremock/pom.xml | 2 +- tests/pom.xml | 2 +- tests/samples-messaging-amqp/pom.xml | 2 +- tests/samples-messaging-camel/pom.xml | 2 +- tests/samples-messaging-integration/pom.xml | 2 +- tests/samples-messaging-spring/pom.xml | 2 +- tests/samples-messaging-stream/pom.xml | 2 +- tests/spring-cloud-contract-stub-runner-amqp/pom.xml | 2 +- tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml | 2 +- .../spring-cloud-contract-stub-runner-boot-zookeeper/pom.xml | 2 +- tests/spring-cloud-contract-stub-runner-camel/pom.xml | 2 +- tests/spring-cloud-contract-stub-runner-context-path/pom.xml | 2 +- tests/spring-cloud-contract-stub-runner-integration/pom.xml | 2 +- .../pom.xml | 2 +- tests/spring-cloud-contract-stub-runner-moco/pom.xml | 2 +- tests/spring-cloud-contract-stub-runner-stream/pom.xml | 2 +- 61 files changed, 73 insertions(+), 73 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 1139dc4e22..f9a22cb634 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-docs diff --git a/pom.xml b/pom.xml index c04d778eb0..f7f50e8f81 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ spring-cloud-contract-parent pom - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT Spring Cloud Contract Spring Cloud Contract diff --git a/samples/pom.xml b/samples/pom.xml index fd8423495f..558092ad03 100644 --- a/samples/pom.xml +++ b/samples/pom.xml @@ -6,13 +6,13 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-samples pom - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT Spring Cloud Contract Samples Spring Cloud Contract Samples diff --git a/samples/standalone/contracts/com/example/server/pom.xml b/samples/standalone/contracts/com/example/server/pom.xml index 755c0832fd..0e963b862a 100644 --- a/samples/standalone/contracts/com/example/server/pom.xml +++ b/samples/standalone/contracts/com/example/server/pom.xml @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT Edgware.BUILD-SNAPSHOT true diff --git a/samples/standalone/dsl/http-server/gradle.properties b/samples/standalone/dsl/http-server/gradle.properties index b99fcb4df7..cd9eed2f62 100644 --- a/samples/standalone/dsl/http-server/gradle.properties +++ b/samples/standalone/dsl/http-server/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=false -verifierVersion=1.2.2.BUILD-SNAPSHOT +verifierVersion=1.2.3.BUILD-SNAPSHOT BOM_VERSION=Edgware.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/dsl/http-server/pom.xml b/samples/standalone/dsl/http-server/pom.xml index a9b142a766..cd0c5c5ec4 100644 --- a/samples/standalone/dsl/http-server/pom.xml +++ b/samples/standalone/dsl/http-server/pom.xml @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT Edgware.BUILD-SNAPSHOT diff --git a/samples/standalone/dsl/pom.xml b/samples/standalone/dsl/pom.xml index c56bd4c79e..8dbe7803aa 100644 --- a/samples/standalone/dsl/pom.xml +++ b/samples/standalone/dsl/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-samples-standalone - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. @@ -17,7 +17,7 @@ Spring Cloud Contract Standalone Test Samples used for end to end tests - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT diff --git a/samples/standalone/messaging/pom.xml b/samples/standalone/messaging/pom.xml index 3eea667768..2d4a42216b 100644 --- a/samples/standalone/messaging/pom.xml +++ b/samples/standalone/messaging/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-samples-standalone - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. @@ -17,7 +17,7 @@ Spring Cloud Contract Standalone Test Samples used for end to end tests - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT diff --git a/samples/standalone/messaging/stream-sink/gradle.properties b/samples/standalone/messaging/stream-sink/gradle.properties index b99fcb4df7..cd9eed2f62 100644 --- a/samples/standalone/messaging/stream-sink/gradle.properties +++ b/samples/standalone/messaging/stream-sink/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=false -verifierVersion=1.2.2.BUILD-SNAPSHOT +verifierVersion=1.2.3.BUILD-SNAPSHOT BOM_VERSION=Edgware.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/messaging/stream-sink/pom.xml b/samples/standalone/messaging/stream-sink/pom.xml index e51b7e50b0..dcac36d70d 100644 --- a/samples/standalone/messaging/stream-sink/pom.xml +++ b/samples/standalone/messaging/stream-sink/pom.xml @@ -21,7 +21,7 @@ UTF-8 1.8 - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT diff --git a/samples/standalone/messaging/stream-source/gradle.properties b/samples/standalone/messaging/stream-source/gradle.properties index b99fcb4df7..cd9eed2f62 100644 --- a/samples/standalone/messaging/stream-source/gradle.properties +++ b/samples/standalone/messaging/stream-source/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=false -verifierVersion=1.2.2.BUILD-SNAPSHOT +verifierVersion=1.2.3.BUILD-SNAPSHOT BOM_VERSION=Edgware.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/messaging/stream-source/pom.xml b/samples/standalone/messaging/stream-source/pom.xml index e4d0bc5e7d..8be95b5be3 100644 --- a/samples/standalone/messaging/stream-source/pom.xml +++ b/samples/standalone/messaging/stream-source/pom.xml @@ -21,7 +21,7 @@ UTF-8 1.8 - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT diff --git a/samples/standalone/pact/pact-http-server/gradle.properties b/samples/standalone/pact/pact-http-server/gradle.properties index b99fcb4df7..cd9eed2f62 100644 --- a/samples/standalone/pact/pact-http-server/gradle.properties +++ b/samples/standalone/pact/pact-http-server/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=false -verifierVersion=1.2.2.BUILD-SNAPSHOT +verifierVersion=1.2.3.BUILD-SNAPSHOT BOM_VERSION=Edgware.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/pact/pact-http-server/pom.xml b/samples/standalone/pact/pact-http-server/pom.xml index c1f7757c22..c06707fc95 100644 --- a/samples/standalone/pact/pact-http-server/pom.xml +++ b/samples/standalone/pact/pact-http-server/pom.xml @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT Edgware.BUILD-SNAPSHOT diff --git a/samples/standalone/pact/pom.xml b/samples/standalone/pact/pom.xml index 6ed9a76f15..97d54eafdd 100644 --- a/samples/standalone/pact/pom.xml +++ b/samples/standalone/pact/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-samples-standalone - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. @@ -17,7 +17,7 @@ Spring Cloud Contract Standalone Test Samples used for end to end tests with Pact - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT diff --git a/samples/standalone/pom.xml b/samples/standalone/pom.xml index 49f22a073e..3d10bea281 100644 --- a/samples/standalone/pom.xml +++ b/samples/standalone/pom.xml @@ -6,13 +6,13 @@ org.springframework.cloud spring-cloud-contract-samples - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-samples-standalone pom - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT Spring Cloud Contract Standalone Test Samples Spring Cloud Contract Standalone Test Samples used for end to end tests diff --git a/samples/standalone/restdocs/http-client/gradle.properties b/samples/standalone/restdocs/http-client/gradle.properties index b99fcb4df7..cd9eed2f62 100644 --- a/samples/standalone/restdocs/http-client/gradle.properties +++ b/samples/standalone/restdocs/http-client/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=false -verifierVersion=1.2.2.BUILD-SNAPSHOT +verifierVersion=1.2.3.BUILD-SNAPSHOT BOM_VERSION=Edgware.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/restdocs/http-client/pom.xml b/samples/standalone/restdocs/http-client/pom.xml index 8d49ee2ae4..bcebf74a72 100644 --- a/samples/standalone/restdocs/http-client/pom.xml +++ b/samples/standalone/restdocs/http-client/pom.xml @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT diff --git a/samples/standalone/restdocs/http-server/gradle.properties b/samples/standalone/restdocs/http-server/gradle.properties index b99fcb4df7..cd9eed2f62 100644 --- a/samples/standalone/restdocs/http-server/gradle.properties +++ b/samples/standalone/restdocs/http-server/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=false -verifierVersion=1.2.2.BUILD-SNAPSHOT +verifierVersion=1.2.3.BUILD-SNAPSHOT BOM_VERSION=Edgware.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/restdocs/http-server/pom.xml b/samples/standalone/restdocs/http-server/pom.xml index 30d397def8..b616b98e8e 100644 --- a/samples/standalone/restdocs/http-server/pom.xml +++ b/samples/standalone/restdocs/http-server/pom.xml @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT diff --git a/samples/standalone/restdocs/pom.xml b/samples/standalone/restdocs/pom.xml index 2c4d67c86b..5a6bde26a6 100644 --- a/samples/standalone/restdocs/pom.xml +++ b/samples/standalone/restdocs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-samples-standalone - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. @@ -17,7 +17,7 @@ Spring Cloud Contract Standalone Test Samples used for end to end tests - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT diff --git a/samples/wiremock-jetty/pom.xml b/samples/wiremock-jetty/pom.xml index 73d9cc3b16..d9dc48c634 100644 --- a/samples/wiremock-jetty/pom.xml +++ b/samples/wiremock-jetty/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-jetty - 0.0.1 + 1.2.3.BUILD-SNAPSHOT jar wiremock-jetty @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT diff --git a/samples/wiremock-native/pom.xml b/samples/wiremock-native/pom.xml index 585eed522c..24bd756aa4 100644 --- a/samples/wiremock-native/pom.xml +++ b/samples/wiremock-native/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-native - 0.0.1 + 1.2.3.BUILD-SNAPSHOT jar wiremock-native @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT diff --git a/samples/wiremock-tomcat/pom.xml b/samples/wiremock-tomcat/pom.xml index f6a56b68e9..e8e78e28e8 100644 --- a/samples/wiremock-tomcat/pom.xml +++ b/samples/wiremock-tomcat/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-tomcat - 0.0.1 + 1.2.3.BUILD-SNAPSHOT jar wiremock-tomcat @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT diff --git a/samples/wiremock-undertow-ssl/pom.xml b/samples/wiremock-undertow-ssl/pom.xml index 1593507618..0400878714 100644 --- a/samples/wiremock-undertow-ssl/pom.xml +++ b/samples/wiremock-undertow-ssl/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-undertow-ssl - 0.0.1 + 1.2.3.BUILD-SNAPSHOT jar wiremock-undertow-ssl @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT diff --git a/samples/wiremock-undertow/pom.xml b/samples/wiremock-undertow/pom.xml index c226c4646d..7e2c63b94b 100644 --- a/samples/wiremock-undertow/pom.xml +++ b/samples/wiremock-undertow/pom.xml @@ -4,7 +4,7 @@ 4.0.0 wiremock-undertow - 0.0.1 + 1.2.3.BUILD-SNAPSHOT jar wiremock-undertow @@ -20,7 +20,7 @@ UTF-8 1.8 - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT diff --git a/spring-cloud-contract-dependencies/pom.xml b/spring-cloud-contract-dependencies/pom.xml index d4afd8f28f..1e573d1a9d 100644 --- a/spring-cloud-contract-dependencies/pom.xml +++ b/spring-cloud-contract-dependencies/pom.xml @@ -9,7 +9,7 @@ spring-cloud-contract-dependencies - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT pom spring-cloud-contract-dependencies Spring Cloud Contract Dependencies diff --git a/spring-cloud-contract-shade/pom.xml b/spring-cloud-contract-shade/pom.xml index 1b678eceba..0cb67c891f 100644 --- a/spring-cloud-contract-shade/pom.xml +++ b/spring-cloud-contract-shade/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-shade diff --git a/spring-cloud-contract-spec/pom.xml b/spring-cloud-contract-spec/pom.xml index 7bc987e897..b499d74fca 100644 --- a/spring-cloud-contract-spec/pom.xml +++ b/spring-cloud-contract-spec/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-spec diff --git a/spring-cloud-contract-starters/pom.xml b/spring-cloud-contract-starters/pom.xml index 3a9f1a8ff8..966d89e907 100644 --- a/spring-cloud-contract-starters/pom.xml +++ b/spring-cloud-contract-starters/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. diff --git a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner-jetty/pom.xml b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner-jetty/pom.xml index 9b899e9e1f..0bf81ce9b0 100644 --- a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner-jetty/pom.xml +++ b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner-jetty/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-starters - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-starter-contract-stub-runner-jetty diff --git a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml index 775e63461c..c09673084a 100644 --- a/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml +++ b/spring-cloud-contract-starters/spring-cloud-starter-contract-stub-runner/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-starters - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-starter-contract-stub-runner diff --git a/spring-cloud-contract-starters/spring-cloud-starter-contract-verifier/pom.xml b/spring-cloud-contract-starters/spring-cloud-starter-contract-verifier/pom.xml index 436bcbac20..c1f03297df 100644 --- a/spring-cloud-contract-starters/spring-cloud-starter-contract-verifier/pom.xml +++ b/spring-cloud-contract-starters/spring-cloud-starter-contract-verifier/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-starters - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-starter-contract-verifier diff --git a/spring-cloud-contract-stub-runner/pom.xml b/spring-cloud-contract-stub-runner/pom.xml index 45707881ec..7503c8a711 100644 --- a/spring-cloud-contract-stub-runner/pom.xml +++ b/spring-cloud-contract-stub-runner/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner diff --git a/spring-cloud-contract-tools/pom.xml b/spring-cloud-contract-tools/pom.xml index 656bd286ac..92999e3952 100644 --- a/spring-cloud-contract-tools/pom.xml +++ b/spring-cloud-contract-tools/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. diff --git a/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml index 2efc2d4918..e0e68ba5f0 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tools - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-converters diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties index 6e0df725d8..5258a2dd14 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/gradle.properties @@ -16,6 +16,6 @@ nexusUsername = nexusPassword = -verifierVersion=1.2.2.BUILD-SNAPSHOT +verifierVersion=1.2.3.BUILD-SNAPSHOT org.gradle.daemon=false aetherVersion=1.0.2.v20150114 \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml index b043a61f4f..0c9ddad425 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-tools - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties index 2f45204455..0cfa89e919 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/bootSimple/gradle.properties @@ -15,5 +15,5 @@ # wiremockVersion=2.12.0 jsonAssertVersion=0.4.10 -verifierVersion=1.2.2.BUILD-SNAPSHOT +verifierVersion=1.2.3.BUILD-SNAPSHOT diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties index a979b2c048..51cde3cb52 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/gradle.properties @@ -15,4 +15,4 @@ # wiremockVersion=2.12.0 jsonAssertVersion=0.4.10 -verifierVersion=1.2.2.BUILD-SNAPSHOT +verifierVersion=1.2.3.BUILD-SNAPSHOT diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties index 887e30a1a5..a97f9ada15 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/gradle.properties @@ -15,4 +15,4 @@ # wiremockVersion=2.12.0 jsonAssertVersion=0.4.10 -verifierVersion=1.2.2.BUILD-SNAPSHOT \ No newline at end of file +verifierVersion=1.2.3.BUILD-SNAPSHOT \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties index 2f45204455..0cfa89e919 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/gradle.properties @@ -15,5 +15,5 @@ # wiremockVersion=2.12.0 jsonAssertVersion=0.4.10 -verifierVersion=1.2.2.BUILD-SNAPSHOT +verifierVersion=1.2.3.BUILD-SNAPSHOT diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml index 47e1578ff7..80e8bc3fc2 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-tools - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. @@ -33,7 +33,7 @@ 1.7 - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT 2016 diff --git a/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/pom.xml index 010a145f03..215331f081 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-spec-pact/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tools - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-spec-pact diff --git a/spring-cloud-contract-verifier/pom.xml b/spring-cloud-contract-verifier/pom.xml index 1bfbef1670..c34abb4dd3 100644 --- a/spring-cloud-contract-verifier/pom.xml +++ b/spring-cloud-contract-verifier/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-verifier diff --git a/spring-cloud-contract-wiremock/pom.xml b/spring-cloud-contract-wiremock/pom.xml index d8917f583d..3e1fa5527f 100644 --- a/spring-cloud-contract-wiremock/pom.xml +++ b/spring-cloud-contract-wiremock/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-wiremock diff --git a/tests/pom.xml b/tests/pom.xml index 23fc5c696a..22c7801b02 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -7,7 +7,7 @@ org.springframework.cloud spring-cloud-contract-parent - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. diff --git a/tests/samples-messaging-amqp/pom.xml b/tests/samples-messaging-amqp/pom.xml index ae65414dcb..d6f89bb7eb 100644 --- a/tests/samples-messaging-amqp/pom.xml +++ b/tests/samples-messaging-amqp/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-sample-amqp diff --git a/tests/samples-messaging-camel/pom.xml b/tests/samples-messaging-camel/pom.xml index e21d43bcc6..0b1da02980 100644 --- a/tests/samples-messaging-camel/pom.xml +++ b/tests/samples-messaging-camel/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-sample-camel diff --git a/tests/samples-messaging-integration/pom.xml b/tests/samples-messaging-integration/pom.xml index 61cb05a88b..5054881e7b 100644 --- a/tests/samples-messaging-integration/pom.xml +++ b/tests/samples-messaging-integration/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-sample-integration diff --git a/tests/samples-messaging-spring/pom.xml b/tests/samples-messaging-spring/pom.xml index 05a2869d61..34b567c7ed 100644 --- a/tests/samples-messaging-spring/pom.xml +++ b/tests/samples-messaging-spring/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-sample-spring diff --git a/tests/samples-messaging-stream/pom.xml b/tests/samples-messaging-stream/pom.xml index 8853057412..b07400f348 100644 --- a/tests/samples-messaging-stream/pom.xml +++ b/tests/samples-messaging-stream/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-sample-stream diff --git a/tests/spring-cloud-contract-stub-runner-amqp/pom.xml b/tests/spring-cloud-contract-stub-runner-amqp/pom.xml index 5bdd5137b1..1220b32b71 100644 --- a/tests/spring-cloud-contract-stub-runner-amqp/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-amqp/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-amqp diff --git a/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml b/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml index a384873054..fbae3a382c 100644 --- a/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-boot-eureka/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-boot-eureka diff --git a/tests/spring-cloud-contract-stub-runner-boot-zookeeper/pom.xml b/tests/spring-cloud-contract-stub-runner-boot-zookeeper/pom.xml index f161ddb383..38e63532dc 100644 --- a/tests/spring-cloud-contract-stub-runner-boot-zookeeper/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-boot-zookeeper/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-boot-zookeeper diff --git a/tests/spring-cloud-contract-stub-runner-camel/pom.xml b/tests/spring-cloud-contract-stub-runner-camel/pom.xml index 43b9e29099..1f96746928 100644 --- a/tests/spring-cloud-contract-stub-runner-camel/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-camel/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-camel diff --git a/tests/spring-cloud-contract-stub-runner-context-path/pom.xml b/tests/spring-cloud-contract-stub-runner-context-path/pom.xml index 3d4d50809e..b5dbcef06d 100644 --- a/tests/spring-cloud-contract-stub-runner-context-path/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-context-path/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-context-path diff --git a/tests/spring-cloud-contract-stub-runner-integration/pom.xml b/tests/spring-cloud-contract-stub-runner-integration/pom.xml index 7f160c2f6b..17961042f4 100644 --- a/tests/spring-cloud-contract-stub-runner-integration/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-integration/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-integration diff --git a/tests/spring-cloud-contract-stub-runner-moco-contract-jar/pom.xml b/tests/spring-cloud-contract-stub-runner-moco-contract-jar/pom.xml index c1914d8fd4..b2e521b7a4 100644 --- a/tests/spring-cloud-contract-stub-runner-moco-contract-jar/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-moco-contract-jar/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-moco-contract-jar diff --git a/tests/spring-cloud-contract-stub-runner-moco/pom.xml b/tests/spring-cloud-contract-stub-runner-moco/pom.xml index 9e4e1aef0e..954d67c8c4 100644 --- a/tests/spring-cloud-contract-stub-runner-moco/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-moco/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-moco diff --git a/tests/spring-cloud-contract-stub-runner-stream/pom.xml b/tests/spring-cloud-contract-stub-runner-stream/pom.xml index c3ea352ff3..f093984552 100644 --- a/tests/spring-cloud-contract-stub-runner-stream/pom.xml +++ b/tests/spring-cloud-contract-stub-runner-stream/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-contract-tests - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. spring-cloud-contract-stub-runner-stream From b745d9a7e38c0ecaef96f3050206a63c7f98b55a Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Fri, 19 Jan 2018 00:27:15 +0100 Subject: [PATCH 12/14] YAML contracts (#522) with this change we allow the users to use YAML to define contracts fixes gh-300 gh-508 helps with #501 --- README.adoc | 166 +++++- docs/src/main/asciidoc/verifier_contract.adoc | 327 ++++++++-- docs/src/main/asciidoc/verifier_faq.adoc | 7 +- .../main/asciidoc/verifier_introduction.adoc | 44 +- .../src/main/asciidoc/verifier_messaging.adoc | 21 + docs/src/main/asciidoc/verifier_setup.adoc | 3 +- .../standalone/dsl/http-client/build.gradle | 2 +- .../standalone/dsl/http-server/build.gradle | 2 +- .../shouldReturnAFraudForTheName.groovy | 4 +- .../messaging/stream-sink/build.gradle | 2 +- .../messaging/stream-source/build.gradle | 2 +- .../shouldReturnOkWhenServiceIsHealthy.groovy | 11 - .../shouldReturnOkWhenServiceIsHealthy.yml | 6 + .../pact/pact-http-client/build.gradle | 2 +- .../pact/pact-http-server/build.gradle | 2 +- samples/standalone/pom.xml | 1 + .../restdocs/http-client/build.gradle | 2 +- .../restdocs/http-server/build.gradle | 2 +- samples/standalone/yml/http-client/.gitignore | 6 + .../yml/http-client/.mvn/jvm.config | 1 + .../yml/http-client/.mvn/maven.config | 1 + .../.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 49519 bytes .../.mvn/wrapper/maven-wrapper.properties | 1 + .../standalone/yml/http-client/README.adoc | 26 + .../standalone/yml/http-client/build.gradle | 64 ++ .../yml/http-client/gradle.properties | 2 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53324 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + samples/standalone/yml/http-client/gradlew | 164 ++++++ .../standalone/yml/http-client/gradlew.bat | 90 +++ samples/standalone/yml/http-client/mvnw | 234 ++++++++ samples/standalone/yml/http-client/mvnw.cmd | 145 +++++ samples/standalone/yml/http-client/pom.xml | 210 +++++++ .../yml/http-client/settings.gradle | 1 + .../java/com/example/loan/Application.java | 13 + .../example/loan/LoanApplicationService.java | 93 +++ .../java/com/example/loan/model/Client.java | 21 + .../example/loan/model/FraudCheckStatus.java | 5 + .../loan/model/FraudServiceRequest.java | 37 ++ .../loan/model/FraudServiceResponse.java | 30 + .../example/loan/model/LoanApplication.java | 44 ++ .../loan/model/LoanApplicationResult.java | 32 + .../loan/model/LoanApplicationStatus.java | 5 + .../java/com/example/loan/model/Response.java | 20 + .../src/main/resources/application.yml | 1 + ...oanApplicationServiceContextPathTests.java | 54 ++ .../loan/LoanApplicationServiceTests.java | 74 +++ .../test/resources/application-gradle.yaml | 3 + .../test/resources/application-test-repo.yaml | 3 + .../src/test/resources/application.yaml | 2 + samples/standalone/yml/http-server/.gitignore | 6 + .../yml/http-server/.mvn/jvm.config | 1 + .../yml/http-server/.mvn/maven.config | 1 + .../.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 49519 bytes .../.mvn/wrapper/maven-wrapper.properties | 1 + .../standalone/yml/http-server/README.adoc | 20 + .../standalone/yml/http-server/build.gradle | 78 +++ .../yml/http-server/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53324 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + samples/standalone/yml/http-server/gradlew | 164 ++++++ .../standalone/yml/http-server/gradlew.bat | 90 +++ samples/standalone/yml/http-server/mvnw | 234 ++++++++ samples/standalone/yml/http-server/mvnw.cmd | 145 +++++ samples/standalone/yml/http-server/pom.xml | 256 ++++++++ .../yml/http-server/settings.gradle | 1 + .../java/com/example/fraud/Application.java | 17 + .../fraud/FraudDetectionController.java | 40 ++ .../example/fraud/FraudNameController.java | 69 +++ .../example/fraud/FraudStatsController.java | 51 ++ .../com/example/fraud/model/FraudCheck.java | 32 + .../example/fraud/model/FraudCheckResult.java | 35 ++ .../example/fraud/model/FraudCheckStatus.java | 5 + .../src/main/resources/application.yml | 1 + .../java/com/example/fraud/FraudBase.java | 29 + .../java/com/example/fraud/FraudnameBase.java | 17 + .../fraud/shouldMarkClientAsFraud.yml | 52 ++ .../fraud/shouldMarkClientAsNotFraud.yaml | 25 + .../fraud/shouldReturnFraudStats.yml | 21 + .../shouldReturnAFraudForTheName.yml | 15 + .../shouldReturnNonFraudForTheName.yml | 18 + samples/standalone/yml/pom.xml | 40 ++ .../cloud/contract/spec/internal/Body.groovy | 3 +- .../spec/internal/BodyMatchers.groovy | 2 +- .../contract/spec/internal/Headers.groovy | 12 + .../spec/internal/ContractSpec.groovy | 9 + .../contract/stubrunner/StubRepository.java | 15 +- .../build.gradle | 2 +- .../sampleJerseyProject/build.gradle | 2 +- .../functionalTest/sampleProject/build.gradle | 2 +- .../scenarioProject/build.gradle | 2 +- .../maven/verifier/GenerateStubsMojo.java | 13 +- .../test/projects/plugin-extension/pom.xml | 2 +- .../test/projects/spring-boot-groovy/pom.xml | 2 +- .../test/projects/spring-boot-java/pom.xml | 2 +- spring-cloud-contract-verifier/pom.xml | 13 +- .../verifier/builder/JavaTestGenerator.groovy | 1 - .../verifier/builder/MethodBuilder.groovy | 18 +- .../verifier/converter/YamlContract.groovy | 177 ++++++ .../converter/YamlContractConverter.groovy | 556 ++++++++++++++++++ .../WireMockRequestStubStrategy.groovy | 10 +- .../verifier/file/ContractFileScanner.groovy | 12 +- .../verifier/converter/YamlContract.groovy | 38 -- .../converter/YamlContractConverter.groovy | 89 --- .../YamlContractConverterSpec.groovy | 374 +++++++++++- .../test/resources/META-INF/spring.factories | 4 - .../src/test/resources/contract.yml | 34 +- .../src/test/resources/yml/contract.yml | 69 +++ .../yml/contract_broken_request_headers.yml | 34 ++ .../yml/contract_broken_response_headers.yml | 12 + .../test/resources/yml/contract_from_file.yml | 7 + .../test/resources/yml/contract_matchers.yml | 124 ++++ .../test/resources/yml/contract_message.yml | 43 ++ .../yml/contract_message_input_message.yml | 23 + .../resources/yml/contract_message_method.yml | 17 + .../yml/contract_message_scenario1.yml | 10 + .../yml/contract_message_scenario2.yml | 13 + .../yml/contract_message_scenario3.yml | 8 + .../test/resources/yml/contract_multipart.yml | 29 + .../yml/contract_reference_request.yml | 30 + .../src/test/resources/yml/contract_rest.yml | 45 ++ .../resources/yml/contract_rest_with_path.yml | 47 ++ .../test/resources/yml/multiple_contracts.yml | 14 + .../src/test/resources/yml/request.json | 1 + .../src/test/resources/yml/response.json | 1 + 125 files changed, 5116 insertions(+), 272 deletions(-) delete mode 100644 samples/standalone/messaging/stream-source/src/test/resources/contracts/shouldReturnOkWhenServiceIsHealthy.groovy create mode 100644 samples/standalone/messaging/stream-source/src/test/resources/contracts/shouldReturnOkWhenServiceIsHealthy.yml create mode 100644 samples/standalone/yml/http-client/.gitignore create mode 100644 samples/standalone/yml/http-client/.mvn/jvm.config create mode 100644 samples/standalone/yml/http-client/.mvn/maven.config create mode 100644 samples/standalone/yml/http-client/.mvn/wrapper/maven-wrapper.jar create mode 100644 samples/standalone/yml/http-client/.mvn/wrapper/maven-wrapper.properties create mode 100644 samples/standalone/yml/http-client/README.adoc create mode 100644 samples/standalone/yml/http-client/build.gradle create mode 100644 samples/standalone/yml/http-client/gradle.properties create mode 100644 samples/standalone/yml/http-client/gradle/wrapper/gradle-wrapper.jar create mode 100644 samples/standalone/yml/http-client/gradle/wrapper/gradle-wrapper.properties create mode 100755 samples/standalone/yml/http-client/gradlew create mode 100644 samples/standalone/yml/http-client/gradlew.bat create mode 100755 samples/standalone/yml/http-client/mvnw create mode 100644 samples/standalone/yml/http-client/mvnw.cmd create mode 100644 samples/standalone/yml/http-client/pom.xml create mode 100644 samples/standalone/yml/http-client/settings.gradle create mode 100644 samples/standalone/yml/http-client/src/main/java/com/example/loan/Application.java create mode 100644 samples/standalone/yml/http-client/src/main/java/com/example/loan/LoanApplicationService.java create mode 100644 samples/standalone/yml/http-client/src/main/java/com/example/loan/model/Client.java create mode 100644 samples/standalone/yml/http-client/src/main/java/com/example/loan/model/FraudCheckStatus.java create mode 100644 samples/standalone/yml/http-client/src/main/java/com/example/loan/model/FraudServiceRequest.java create mode 100644 samples/standalone/yml/http-client/src/main/java/com/example/loan/model/FraudServiceResponse.java create mode 100644 samples/standalone/yml/http-client/src/main/java/com/example/loan/model/LoanApplication.java create mode 100644 samples/standalone/yml/http-client/src/main/java/com/example/loan/model/LoanApplicationResult.java create mode 100644 samples/standalone/yml/http-client/src/main/java/com/example/loan/model/LoanApplicationStatus.java create mode 100644 samples/standalone/yml/http-client/src/main/java/com/example/loan/model/Response.java create mode 100644 samples/standalone/yml/http-client/src/main/resources/application.yml create mode 100644 samples/standalone/yml/http-client/src/test/java/com/example/loan/LoanApplicationServiceContextPathTests.java create mode 100644 samples/standalone/yml/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java create mode 100644 samples/standalone/yml/http-client/src/test/resources/application-gradle.yaml create mode 100644 samples/standalone/yml/http-client/src/test/resources/application-test-repo.yaml create mode 100644 samples/standalone/yml/http-client/src/test/resources/application.yaml create mode 100644 samples/standalone/yml/http-server/.gitignore create mode 100644 samples/standalone/yml/http-server/.mvn/jvm.config create mode 100644 samples/standalone/yml/http-server/.mvn/maven.config create mode 100644 samples/standalone/yml/http-server/.mvn/wrapper/maven-wrapper.jar create mode 100644 samples/standalone/yml/http-server/.mvn/wrapper/maven-wrapper.properties create mode 100644 samples/standalone/yml/http-server/README.adoc create mode 100644 samples/standalone/yml/http-server/build.gradle create mode 100644 samples/standalone/yml/http-server/gradle.properties create mode 100644 samples/standalone/yml/http-server/gradle/wrapper/gradle-wrapper.jar create mode 100644 samples/standalone/yml/http-server/gradle/wrapper/gradle-wrapper.properties create mode 100755 samples/standalone/yml/http-server/gradlew create mode 100644 samples/standalone/yml/http-server/gradlew.bat create mode 100755 samples/standalone/yml/http-server/mvnw create mode 100644 samples/standalone/yml/http-server/mvnw.cmd create mode 100644 samples/standalone/yml/http-server/pom.xml create mode 100644 samples/standalone/yml/http-server/settings.gradle create mode 100644 samples/standalone/yml/http-server/src/main/java/com/example/fraud/Application.java create mode 100644 samples/standalone/yml/http-server/src/main/java/com/example/fraud/FraudDetectionController.java create mode 100644 samples/standalone/yml/http-server/src/main/java/com/example/fraud/FraudNameController.java create mode 100644 samples/standalone/yml/http-server/src/main/java/com/example/fraud/FraudStatsController.java create mode 100644 samples/standalone/yml/http-server/src/main/java/com/example/fraud/model/FraudCheck.java create mode 100644 samples/standalone/yml/http-server/src/main/java/com/example/fraud/model/FraudCheckResult.java create mode 100644 samples/standalone/yml/http-server/src/main/java/com/example/fraud/model/FraudCheckStatus.java create mode 100644 samples/standalone/yml/http-server/src/main/resources/application.yml create mode 100644 samples/standalone/yml/http-server/src/test/java/com/example/fraud/FraudBase.java create mode 100644 samples/standalone/yml/http-server/src/test/java/com/example/fraud/FraudnameBase.java create mode 100644 samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.yml create mode 100644 samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsNotFraud.yaml create mode 100644 samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldReturnFraudStats.yml create mode 100644 samples/standalone/yml/http-server/src/test/resources/contracts/fraudname/shouldReturnAFraudForTheName.yml create mode 100644 samples/standalone/yml/http-server/src/test/resources/contracts/fraudname/shouldReturnNonFraudForTheName.yml create mode 100644 samples/standalone/yml/pom.xml create mode 100644 spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContract.groovy create mode 100644 spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy delete mode 100644 spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContract.groovy delete mode 100644 spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/contract.yml create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/contract_broken_request_headers.yml create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/contract_broken_response_headers.yml create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/contract_from_file.yml create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/contract_matchers.yml create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/contract_message.yml create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/contract_message_input_message.yml create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/contract_message_method.yml create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario1.yml create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario2.yml create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario3.yml create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/contract_multipart.yml create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/contract_reference_request.yml create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/contract_rest.yml create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/contract_rest_with_path.yml create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/multiple_contracts.yml create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/request.json create mode 100644 spring-cloud-contract-verifier/src/test/resources/yml/response.json diff --git a/README.adoc b/README.adoc index 0e9d56353c..98e3fa145e 100644 --- a/README.adoc +++ b/README.adoc @@ -130,6 +130,7 @@ Assume that you want to send a request containing the ID of a client company and amount it wants to borrow from us. You also want to send it to the /fraudcheck url via the PUT method. +.Groovy DSL [source,groovy,indent=0] ---- package contracts @@ -191,6 +192,63 @@ From the Producer perspective, in the autogenerated producer-side test: */ ---- +.YAML +[source,yml,indent=0] +---- +request: # (1) + method: PUT # (2) + url: /fraudcheck # (3) + body: # (4) + "client.id": 1234567890 + loanAmount: 99999 + headers: # (5) + Content-Type: application/json + matchers: + body: + - path: $.['client.id'] # (6) + type: by_regex + value: "[0-9]{10}" +response: # (7) + status: 200 # (8) + body: # (9) + fraudCheckStatus: "FRAUD" + "rejection.reason": "Amount too high" + headers: # (10) + Content-Type: application/json;charset=UTF-8 + + +#From the Consumer perspective, when shooting a request in the integration test: +# +#(1) - If the consumer sends a request +#(2) - With the "PUT" method +#(3) - to the URL "/fraudcheck" +#(4) - with the JSON body that +# * has a field `clientId` +# * has a field `loanAmount` that is equal to `99999` +#(5) - with header `Content-Type` equal to `application/json` +#(6) - and a `client.id` json entry matches the regular expression `[0-9]{10}` +#(7) - then the response will be sent with +#(8) - status equal `200` +#(9) - and JSON body equal to +# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" } +#(10) - with header `Content-Type` equal to `application/json` +# +#From the Producer perspective, in the autogenerated producer-side test: +# +#(1) - A request will be sent to the producer +#(2) - With the "PUT" method +#(3) - to the URL "/fraudcheck" +#(4) - with the JSON body that +# * has a field `clientId` `1234567890` +# * has a field `loanAmount` that is equal to `99999` +#(5) - with header `Content-Type` equal to `application/json` +#(7) - then the test will assert if the response has been sent with +#(8) - status equal `200` +#(9) - and JSON body equal to +# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" } +#(10) - with header `Content-Type` equal to `application/json;charset=UTF-8` +---- + ==== Client Side Spring Cloud Contract generates stubs, which you can use during client-side testing. @@ -231,7 +289,7 @@ your application behaves in a different way, especially in production. To ensure that your application behaves the way you define in your stub, tests are generated from the stub you provide. -The autogenerated test looks like this: +The autogenerated test looks, more or less, like this: [source,java,indent=0] ---- @@ -418,7 +476,7 @@ clone it. [source,bash,indent=0] ---- -git clone https://your-git-server.com/server-side.git local-http-server-repo +$ git clone https://your-git-server.com/server-side.git local-http-server-repo ---- *Define the contract locally in the repo of Fraud Detection service.* @@ -429,6 +487,7 @@ your expectations. To do so, write the following contract: IMPORTANT: Place the contract under `src/test/resources/contracts/fraud` folder. The `fraud` folder is important because the producer's test base class name references that folder. +.Groovy DSL [source,groovy,indent=0] ---- package contracts @@ -490,8 +549,66 @@ From the Producer perspective, in the autogenerated producer-side test: */ ---- -The Contract is written using a statically typed Groovy DSL. You might wonder what about -those `value(client(...), server(...))` parts. By using this notation, Spring Cloud +.YAML +[source,yml,indent=0] +---- +request: # (1) + method: PUT # (2) + url: /fraudcheck # (3) + body: # (4) + "client.id": 1234567890 + loanAmount: 99999 + headers: # (5) + Content-Type: application/json + matchers: + body: + - path: $.['client.id'] # (6) + type: by_regex + value: "[0-9]{10}" +response: # (7) + status: 200 # (8) + body: # (9) + fraudCheckStatus: "FRAUD" + "rejection.reason": "Amount too high" + headers: # (10) + Content-Type: application/json;charset=UTF-8 + + +#From the Consumer perspective, when shooting a request in the integration test: +# +#(1) - If the consumer sends a request +#(2) - With the "PUT" method +#(3) - to the URL "/fraudcheck" +#(4) - with the JSON body that +# * has a field `clientId` +# * has a field `loanAmount` that is equal to `99999` +#(5) - with header `Content-Type` equal to `application/json` +#(6) - and a `client.id` json entry matches the regular expression `[0-9]{10}` +#(7) - then the response will be sent with +#(8) - status equal `200` +#(9) - and JSON body equal to +# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" } +#(10) - with header `Content-Type` equal to `application/json` +# +#From the Producer perspective, in the autogenerated producer-side test: +# +#(1) - A request will be sent to the producer +#(2) - With the "PUT" method +#(3) - to the URL "/fraudcheck" +#(4) - with the JSON body that +# * has a field `clientId` `1234567890` +# * has a field `loanAmount` that is equal to `99999` +#(5) - with header `Content-Type` equal to `application/json` +#(7) - then the test will assert if the response has been sent with +#(8) - status equal `200` +#(9) - and JSON body equal to +# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" } +#(10) - with header `Content-Type` equal to `application/json;charset=UTF-8` +---- + +The YML contract is quite straight-forward. However when you take a look at the Contract +written using a statically typed Groovy DSL - you might wonder what the +`value(client(...), server(...))` parts are. By using this notation, Spring Cloud Contract lets you define parts of a JSON block, a URL, etc., which are dynamic. In case of an identifier or a timestamp, you need not hardcode a value. You want to allow some different ranges of values. To enable ranges of values, you can set regular expressions @@ -565,8 +682,8 @@ stubs. You need to skip the test generation and execution. When you execute: [source,bash,indent=0] ---- -cd local-http-server-repo -./mvnw clean install -DskipTests +$ cd local-http-server-repo +$ ./mvnw clean install -DskipTests ---- In the logs, you see something like this: @@ -691,8 +808,8 @@ return new FraudCheckResult(FraudCheckStatus.OK, NO_REASON); [source,bash,indent=0] ---- -git checkout -b contract-change-pr master -git pull https://your-git-server.com/server-side-fork.git contract-change-pr +$ git checkout -b contract-change-pr master +$ git pull https://your-git-server.com/server-side-fork.git contract-change-pr ---- You must add the dependencies needed by the autogenerated tests: @@ -802,8 +919,9 @@ public void validate_shouldMarkClientAsFraud() throws Exception { } ---- -As you can see, all the `producer()` parts of the Contract that were present in the +If you used the Groovy DSL, you can see, all the `producer()` parts of the Contract that were present in the `value(consumer(...), producer(...))` blocks got injected into the test. +In case of using YAML, the same applied for the `matchers` sections of the `response`. Note that, on the producer side, you are also doing TDD. The expectations are expressed in the form of a test. This test sends a request to our own application with the URL, @@ -837,9 +955,9 @@ Once you finish your work, you can deploy your change. First, merge the branch: [source,bash,indent=0] ---- -git checkout master -git merge --no-ff contract-change-pr -git push origin master +$ git checkout master +$ git merge --no-ff contract-change-pr +$ git push origin master ---- Your CI might run something like `./mvnw clean deploy`, which would publish both the @@ -853,8 +971,8 @@ As a developer of the Loan Issuance service (a consumer of the Fraud Detection s [source,bash,indent=0] ---- -git checkout master -git merge --no-ff contract-change-pr +$ git checkout master +$ git merge --no-ff contract-change-pr ---- *Work online.* @@ -1149,6 +1267,23 @@ Spring Boot embedded servers, and Wiremock itself has "native" support for a par version of Jetty (currently 9.2). To use the native Jetty, you need to add the native Wiremock dependencies and exclude the Spring Boot container (if there is one). +=== Customization of WireMock configuration + +You can register a bean of `org.springframework.cloud.contract.wiremock.WireMockConfigurationCustomizer` type +in order to customize the WireMock configuration (e.g. add custom transformers). +Example: + +[source,java,indent=0] +---- + @Bean WireMockConfigurationCustomizer optionsCustomizer() { + return new WireMockConfigurationCustomizer() { + @Override public void customize(WireMockConfiguration options) { +// perform your customization here + } + }; + } +---- + === Generating Stubs using REST Docs https://projects.spring.io/spring-restdocs[Spring REST Docs] can be used to generate @@ -1300,12 +1435,13 @@ Consider the following test: .accept(MediaType.APPLICATION_PDF) .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON) - .content("{\"foo\": 23 }")) + .content("{\"foo\": 23, \"bar\" : \"baz\" }")) .andExpect(status().isOk()) .andExpect(content().string("bar")) // first WireMock .andDo(WireMockRestDocs.verify() .jsonPath("$[?(@.foo >= 20)]") + .jsonPath("$[?(@.bar in ['baz','bazz','bazzz'])]") .contentType(MediaType.valueOf("application/json")) .stub("shouldGrantABeerIfOldEnough")) // then Contract DSL documentation diff --git a/docs/src/main/asciidoc/verifier_contract.adoc b/docs/src/main/asciidoc/verifier_contract.adoc index 3233e7f23d..718a5bad2f 100644 --- a/docs/src/main/asciidoc/verifier_contract.adoc +++ b/docs/src/main/asciidoc/verifier_contract.adoc @@ -1,33 +1,36 @@ == Contract DSL -IMPORTANT: Remember that, inside the contract file, you have to provide the fully +Spring Cloud Contract supports out of the box 2 types of DSL. One written in +`Groovy` and one written in `YAML`. + +If you decide to write the contract in Groovy, do not be alarmed if you have not used Groovy +before. Knowledge of the language is not really needed, as the Contract DSL uses only a +tiny subset of it (only literals, method calls and closures). Also, the DSL is statically +typed, to make it programmer-readable without any knowledge of the DSL itself. + +IMPORTANT: Remember that, inside the Groovy contract file, you have to provide the fully qualified name to the `Contract` class and `make` static imports, such as `org.springframework.cloud.spec.Contract.make { ... }`. You can also provide an import to the `Contract` class: `import org.springframework.cloud.spec.Contract` and then call `Contract.make { ... }`. -Contract DSL is written in Groovy, but do not be alarmed if you have not used Groovy -before. Knowledge of the language is not really needed, as the Contract DSL uses only a -tiny subset of it (only literals, method calls and closures). Also, the DSL is statically -typed, to make it programmer-readable without any knowledge of the DSL itself. - TIP: Spring Cloud Contract supports defining multiple contracts in a single file. -The Contract is present in the `spring-cloud-contract-spec` module of the -https://github.com/spring-cloud/spring-cloud-contract/tree/master/spring-cloud-contract-verifier[Spring -Cloud Contract Verifier repository]. - -The following is a complete example of a contract definition: +The following is a complete example of a Groovy contract definition: [source,groovy,indent=0] ---- include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy[tags=dsl_example,indent=0] ---- -NOTE: The preceding example does not contain all the features of the DSL appear. The -remainder of this section describes the other features. +The following is a complete example of a YAML contract definition: -You can compile Contracts to WireMock stubs mapping using standalone maven command: +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract_rest.yml[indent=0] +---- + +TIP: You can compile contracts to stubs mapping using standalone maven command: `mvn org.springframework.cloud:spring-cloud-contract-maven-plugin:convert` === Limitations @@ -41,7 +44,7 @@ to turn it on, please set the value of the following system property to `true`: You can also provide the `assertJsonSize` property in the plugin configuration. WARNING: Because JSON structure can have any form, it can be impossible to parse it -properly when using the `value(consumer(...), producer(...))` notation in `GString`. That +properly when using the Groovy DSL and the `value(consumer(...), producer(...))` notation in `GString`. That is why you should use the Groovy Map notation. === Common Top-Level elements @@ -60,11 +63,18 @@ The following sections describe the most common top-level elements: You can add a `description` to your contract. The description is arbitrary text. The following code shows an example: +.Groovy DSL [source,groovy,indent=0] ---- include::{contract_spec_path}/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy[tags=description,indent=0] ---- +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract_rest.yml[indent=0] +---- + [[contract-dsl-name]] ==== Name @@ -78,17 +88,36 @@ generated test not compile. Also, remember that, if you provide the same name fo multiple contracts, your autogenerated tests fail to compile and your generated stubs override each other. +.Groovy DSL +[source,groovy,indent=0] +---- +include::{contract_spec_path}/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy[tags=name,indent=0] +---- + +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=name,indent=0] +---- + [[contract-dsl-ignoring-contracts]] ==== Ignoring Contracts If you want to ignore a contract, you can either set a value of ignored contracts in the plugin configuration or set the `ignored` property on the contract itself: +.Groovy DSL [source,groovy,indent=0] ---- include::{contract_spec_path}/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy[tags=ignored,indent=0] ---- +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=ignored,indent=0] +---- + [[contract-dsl-passing-values-from-files]] ==== Passing Values from Files @@ -108,11 +137,18 @@ following resources in our project. Further assume that your contract is as follows: +.Groovy DSL [source,groovy,indent=0] ---- include::{verifier_core_path}/src/test/resources/classpath/readFromFile.groovy[indent=0] ---- +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract_from_file.yml[indent=0] +---- + Further assume that the JSON files is as follows: *request.json* @@ -128,9 +164,8 @@ include::{verifier_core_path}/src/test/resources/classpath/response.json[indent= ---- When test or stub generation takes place, the contents of the file is passed to the body -of a request or a response. That works because of the `file(...)` method. The argument of -that method needs to be a file with location relative to the folder in which the contract -lays. +of a request or a response. The name of the file needs to be a file with location +relative to the folder in which the contract lays. [[contract-dsl-http-top-level-elements]] ==== HTTP Top-Level Elements @@ -138,62 +173,124 @@ lays. The following methods can be called in the top-level closure of a contract definition. `request` and `response` are mandatory. `priority` is optional. +.Groovy DSL [source,groovy,indent=0] ---- include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=http_dsl,indent=0] ---- +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=priority,indent=0] +include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=request,indent=0] +... +include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=response,indent=0] +... +---- + +IMPORTANT: If you want to make your contract have a **higher** value of priority +you need to pass a **lower** number to the `priority` tag / method. E.g. `priority` with +value `5` has **higher** priority than `priority` with value `10`. + === Request -The HTTP protocol requires only **method and address** to be specified in a request. The +The HTTP protocol requires only **method and url** to be specified in a request. The same information is mandatory in request definition of the Contract. +.Groovy DSL [source,groovy,indent=0] ---- include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=request,indent=0] ---- +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=request_obligatory,indent=0] +---- + It is possible to specify an absolute rather than relative `url`, but using `urlPath` is the recommended way, as doing so makes the tests **host-independent**. +.Groovy DSL [source,groovy,indent=0] ---- include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=url,indent=0] - ---- -`request` may contain **query parameters**, which are specified in a closure nested in a -call to `urlPath` or `url`. +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract_rest_with_path.yml[tags=url_path,indent=0] +---- +`request` may contain **query parameters**. + +.Groovy DSL [source,groovy,indent=0] ---- include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=urlpath,indent=0] ---- +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=request,indent=0] +... +include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=query_params,indent=0] +---- + `request` may contain additional **request headers**, as shown in the following example: +.Groovy DSL [source,groovy,indent=0] ---- include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=headers,indent=0] ---- -`request` may contain a **request body**, as shown in the following example: +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=request,indent=0] +... +include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=headers,indent=0] +---- +`request` may contain a **request body**: + +.Groovy DSL [source,groovy,indent=0] ---- include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=body,indent=0] ---- -`request` may contain **multipart** elements. To include multipart elements, call the -`multipart()` method, as shown in the following example +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=request,indent=0] +... +include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=body,indent=0] +---- +`request` may contain **multipart** elements. To include multipart elements, use the +`multipart` method/section, as shown in the following examples + +.Groovy DSL [source,groovy,indent=0] ---- include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy[tags=multipartdsl,indent=0] ---- +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract_multipart.yml[indent=0] +---- + In the preceding example, we define parameters in either of two ways: +.Groovy DSL * Directly, by using the map notation, where the value can be a dynamic property (such as `formParameter: $(consumer(...), producer(...))`). * By using the `named(...)` method that lets you set a named parameter. A named parameter @@ -201,6 +298,20 @@ can set a `name` and `content`. You can call it either via a method with two ar such as `named("fileName", "fileContent")`, or via a map notation, such as `named(name: "fileName", content: "fileContent")`. +.YAML +* The multipart parameters are set via `multipart.params` section +* The named parameters (the `fileName` and `fileContent` for a given parameter name) +can be set via the `multipart.named` section. That section contains +the `paramName` (name of the parameter), `fileName` (name of the file), +`fileContent` (content of the file) fields +* The dynamic bits can be set via the `matchers.multipart` section +** for parameters use the `params` section that can accept +`regex` or a `predefined` regular expression +** for named params use the `named` section where first you +define the parameter name via `paramName` and then you can pass the +parametrization of either `fileName` or `fileContent` via +`regex` or a `predefined` regular expression + From this contract, the generated test is as follows: [source,java,indent=0] @@ -227,17 +338,26 @@ The WireMock stub is as follows: include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/WireMockGroovyDslSpec.groovy[tags=multipartwiremock,indent=0] ---- - === Response The response must contain an **HTTP status code** and may contain other information. The following code shows an example: +.Groovy DSL [source,groovy,indent=0] ---- include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[tags=response,indent=0] ---- +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=response,indent=0] +... +include::{verifier_core_path}/src/test/resources/yml/contract.yml[tags=response_obligatory,indent=0] +---- + + Besides status, the response may contain **headers** and a **body**, both of which are specified the same way as in the request (see the previous paragraph). @@ -245,12 +365,19 @@ specified the same way as in the request (see the previous paragraph). The contract can contain some dynamic properties: timestamps, IDs, and so on. You do not want to force the consumers to stub their clocks to always return the same value of time -so that it gets matched by the stub. You can provide the dynamic parts in your contracts +so that it gets matched by the stub. + +For Groovy DSL you can provide the dynamic parts in your contracts in two ways: pass them directly in the body or set them in separate sections called `testMatchers` and `stubMatchers`. +For YAML you can only use the `matchers` section. + ==== Dynamic properties inside the body +IMPORTANT: This section is valid only for Groovy DSL. Check out the +<> section for YAML examples of a similar feature. + You can set the properties inside the body either with the `value` method or, if you use the Groovy map notation, with `$()`. The following example shows how to set dynamic properties with the value method: @@ -278,6 +405,9 @@ method. Subsequent sections take a closer look at what you can do with those val ==== Regular expressions +IMPORTANT: This section is valid only for Groovy DSL. Check out the +<> section for YAML examples of a similar feature. + You can use regular expressions to write your requests in Contract DSL. Doing so is particularly useful when you want to indicate that a given response should be provided for requests that follow a given pattern. Also, you can use regular expressions when you @@ -319,6 +449,9 @@ include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract ==== Passing Optional Parameters +IMPORTANT: This section is valid only for Groovy DSL. Check out the +<> section for YAML examples of a similar feature. + It is possible to provide optional parameters in your contract. However, you can provide optional parameters only for the following: @@ -351,6 +484,9 @@ include::{plugins_path}/spring-cloud-contract-converters/src/test/groovy/org/spr ==== Executing Custom Methods on the Server Side +IMPORTANT: This section is valid only for Groovy DSL. Check out the +<> section for YAML examples of a similar feature. + You can define a method call that executes on the server side during the test. Such a method can be added to the class defined as "baseClassForTests" in the configuration. The following code shows an example of the contract portion of the test case: @@ -414,7 +550,9 @@ It should resemble the following code: ==== Referencing the Request from the Response The best situation is to provide fixed values, but sometimes you need to reference a -request in your response. To do so, you can use the `fromRequest()` method, which lets +request in your response. + +If you're writing contracts using Groovy DSL, you can use the `fromRequest()` method, which lets you reference a bunch of elements from the HTTP request. You can use the following options: @@ -430,13 +568,36 @@ given name. * `fromRequest().body(String jsonPath)`: Returns the element from the request that matches the JSON Path. +If you're using the YAML contract definition you have to use the +http://handlebarsjs.com/[Handlebars] `{{{ }}}` notation with custom, Spring Cloud Contract + functions to achieve this. + +* `{{{ request.url }}}`: Returns the request URL and query parameters. +* `{{{ request.query.key.[index] }}}`: Returns the nth query parameter with a given name. +E.g. for key `foo`, first entry `{{{ request.query.foo.[0] }}}` +* `{{{ request.path }}}`: Returns the full path. +* `{{{ request.path.[index] }}}`: Returns the nth path element. E.g. +for first entry ```{{{ request.path.[0] }}} +* `{{{ request.headers.key }}}`: Returns the first header with a given name. +* `{{{ request.headers.key.[index] }}}`: Returns the nth header with a given name. +* `{{{ request.body }}}`: Returns the full request body. +* `{{{ jsonpath this 'your.json.path' }}}`: Returns the element from the request that +matches the JSON Path. E.g. for json path `$.foo` - `{{{ jsonpath this '$.foo' }}}` + Consider the following contract: +.Groovy DSL [source,groovy,indent=0] ---- include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderSpec.groovy[tags=template_contract,indent=0] ---- +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract_reference_request.yml[indent=0] +---- + Running a JUnit test generation leads to a test that resembles the following example: [source,java,indent=0] @@ -561,6 +722,7 @@ include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract IMPORTANT: Remember to override the `applyGlobally()` method and set it to `false` if you want the transformation to be applied only for a mapping that explicitly requires it. +[[contract-matchers]] ==== Dynamic Properties in the Matchers Sections If you work with https://docs.pact.io/[Pact], the following discussion may seem familiar. @@ -577,6 +739,8 @@ contract. Currently, Spring Cloud Contract Verifier supports only JSON Path-based matchers with the following matching possibilities: +.Groovy DSL + * For `stubMatchers`: ** `byEquality()`: The value taken from the response via the provided JSON Path must be equal to the value provided in the contract. @@ -615,13 +779,63 @@ following, depending on the JSON path: *** `Number`: If you point to `Integer`, `Double`, or other kind of number. *** `Boolean`: If you point to a `Boolean`. +.YAML + +_Please read the Groovy section for detailed explanation of +what the types mean_ + +For YAML the structure of a matcher looks like this + +[source,yml,indent=0] +---- +- path: $.foo + type: by_regex + value: bar +---- + +Or if you want to use one of the predefined regular expressions +`[only_alpha_unicode, number, any_boolean, ip_address, hostname, +email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_empty, non_blank]`: + +[source,yml,indent=0] +---- +- path: $.foo + type: by_regex + predefined: only_alpha_unicode +---- + +Below you can find the allowed list of `type`s. + +* For `stubMatchers`: +** `by_equality` +** `by_regex` +** `by_date` +** `by_timestamp` +** `by_time` +* For `testMatchers`: +** `by_equality` +** `by_regex` +** `by_date` +** `by_timestamp` +** `by_time` +** `by_type` +*** there are 2 additional fields accepted: `minOccurrence` and `maxOccurrence`. +** `by_command` + Consider the following example: +.Groovy DSL [source,groovy,indent=0] ---- include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MockMvcMethodBodyBuilderWithMatchersSpec.groovy[tags=matchers,indent=0] ---- +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract_matchers.yml[indent=0] +---- + In the preceding example, you can see the dynamic portions of the contract in the `matchers` sections. For the request part, you can see that, for all fields but `valueWithoutAMatcher`, the values of the regular expressions that the stub should @@ -784,6 +998,7 @@ If you're using asynchronous communication on the server side (your controllers returning `Callable`, `DeferredResult`, and so on), then, inside your contract, you must provide a `sync()` method in the `response` section. The following code shows an example: +.Groovy DSL [source,groovy,indent=0] ---- org.springframework.cloud.contract.spec.Contract.make { @@ -799,6 +1014,13 @@ org.springframework.cloud.contract.spec.Contract.make { } ---- +.YAML +[source,yml,indent=0] +---- +response: + async: true +---- + === Working with Context Paths Spring Cloud Contract supports context paths. @@ -871,11 +1093,18 @@ following sections explain the differences: The output message can be triggered by calling a method (such as a `Scheduler` when a was started and a message was sent), as shown in the following example: +.Groovy DSL [source,groovy] ---- include::{tests_path}/samples-messaging-integration/src/test/groovy/com/example/IntegrationMessagingApplicationSpec.groovy[tags=method_trigger,indent=0] ---- +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract_message_method.yml[indent=0] +---- + In the previous example case, the output message is sent to `output` if a method called `bookReturnedTriggered` is executed. On the message *publisher's* side, we generate a test that calls that method to trigger the message. On the *consumer* side, you can use @@ -887,11 +1116,18 @@ the `some_label` to trigger the message. The output message can be triggered by receiving a message, as shown in the following example: +.Groovy DSL [source,groovy] ---- include::{tests_path}/samples-messaging-integration/src/test/groovy/com/example/IntegrationMessagingApplicationSpec.groovy[tags=message_trigger,indent=0] ---- +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract_message_input_message.yml[indent=0] +---- + In the preceding example, the output message is sent to `output` if a proper message is received on the `input` destination. On the message *publisher's* side, the engine generates a test that sends the input message to the defined destination. On the @@ -901,6 +1137,8 @@ generates a test that sends the input message to the defined destination. On the [[contract-dsl-consumer-producer]] ==== Consumer/Producer +IMPORTANT: This section is valid only for Groovy DSL. + In HTTP, you have a notion of `client`/`stub and `server`/`test` notation. You can also use those paradigms in messaging. In addition, Spring Cloud Contract Verifier also provides the `consumer` and `producer` methods, as presented in the following example @@ -915,21 +1153,28 @@ include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract [[contract-dsl-common]] ==== Common -In the `input {}` or `outputMessage {}` section you can call `assertThat` with the name +In the `input` or `outputMessage` section you can call `assertThat` with the name of a `method` (e.g. `assertThatMessageIsOnTheQueue()`) that you have defined in the -base class or in a static import. Spring Cloud Pipelines will execute that method -in the genertaed test. +base class or in a static import. Spring Cloud Contract will execute that method +in the generated test. === Multiple Contracts in One File You can define multiple contracts in one file. Such a contract might resemble the following example: +.Groovy DSL [source,groovy,indent=0] ---- include::{plugins_path}/spring-cloud-contract-maven-plugin/src/test/projects/multiple-contracts/src/test/resources/contracts/com/hello/v1/WithList.groovy[lines=18..-1,indent=0] ---- +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/multiple_contracts.yml[indent=0] +---- + In the preceding example, one contract has the `name` field and the other does not. This leads to generation of two tests that look more or less like this: @@ -1001,6 +1246,8 @@ your tests far more meaningful. == Customization +IMPORTANT: This section is valid only for Groovy DSL + You can customize the Spring Cloud Contract Verifier by extending the DSL, as shown in the remainder of this section. @@ -1102,13 +1349,6 @@ can generate stubs for other HTTP server implementations). === Custom Contract Converter -Assume that your contract is written in a YAML file as follows: - -[source,yml] ----- -include::{verifier_core_path}/src/test/resources/contract.yml[indent=0] ----- - The `ContractConverter` interface lets you register your own implementation of a contract structure converter. The following code listing shows the `ContractConverter` interface: @@ -1128,15 +1368,8 @@ The following example shows a typical `spring.factories` file: [source] ---- -include::{verifier_core_path}/src/test/resources/META-INF/spring.factories[indent=0] ----- - -The following example shows a typical YAML implementation that matches the preceding -example: - -[source,groovy] ----- -include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy[indent=0,lines=16..-1] +org.springframework.cloud.contract.spec.ContractConverter=\ +org.springframework.cloud.contract.verifier.converter.YamlContractConverter ---- ==== Pact Converter diff --git a/docs/src/main/asciidoc/verifier_faq.adoc b/docs/src/main/asciidoc/verifier_faq.adoc index 845d07dce1..d39a5206a4 100644 --- a/docs/src/main/asciidoc/verifier_faq.adoc +++ b/docs/src/main/asciidoc/verifier_faq.adoc @@ -15,6 +15,10 @@ Spring Cloud Contract Verifier stand out on the "market" of Consumer Driven Cont - Stub Runner functionality - the stubs are automatically downloaded at runtime from Nexus / Artifactory - Spring Cloud integration - no discovery service is needed for integration tests +=== I don't want to write a contract in Groovy! + +No problem. You can write a contract in YAML! + === What is this value(consumer(), producer()) ? One of the biggest challenges related to stubs is their reusability. Only if they can be vastly used, will they serve their purpose. @@ -370,4 +374,5 @@ for this for WireMock. In case of other HTTP server stubs you'll have to impleme ==== Can I reference text from file? Yes! With version 1.2.0 we've added such a possibility. It's enough to call `file(...)` method in the -DSL and provide a path relative to where the contract lays. \ No newline at end of file +DSL and provide a path relative to where the contract lays. +If you're using YAML just use the `bodyFromFile` property. \ No newline at end of file diff --git a/docs/src/main/asciidoc/verifier_introduction.adoc b/docs/src/main/asciidoc/verifier_introduction.adoc index 1fadbffad2..4e9b2d4259 100644 --- a/docs/src/main/asciidoc/verifier_introduction.adoc +++ b/docs/src/main/asciidoc/verifier_introduction.adoc @@ -106,11 +106,18 @@ Assume that you want to send a request containing the ID of a client company and amount it wants to borrow from us. You also want to send it to the /fraudcheck url via the PUT method. +.Groovy DSL [source,groovy,indent=0] ---- include::{introduction_url}/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.groovy[] ---- +.YAML +[source,yml,indent=0] +---- +include::{introduction_url}/samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.yml[] +---- + ==== Client Side Spring Cloud Contract generates stubs, which you can use during client-side testing. @@ -144,7 +151,7 @@ your application behaves in a different way, especially in production. To ensure that your application behaves the way you define in your stub, tests are generated from the stub you provide. -The autogenerated test looks like this: +The autogenerated test looks, more or less, like this: [source,java,indent=0] ---- @@ -260,7 +267,7 @@ clone it. [source,bash,indent=0] ---- -git clone https://your-git-server.com/server-side.git local-http-server-repo +$ git clone https://your-git-server.com/server-side.git local-http-server-repo ---- *Define the contract locally in the repo of Fraud Detection service.* @@ -271,13 +278,21 @@ your expectations. To do so, write the following contract: IMPORTANT: Place the contract under `src/test/resources/contracts/fraud` folder. The `fraud` folder is important because the producer's test base class name references that folder. +.Groovy DSL [source,groovy,indent=0] ---- include::{introduction_url}/samples/standalone/dsl/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.groovy[] ---- -The Contract is written using a statically typed Groovy DSL. You might wonder what about -those `value(client(...), server(...))` parts. By using this notation, Spring Cloud +.YAML +[source,yml,indent=0] +---- +include::{introduction_url}/samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.yml[] +---- + +The YML contract is quite straight-forward. However when you take a look at the Contract +written using a statically typed Groovy DSL - you might wonder what the +`value(client(...), server(...))` parts are. By using this notation, Spring Cloud Contract lets you define parts of a JSON block, a URL, etc., which are dynamic. In case of an identifier or a timestamp, you need not hardcode a value. You want to allow some different ranges of values. To enable ranges of values, you can set regular expressions @@ -333,8 +348,8 @@ stubs. You need to skip the test generation and execution. When you execute: [source,bash,indent=0] ---- -cd local-http-server-repo -./mvnw clean install -DskipTests +$ cd local-http-server-repo +$ ./mvnw clean install -DskipTests ---- In the logs, you see something like this: @@ -440,8 +455,8 @@ include::{introduction_url}/samples/standalone/dsl/http-server/src/main/java/com [source,bash,indent=0] ---- -git checkout -b contract-change-pr master -git pull https://your-git-server.com/server-side-fork.git contract-change-pr +$ git checkout -b contract-change-pr master +$ git pull https://your-git-server.com/server-side-fork.git contract-change-pr ---- You must add the dependencies needed by the autogenerated tests: @@ -511,8 +526,9 @@ public void validate_shouldMarkClientAsFraud() throws Exception { } ---- -As you can see, all the `producer()` parts of the Contract that were present in the +If you used the Groovy DSL, you can see, all the `producer()` parts of the Contract that were present in the `value(consumer(...), producer(...))` blocks got injected into the test. +In case of using YAML, the same applied for the `matchers` sections of the `response`. Note that, on the producer side, you are also doing TDD. The expectations are expressed in the form of a test. This test sends a request to our own application with the URL, @@ -543,9 +559,9 @@ Once you finish your work, you can deploy your change. First, merge the branch: [source,bash,indent=0] ---- -git checkout master -git merge --no-ff contract-change-pr -git push origin master +$ git checkout master +$ git merge --no-ff contract-change-pr +$ git push origin master ---- Your CI might run something like `./mvnw clean deploy`, which would publish both the @@ -559,8 +575,8 @@ As a developer of the Loan Issuance service (a consumer of the Fraud Detection s [source,bash,indent=0] ---- -git checkout master -git merge --no-ff contract-change-pr +$ git checkout master +$ git merge --no-ff contract-change-pr ---- *Work online.* diff --git a/docs/src/main/asciidoc/verifier_messaging.adoc b/docs/src/main/asciidoc/verifier_messaging.adoc index 911429fdef..368a1344ef 100644 --- a/docs/src/main/asciidoc/verifier_messaging.adoc +++ b/docs/src/main/asciidoc/verifier_messaging.adoc @@ -89,11 +89,18 @@ it is resolved as a channel name. For *Camel*, that's a certain component (for e Here is an example for Camel. For the given contract: +.Groovy DSL [source,groovy] ---- include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_method_dsl] ---- +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario1.yml[indent=0] +---- + The following JUnit test is created: [source,groovy] @@ -112,11 +119,18 @@ include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract Here is an example for Camel. For the given contract: +.Groovy DSL [source,groovy] ---- include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_message_dsl] ---- +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario2.yml[indent=0] +---- + The following JUnit test is created: [source,groovy] @@ -135,11 +149,18 @@ include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract Here is an example for Camel. For the given contract: +.Groovy DSL [source,groovy] ---- include::{verifier_core_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/MessagingMethodBodyBuilderSpec.groovy[tags=trigger_no_output_dsl] ---- +.YAML +[source,yml,indent=0] +---- +include::{verifier_core_path}/src/test/resources/yml/contract_message_scenario3.yml[indent=0] +---- + The following JUnit test is created: [source,groovy] diff --git a/docs/src/main/asciidoc/verifier_setup.adoc b/docs/src/main/asciidoc/verifier_setup.adoc index 9237889ca0..baed9d65c8 100644 --- a/docs/src/main/asciidoc/verifier_setup.adoc +++ b/docs/src/main/asciidoc/verifier_setup.adoc @@ -875,7 +875,8 @@ exclude the unwanted dependencies. You can handle scenarios with Spring Cloud Contract Verifier. All you need to do is to stick to the proper naming convention while creating your contracts. The convention -requires including an order number followed by an underscore, as shown in this example: +requires including an order number followed by an underscore. This will work regardles + of whether you're working with YAML or Groovy. Example: [source,indent=0] ---- diff --git a/samples/standalone/dsl/http-client/build.gradle b/samples/standalone/dsl/http-client/build.gradle index 8c205cbed3..752be56a53 100644 --- a/samples/standalone/dsl/http-client/build.gradle +++ b/samples/standalone/dsl/http-client/build.gradle @@ -7,7 +7,7 @@ buildscript { maven { url "http://repo.spring.io/release" } } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE" } } diff --git a/samples/standalone/dsl/http-server/build.gradle b/samples/standalone/dsl/http-server/build.gradle index 708e19075e..95237b337a 100644 --- a/samples/standalone/dsl/http-server/build.gradle +++ b/samples/standalone/dsl/http-server/build.gradle @@ -9,7 +9,7 @@ buildscript { } // end::repos[] dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE" classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${findProperty('verifierVersion') ?: verifierVersion}" } } diff --git a/samples/standalone/dsl/http-server/src/test/resources/contracts/fraudname/shouldReturnAFraudForTheName.groovy b/samples/standalone/dsl/http-server/src/test/resources/contracts/fraudname/shouldReturnAFraudForTheName.groovy index 31c3d0e215..524caa690b 100644 --- a/samples/standalone/dsl/http-server/src/test/resources/contracts/fraudname/shouldReturnAFraudForTheName.groovy +++ b/samples/standalone/dsl/http-server/src/test/resources/contracts/fraudname/shouldReturnAFraudForTheName.groovy @@ -1,9 +1,9 @@ package contracts.fraudname org.springframework.cloud.contract.spec.Contract.make { + // highest priority + priority(1) request { - // highest priority - priority(1) method PUT() url '/frauds/name' body([ diff --git a/samples/standalone/messaging/stream-sink/build.gradle b/samples/standalone/messaging/stream-sink/build.gradle index 804e0a1224..aa3a71be22 100644 --- a/samples/standalone/messaging/stream-sink/build.gradle +++ b/samples/standalone/messaging/stream-sink/build.gradle @@ -7,7 +7,7 @@ buildscript { maven { url "http://repo.spring.io/release" } } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE" } } diff --git a/samples/standalone/messaging/stream-source/build.gradle b/samples/standalone/messaging/stream-source/build.gradle index f37f6bd09c..d44e965526 100644 --- a/samples/standalone/messaging/stream-source/build.gradle +++ b/samples/standalone/messaging/stream-source/build.gradle @@ -7,7 +7,7 @@ buildscript { maven { url "http://repo.spring.io/release" } } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE" classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${findProperty('verifierVersion') ?: verifierVersion}" classpath "com.jayway.restassured:rest-assured:2.9.0" classpath "com.jayway.restassured:spring-mock-mvc:2.9.0" diff --git a/samples/standalone/messaging/stream-source/src/test/resources/contracts/shouldReturnOkWhenServiceIsHealthy.groovy b/samples/standalone/messaging/stream-source/src/test/resources/contracts/shouldReturnOkWhenServiceIsHealthy.groovy deleted file mode 100644 index 462d11ae48..0000000000 --- a/samples/standalone/messaging/stream-source/src/test/resources/contracts/shouldReturnOkWhenServiceIsHealthy.groovy +++ /dev/null @@ -1,11 +0,0 @@ -package contracts - -org.springframework.cloud.contract.spec.Contract.make { - request { - method(GET()) - url("/foo") - } - response { - status(200) - } -} diff --git a/samples/standalone/messaging/stream-source/src/test/resources/contracts/shouldReturnOkWhenServiceIsHealthy.yml b/samples/standalone/messaging/stream-source/src/test/resources/contracts/shouldReturnOkWhenServiceIsHealthy.yml new file mode 100644 index 0000000000..6360b45b39 --- /dev/null +++ b/samples/standalone/messaging/stream-source/src/test/resources/contracts/shouldReturnOkWhenServiceIsHealthy.yml @@ -0,0 +1,6 @@ +description: Should return 200 for /foo +request: + url: /foo + method: GET +response: + status: 200 \ No newline at end of file diff --git a/samples/standalone/pact/pact-http-client/build.gradle b/samples/standalone/pact/pact-http-client/build.gradle index f477e647d2..0d73f96f4d 100644 --- a/samples/standalone/pact/pact-http-client/build.gradle +++ b/samples/standalone/pact/pact-http-client/build.gradle @@ -7,7 +7,7 @@ buildscript { maven { url "http://repo.spring.io/release" } } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE" } } diff --git a/samples/standalone/pact/pact-http-server/build.gradle b/samples/standalone/pact/pact-http-server/build.gradle index 6833482dc8..1edf17f642 100644 --- a/samples/standalone/pact/pact-http-server/build.gradle +++ b/samples/standalone/pact/pact-http-server/build.gradle @@ -7,7 +7,7 @@ buildscript { maven { url "http://repo.spring.io/release" } } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE" classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${findProperty('verifierVersion') ?: verifierVersion}" //tag::pact_dependency[] classpath "org.springframework.cloud:spring-cloud-contract-spec-pact:${findProperty('verifierVersion') ?: verifierVersion}" diff --git a/samples/standalone/pom.xml b/samples/standalone/pom.xml index 3d10bea281..e4729d4b2d 100644 --- a/samples/standalone/pom.xml +++ b/samples/standalone/pom.xml @@ -26,6 +26,7 @@ dsl messaging pact + yml diff --git a/samples/standalone/restdocs/http-client/build.gradle b/samples/standalone/restdocs/http-client/build.gradle index 612371779f..3457d39d16 100644 --- a/samples/standalone/restdocs/http-client/build.gradle +++ b/samples/standalone/restdocs/http-client/build.gradle @@ -7,7 +7,7 @@ buildscript { maven { url "http://repo.spring.io/release" } } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE" } } diff --git a/samples/standalone/restdocs/http-server/build.gradle b/samples/standalone/restdocs/http-server/build.gradle index f4c0302c39..7a23f84dfb 100644 --- a/samples/standalone/restdocs/http-server/build.gradle +++ b/samples/standalone/restdocs/http-server/build.gradle @@ -10,7 +10,7 @@ buildscript { maven { url "http://repo.spring.io/plugins-staging-local/" } } dependencies { - classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE" + classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE" } } diff --git a/samples/standalone/yml/http-client/.gitignore b/samples/standalone/yml/http-client/.gitignore new file mode 100644 index 0000000000..b05ba7bd6d --- /dev/null +++ b/samples/standalone/yml/http-client/.gitignore @@ -0,0 +1,6 @@ + +target/ + +.gradle +build/ + diff --git a/samples/standalone/yml/http-client/.mvn/jvm.config b/samples/standalone/yml/http-client/.mvn/jvm.config new file mode 100644 index 0000000000..894bef17a5 --- /dev/null +++ b/samples/standalone/yml/http-client/.mvn/jvm.config @@ -0,0 +1 @@ +-Xmx1024m -XX:MaxPermSize=256m -Djava.awt.headless=true \ No newline at end of file diff --git a/samples/standalone/yml/http-client/.mvn/maven.config b/samples/standalone/yml/http-client/.mvn/maven.config new file mode 100644 index 0000000000..affad39a42 --- /dev/null +++ b/samples/standalone/yml/http-client/.mvn/maven.config @@ -0,0 +1 @@ +-T2 \ No newline at end of file diff --git a/samples/standalone/yml/http-client/.mvn/wrapper/maven-wrapper.jar b/samples/standalone/yml/http-client/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..c6feb8bb6f76f2553e266ff8bf8867105154237e GIT binary patch literal 49519 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%0A$pwhjXzyA0ZzErH{1@M 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 literal 0 HcmV?d00001 diff --git a/samples/standalone/yml/http-client/.mvn/wrapper/maven-wrapper.properties b/samples/standalone/yml/http-client/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000000..6637cedb28 --- /dev/null +++ b/samples/standalone/yml/http-client/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip \ No newline at end of file diff --git a/samples/standalone/yml/http-client/README.adoc b/samples/standalone/yml/http-client/README.adoc new file mode 100644 index 0000000000..cdcb0e4a56 --- /dev/null +++ b/samples/standalone/yml/http-client/README.adoc @@ -0,0 +1,26 @@ += Http Client + +== Prerequisites + +First you have to publish to Maven Local the stubs of the *http-server* module + +== How to run it? + +Run + +[source=groovy] +-------- +./gradlew clean build +-------- + +or + +-------- +./mvnw clean package +-------- + +To + +- build the app +- use spring-cloud-contract-stub-runner-spring[Stub Runner Spring] to download the stub of `Http Server` +- run the tests against stubbed server \ No newline at end of file diff --git a/samples/standalone/yml/http-client/build.gradle b/samples/standalone/yml/http-client/build.gradle new file mode 100644 index 0000000000..752be56a53 --- /dev/null +++ b/samples/standalone/yml/http-client/build.gradle @@ -0,0 +1,64 @@ +buildscript { + repositories { + mavenCentral() + mavenLocal() + maven { url "http://repo.spring.io/snapshot" } + maven { url "http://repo.spring.io/milestone" } + maven { url "http://repo.spring.io/release" } + } + dependencies { + classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE" + } +} + +group = 'com.example' +version = '0.0.1-SNAPSHOT' + +repositories { + mavenCentral() + mavenLocal() + maven { url "http://repo.spring.io/snapshot" } + maven { url "http://repo.spring.io/milestone" } + maven { url "http://repo.spring.io/release" } +} + +apply plugin: 'groovy' +apply plugin: 'org.springframework.boot' +apply plugin: 'maven-publish' + +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:$BOM_VERSION" + } +} + +dependencies { + compile("org.springframework.boot:spring-boot-starter-web") + compile("org.springframework.boot:spring-boot-starter-actuator") + + testCompile "org.springframework.cloud:spring-cloud-starter-contract-stub-runner" +} + +test { + systemProperty 'spring.profiles.active', 'gradle' + testLogging { + exceptionFormat = 'full' + } +} + +task wrapper(type: Wrapper) { + gradleVersion = '2.14' +} + +task resolveDependencies { + doLast { + project.rootProject.allprojects.each { subProject -> + subProject.buildscript.configurations.each { configuration -> + configuration.resolve() + } + subProject.configurations.each { configuration -> + configuration.resolve() + } + } + } +} diff --git a/samples/standalone/yml/http-client/gradle.properties b/samples/standalone/yml/http-client/gradle.properties new file mode 100644 index 0000000000..0c444e9a19 --- /dev/null +++ b/samples/standalone/yml/http-client/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.daemon=false +BOM_VERSION=Edgware.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/yml/http-client/gradle/wrapper/gradle-wrapper.jar b/samples/standalone/yml/http-client/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..3baa851b28c65f87dd36a6748e1a85cf360c1301 GIT binary patch literal 53324 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

*Ou)7mZ_?&1& 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 literal 0 HcmV?d00001 diff --git a/samples/standalone/yml/http-client/gradle/wrapper/gradle-wrapper.properties b/samples/standalone/yml/http-client/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..ff3de523bd --- /dev/null +++ b/samples/standalone/yml/http-client/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Aug 19 15:39:05 CEST 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.0-bin.zip diff --git a/samples/standalone/yml/http-client/gradlew b/samples/standalone/yml/http-client/gradlew new file mode 100755 index 0000000000..27309d9231 --- /dev/null +++ b/samples/standalone/yml/http-client/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/samples/standalone/yml/http-client/gradlew.bat b/samples/standalone/yml/http-client/gradlew.bat new file mode 100644 index 0000000000..832fdb6079 --- /dev/null +++ b/samples/standalone/yml/http-client/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/samples/standalone/yml/http-client/mvnw b/samples/standalone/yml/http-client/mvnw new file mode 100755 index 0000000000..fc7efd17d0 --- /dev/null +++ b/samples/standalone/yml/http-client/mvnw @@ -0,0 +1,234 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # + # Look for the Apple JDKs first to preserve the existing behaviour, and then look + # for the new JDKs provided by Oracle. + # + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then + # + # Oracle JDKs + # + export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then + # + # Apple JDKs + # + export JAVA_HOME=`/usr/libexec/java_home` + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + local basedir=$(pwd) + local wdir=$(pwd) + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + wdir=$(cd "$wdir/.."; pwd) + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CMD_LINE_ARGS + diff --git a/samples/standalone/yml/http-client/mvnw.cmd b/samples/standalone/yml/http-client/mvnw.cmd new file mode 100644 index 0000000000..001048081d --- /dev/null +++ b/samples/standalone/yml/http-client/mvnw.cmd @@ -0,0 +1,145 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %* + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/samples/standalone/yml/http-client/pom.xml b/samples/standalone/yml/http-client/pom.xml new file mode 100644 index 0000000000..b768a828d7 --- /dev/null +++ b/samples/standalone/yml/http-client/pom.xml @@ -0,0 +1,210 @@ + + + 4.0.0 + + com.example + http-client-yml + 0.0.1-SNAPSHOT + + Spring Cloud Contract Verifier Http Client Sample + Spring Cloud Contract Verifier Http Client Sample + + + org.springframework.boot + spring-boot-starter-parent + 1.5.9.RELEASE + + + + + UTF-8 + 1.8 + Edgware.BUILD-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.apache.httpcomponents + httpclient + + + + + org.springframework.cloud + spring-cloud-starter-contract-stub-runner + test + + + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud-dependencies.version} + pom + import + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + maven-deploy-plugin + 2.8.2 + + true + + + + org.apache.maven.plugins + maven-clean-plugin + 3.0.0 + + + + build + + + target + + + + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + spring-releases + Spring Releases + https://repo.spring.io/release + + false + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + spring-releases + Spring Releases + https://repo.spring.io/release + + false + + + + + + + integration + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + + gradle + test + + ./gradlew + + clean + build + publishToMavenLocal + -PverifierVersion=${spring-cloud-contract.version} + + + + exec + + + + + + + + + windows + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + + gradle + test + + gradlew.bat + + clean + build + publishToMavenLocal + -PverifierVersion=${spring-cloud-contract.version} + + + + exec + + + + + + + + + + diff --git a/samples/standalone/yml/http-client/settings.gradle b/samples/standalone/yml/http-client/settings.gradle new file mode 100644 index 0000000000..e12fd21d66 --- /dev/null +++ b/samples/standalone/yml/http-client/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'http-client-yml-gradle' diff --git a/samples/standalone/yml/http-client/src/main/java/com/example/loan/Application.java b/samples/standalone/yml/http-client/src/main/java/com/example/loan/Application.java new file mode 100644 index 0000000000..6f36301172 --- /dev/null +++ b/samples/standalone/yml/http-client/src/main/java/com/example/loan/Application.java @@ -0,0 +1,13 @@ +package com.example.loan; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/samples/standalone/yml/http-client/src/main/java/com/example/loan/LoanApplicationService.java b/samples/standalone/yml/http-client/src/main/java/com/example/loan/LoanApplicationService.java new file mode 100644 index 0000000000..cf26b27f35 --- /dev/null +++ b/samples/standalone/yml/http-client/src/main/java/com/example/loan/LoanApplicationService.java @@ -0,0 +1,93 @@ +package com.example.loan; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import com.example.loan.model.FraudCheckStatus; +import com.example.loan.model.FraudServiceRequest; +import com.example.loan.model.FraudServiceResponse; +import com.example.loan.model.LoanApplication; +import com.example.loan.model.LoanApplicationResult; +import com.example.loan.model.LoanApplicationStatus; +import com.example.loan.model.Response; + +@Service +public class LoanApplicationService { + + private final RestTemplate restTemplate; + + private int port = 6565; + + @Autowired + public LoanApplicationService(RestTemplateBuilder builder) { + this.restTemplate = builder.build(); + } + + public LoanApplicationResult loanApplication(LoanApplication loanApplication) { + FraudServiceRequest request = + new FraudServiceRequest(loanApplication); + + FraudServiceResponse response = + sendRequestToFraudDetectionService(request); + + return buildResponseFromFraudResult(response); + } + + private FraudServiceResponse sendRequestToFraudDetectionService( + FraudServiceRequest request) { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + + // tag::client_call_server[] + ResponseEntity response = + restTemplate.exchange("http://localhost:" + port + "/fraudcheck", HttpMethod.PUT, + new HttpEntity<>(request, httpHeaders), + FraudServiceResponse.class); + // end::client_call_server[] + + return response.getBody(); + } + + private LoanApplicationResult buildResponseFromFraudResult(FraudServiceResponse response) { + LoanApplicationStatus applicationStatus = null; + if (FraudCheckStatus.OK == response.getFraudCheckStatus()) { + applicationStatus = LoanApplicationStatus.LOAN_APPLIED; + } else if (FraudCheckStatus.FRAUD == response.getFraudCheckStatus()) { + applicationStatus = LoanApplicationStatus.LOAN_APPLICATION_REJECTED; + } + + return new LoanApplicationResult(applicationStatus, response.getRejectionReason()); + } + + public int countAllFrauds() { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + ResponseEntity response = + restTemplate.exchange("http://localhost:" + port + "/frauds", HttpMethod.GET, + new HttpEntity<>(httpHeaders), + Response.class); + return response.getBody().getCount(); + } + + public int countDrunks() { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + ResponseEntity response = + restTemplate.exchange("http://localhost:" + port + "/drunks", HttpMethod.GET, + new HttpEntity<>(httpHeaders), + Response.class); + return response.getBody().getCount(); + } + + public void setPort(int port) { + this.port = port; + } + +} diff --git a/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/Client.java b/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/Client.java new file mode 100644 index 0000000000..abf66106b5 --- /dev/null +++ b/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/Client.java @@ -0,0 +1,21 @@ +package com.example.loan.model; + +public class Client { + + private String pesel; + + public Client() { + } + + public Client(String pesel) { + this.pesel = pesel; + } + + public String getPesel() { + return pesel; + } + + public void setPesel(String pesel) { + this.pesel = pesel; + } +} diff --git a/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/FraudCheckStatus.java b/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/FraudCheckStatus.java new file mode 100644 index 0000000000..c5217ef471 --- /dev/null +++ b/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/FraudCheckStatus.java @@ -0,0 +1,5 @@ +package com.example.loan.model; + +public enum FraudCheckStatus { + OK, FRAUD +} diff --git a/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/FraudServiceRequest.java b/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/FraudServiceRequest.java new file mode 100644 index 0000000000..5cc40027bb --- /dev/null +++ b/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/FraudServiceRequest.java @@ -0,0 +1,37 @@ +package com.example.loan.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.math.BigDecimal; + +public class FraudServiceRequest { + + @JsonProperty("client.id") + private String clientId; + + private BigDecimal loanAmount; + + public FraudServiceRequest() { + } + + public FraudServiceRequest(LoanApplication loanApplication) { + this.clientId = loanApplication.getClient().getPesel(); + this.loanAmount = loanApplication.getAmount(); + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public BigDecimal getLoanAmount() { + return loanAmount; + } + + public void setLoanAmount(BigDecimal loanAmount) { + this.loanAmount = loanAmount; + } +} diff --git a/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/FraudServiceResponse.java b/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/FraudServiceResponse.java new file mode 100644 index 0000000000..af58a03223 --- /dev/null +++ b/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/FraudServiceResponse.java @@ -0,0 +1,30 @@ +package com.example.loan.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class FraudServiceResponse { + + private FraudCheckStatus fraudCheckStatus; + + @JsonProperty("rejection.reason") + private String rejectionReason; + + public FraudServiceResponse() { + } + + public FraudCheckStatus getFraudCheckStatus() { + return fraudCheckStatus; + } + + public void setFraudCheckStatus(FraudCheckStatus fraudCheckStatus) { + this.fraudCheckStatus = fraudCheckStatus; + } + + public String getRejectionReason() { + return rejectionReason; + } + + public void setRejectionReason(String rejectionReason) { + this.rejectionReason = rejectionReason; + } +} diff --git a/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/LoanApplication.java b/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/LoanApplication.java new file mode 100644 index 0000000000..27d66f884c --- /dev/null +++ b/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/LoanApplication.java @@ -0,0 +1,44 @@ +package com.example.loan.model; + +import java.math.BigDecimal; + +public class LoanApplication { + + private Client client; + + private BigDecimal amount; + + private String loanApplicationId; + + public LoanApplication() { + } + + public LoanApplication(Client client, double amount) { + this.client = client; + this.amount = BigDecimal.valueOf(amount); + } + + public Client getClient() { + return client; + } + + public void setClient(Client client) { + this.client = client; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public String getLoanApplicationId() { + return loanApplicationId; + } + + public void setLoanApplicationId(String loanApplicationId) { + this.loanApplicationId = loanApplicationId; + } +} diff --git a/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/LoanApplicationResult.java b/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/LoanApplicationResult.java new file mode 100644 index 0000000000..ffd44e1a26 --- /dev/null +++ b/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/LoanApplicationResult.java @@ -0,0 +1,32 @@ +package com.example.loan.model; + +public class LoanApplicationResult { + + private LoanApplicationStatus loanApplicationStatus; + + private String rejectionReason; + + public LoanApplicationResult() { + } + + public LoanApplicationResult(LoanApplicationStatus loanApplicationStatus, String rejectionReason) { + this.loanApplicationStatus = loanApplicationStatus; + this.rejectionReason = rejectionReason; + } + + public LoanApplicationStatus getLoanApplicationStatus() { + return loanApplicationStatus; + } + + public void setLoanApplicationStatus(LoanApplicationStatus loanApplicationStatus) { + this.loanApplicationStatus = loanApplicationStatus; + } + + public String getRejectionReason() { + return rejectionReason; + } + + public void setRejectionReason(String rejectionReason) { + this.rejectionReason = rejectionReason; + } +} diff --git a/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/LoanApplicationStatus.java b/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/LoanApplicationStatus.java new file mode 100644 index 0000000000..34cf91a89f --- /dev/null +++ b/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/LoanApplicationStatus.java @@ -0,0 +1,5 @@ +package com.example.loan.model; + +public enum LoanApplicationStatus { + LOAN_APPLIED, LOAN_APPLICATION_REJECTED +} diff --git a/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/Response.java b/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/Response.java new file mode 100644 index 0000000000..8335ee0cc6 --- /dev/null +++ b/samples/standalone/yml/http-client/src/main/java/com/example/loan/model/Response.java @@ -0,0 +1,20 @@ +package com.example.loan.model; + +public class Response { + private int count; + + public Response(int count) { + this.count = count; + } + + public Response() { + } + + public int getCount() { + return this.count; + } + + public void setCount(int count) { + this.count = count; + } +} \ No newline at end of file diff --git a/samples/standalone/yml/http-client/src/main/resources/application.yml b/samples/standalone/yml/http-client/src/main/resources/application.yml new file mode 100644 index 0000000000..4b7fe13158 --- /dev/null +++ b/samples/standalone/yml/http-client/src/main/resources/application.yml @@ -0,0 +1 @@ +server.port: 8090 \ No newline at end of file diff --git a/samples/standalone/yml/http-client/src/test/java/com/example/loan/LoanApplicationServiceContextPathTests.java b/samples/standalone/yml/http-client/src/test/java/com/example/loan/LoanApplicationServiceContextPathTests.java new file mode 100644 index 0000000000..522edf2998 --- /dev/null +++ b/samples/standalone/yml/http-client/src/test/java/com/example/loan/LoanApplicationServiceContextPathTests.java @@ -0,0 +1,54 @@ +package com.example.loan; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; + +import com.example.loan.model.Client; +import com.example.loan.model.LoanApplication; +import com.example.loan.model.LoanApplicationResult; +import com.example.loan.model.LoanApplicationStatus; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment=WebEnvironment.NONE, properties="server.context-path=/app") +@AutoConfigureStubRunner(ids = {"com.example:http-server-yml:+:stubs:6565"}, workOffline = true) +@DirtiesContext +public class LoanApplicationServiceContextPathTests { + + @Autowired + private LoanApplicationService service; + + @Test + public void shouldSuccessfullyApplyForLoan() { + // given: + LoanApplication application = new LoanApplication(new Client("1234567890"), + 123.123); + // when: + LoanApplicationResult loanApplication = service.loanApplication(application); + // then: + assertThat(loanApplication.getLoanApplicationStatus()) + .isEqualTo(LoanApplicationStatus.LOAN_APPLIED); + assertThat(loanApplication.getRejectionReason()).isNull(); + } + + @Test + public void shouldBeRejectedDueToAbnormalLoanAmount() { + // given: + LoanApplication application = new LoanApplication(new Client("1234567890"), + 99999); + // when: + LoanApplicationResult loanApplication = service.loanApplication(application); + // then: + assertThat(loanApplication.getLoanApplicationStatus()) + .isEqualTo(LoanApplicationStatus.LOAN_APPLICATION_REJECTED); + assertThat(loanApplication.getRejectionReason()).isEqualTo("Amount too high"); + } + +} diff --git a/samples/standalone/yml/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java b/samples/standalone/yml/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java new file mode 100644 index 0000000000..ada18d72bb --- /dev/null +++ b/samples/standalone/yml/http-client/src/test/java/com/example/loan/LoanApplicationServiceTests.java @@ -0,0 +1,74 @@ +package com.example.loan; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; + +import com.example.loan.model.Client; +import com.example.loan.model.LoanApplication; +import com.example.loan.model.LoanApplicationResult; +import com.example.loan.model.LoanApplicationStatus; + +import static org.assertj.core.api.Assertions.assertThat; + +// tag::autoconfigure_stubrunner[] +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment=WebEnvironment.NONE) +@AutoConfigureStubRunner(ids = {"com.example:http-server-yml:+:stubs:6565"}, workOffline = true) +@DirtiesContext +public class LoanApplicationServiceTests { +// end::autoconfigure_stubrunner[] + + @Autowired + private LoanApplicationService service; + + @Test + public void shouldSuccessfullyApplyForLoan() { + // given: + LoanApplication application = new LoanApplication(new Client("1234567890"), + 123.123); + // when: + LoanApplicationResult loanApplication = service.loanApplication(application); + // then: + assertThat(loanApplication.getLoanApplicationStatus()) + .isEqualTo(LoanApplicationStatus.LOAN_APPLIED); + assertThat(loanApplication.getRejectionReason()).isNull(); + } + + // tag::client_tdd[] + @Test + public void shouldBeRejectedDueToAbnormalLoanAmount() { + // given: + LoanApplication application = new LoanApplication(new Client("1234567890"), + 99999); + // when: + LoanApplicationResult loanApplication = service.loanApplication(application); + // then: + assertThat(loanApplication.getLoanApplicationStatus()) + .isEqualTo(LoanApplicationStatus.LOAN_APPLICATION_REJECTED); + assertThat(loanApplication.getRejectionReason()).isEqualTo("Amount too high"); + } + // end::client_tdd[] + + @Test + public void shouldSuccessfullyGetAllFrauds() { + // when: + int count = service.countAllFrauds(); + // then: + assertThat(count).isEqualTo(200); + } + + @Test + public void shouldSuccessfullyGetAllDrunks() { + // when: + int count = service.countDrunks(); + // then: + assertThat(count).isEqualTo(100); + } + +} diff --git a/samples/standalone/yml/http-client/src/test/resources/application-gradle.yaml b/samples/standalone/yml/http-client/src/test/resources/application-gradle.yaml new file mode 100644 index 0000000000..3ea79d4f5f --- /dev/null +++ b/samples/standalone/yml/http-client/src/test/resources/application-gradle.yaml @@ -0,0 +1,3 @@ +stubrunner: + work-offline: true + stubs.ids: 'com.example:http-server-dsl-gradle:+:stubs:6565' \ No newline at end of file diff --git a/samples/standalone/yml/http-client/src/test/resources/application-test-repo.yaml b/samples/standalone/yml/http-client/src/test/resources/application-test-repo.yaml new file mode 100644 index 0000000000..0fe0e54c25 --- /dev/null +++ b/samples/standalone/yml/http-client/src/test/resources/application-test-repo.yaml @@ -0,0 +1,3 @@ +stubrunner: + ids: 'com.example:http-server-dsl:+:stubs:8080' + repositoryRoot: http://repo.spring.io/libs-snapshot diff --git a/samples/standalone/yml/http-client/src/test/resources/application.yaml b/samples/standalone/yml/http-client/src/test/resources/application.yaml new file mode 100644 index 0000000000..21459f2953 --- /dev/null +++ b/samples/standalone/yml/http-client/src/test/resources/application.yaml @@ -0,0 +1,2 @@ +server: + port: 6565 diff --git a/samples/standalone/yml/http-server/.gitignore b/samples/standalone/yml/http-server/.gitignore new file mode 100644 index 0000000000..b05ba7bd6d --- /dev/null +++ b/samples/standalone/yml/http-server/.gitignore @@ -0,0 +1,6 @@ + +target/ + +.gradle +build/ + diff --git a/samples/standalone/yml/http-server/.mvn/jvm.config b/samples/standalone/yml/http-server/.mvn/jvm.config new file mode 100644 index 0000000000..894bef17a5 --- /dev/null +++ b/samples/standalone/yml/http-server/.mvn/jvm.config @@ -0,0 +1 @@ +-Xmx1024m -XX:MaxPermSize=256m -Djava.awt.headless=true \ No newline at end of file diff --git a/samples/standalone/yml/http-server/.mvn/maven.config b/samples/standalone/yml/http-server/.mvn/maven.config new file mode 100644 index 0000000000..7681bc67b9 --- /dev/null +++ b/samples/standalone/yml/http-server/.mvn/maven.config @@ -0,0 +1 @@ +-T2 diff --git a/samples/standalone/yml/http-server/.mvn/wrapper/maven-wrapper.jar b/samples/standalone/yml/http-server/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..c6feb8bb6f76f2553e266ff8bf8867105154237e GIT binary patch literal 49519 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%0A$pwhjXzyA0ZzErH{1@M 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 literal 0 HcmV?d00001 diff --git a/samples/standalone/yml/http-server/.mvn/wrapper/maven-wrapper.properties b/samples/standalone/yml/http-server/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000000..6637cedb28 --- /dev/null +++ b/samples/standalone/yml/http-server/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1 @@ +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip \ No newline at end of file diff --git a/samples/standalone/yml/http-server/README.adoc b/samples/standalone/yml/http-server/README.adoc new file mode 100644 index 0000000000..2fd3fbc40d --- /dev/null +++ b/samples/standalone/yml/http-server/README.adoc @@ -0,0 +1,20 @@ += Http Server + +Run + +[source=groovy] +-------- +./gradlew clean build publishToMavenLocal +-------- + +or + +-------- +./mvnw clean install +-------- + +To + +- build the app +- generate and run Spring Cloud Contract Verifier tests +- publish the fatJar and the stubs to Maven Local \ No newline at end of file diff --git a/samples/standalone/yml/http-server/build.gradle b/samples/standalone/yml/http-server/build.gradle new file mode 100644 index 0000000000..95237b337a --- /dev/null +++ b/samples/standalone/yml/http-server/build.gradle @@ -0,0 +1,78 @@ +// tag::repos[] +buildscript { + repositories { + mavenCentral() + mavenLocal() + maven { url "http://repo.spring.io/snapshot" } + maven { url "http://repo.spring.io/milestone" } + maven { url "http://repo.spring.io/release" } + } +// end::repos[] + dependencies { + classpath "org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE" + classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${findProperty('verifierVersion') ?: verifierVersion}" + } +} + +group = 'com.example' +version = '0.0.1-SNAPSHOT' + +// tag::deps_repos[] +repositories { + mavenCentral() + mavenLocal() + maven { url "http://repo.spring.io/snapshot" } + maven { url "http://repo.spring.io/milestone" } + maven { url "http://repo.spring.io/release" } +} +// end::deps_repos[] + +apply plugin: 'groovy' +apply plugin: 'org.springframework.boot' +apply plugin: 'spring-cloud-contract' +apply plugin: 'maven-publish' + +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:$BOM_VERSION" + } +} + +contracts { + packageWithBaseClasses = 'com.example.fraud' +} + +dependencies { + compile("org.springframework.boot:spring-boot-starter-web") + compile("org.springframework.boot:spring-boot-starter-actuator") + + testCompile 'org.springframework.cloud:spring-cloud-starter-contract-verifier' +} + +test { + systemProperty 'spring.profiles.active', 'gradle' + testLogging { + exceptionFormat = 'full' + } +} + +task wrapper(type: Wrapper) { + gradleVersion = '2.14' +} + +clean.doFirst { + delete "~/.m2/repository/com/example/http-server-dsl-gradle" +} + +task resolveDependencies { + doLast { + project.rootProject.allprojects.each { subProject -> + subProject.buildscript.configurations.each { configuration -> + configuration.resolve() + } + subProject.configurations.each { configuration -> + configuration.resolve() + } + } + } +} diff --git a/samples/standalone/yml/http-server/gradle.properties b/samples/standalone/yml/http-server/gradle.properties new file mode 100644 index 0000000000..b99fcb4df7 --- /dev/null +++ b/samples/standalone/yml/http-server/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.daemon=false +verifierVersion=1.2.2.BUILD-SNAPSHOT +BOM_VERSION=Edgware.BUILD-SNAPSHOT \ No newline at end of file diff --git a/samples/standalone/yml/http-server/gradle/wrapper/gradle-wrapper.jar b/samples/standalone/yml/http-server/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..3baa851b28c65f87dd36a6748e1a85cf360c1301 GIT binary patch literal 53324 zcmagFW0a=N(k5EAZQHhO+qUhlE<9!1wrzCTwrzJ=eR|IR&NuV!vu7sP%KOeAkt?wx zBCm+NQb`sR3D=FE`hl$X@}? zzZLC&6_giNkd_cvRb!Bs_$@y*DJM(MFb^+FPct($+oZy@#JYF?D@_Ls za{(|*Ju23rZpS1qJt{UC8)(5f$Id*%esH;W0sddcd=dNSF8qlk9qyO4D5f& zSh^US*_rgi(aD`#2h^x>>Q2F$e0;S?TlSr z{iEe!2AGgScdgiUXgwH%U{?XTzX+X(8Tf?lMD3uZr7L@~U=jBUhR~cZ`A+x=ru^z& z4xx!e2l}y0MEqJg(B4VpbF?zV{wG~Eg0{Hjl}O)Mi??j0w*GY1wj}V zGYIcakMI9)yF9!Tx`Oss2b}(HvDp7*jjG53|TiD&r%G|-t z+SJ(1(dA#8P@-z@h$4&>fI$^DI)6}MRFkr?;-hvP={RqM1053q%`9IjFDGbk3~E{H zY37*lQ1=*R&vp;_S`^(RltKiIdOJ9C#rJ!PkGJdP@O1SSXu`{eBqj(N797-;dW)kW zHU^DDRcqx2A62*AmIPt6zxAgt+&HGeUWGoOU6s#FYH6ULA1mV#niS0GeERD<*R_;Ug)z!n64rvWhne$!)XYb!975^6DNpGIS?iD z@>iJb34luVvZ~5<6hfl%ZW~igNt-bt4%FI(YvvLto`BF;Q)94b72~*?G1IlCTqN_n zkKK|G!}HZE1<88eLALMNn2idp4~X(D;1l3_4b*@97&IUI(%=TJp2-Fh^$>dWo^uKF z$69#L1#G{==_>fsiAytDsYm_@!^)MH{*<)wV38nE)3SBdPDxA(4^ic-WIifu=Bhlu zG{|t4tDZNs5dPsj^jNb%s#BOFn`(fD(K?ItA*TSRt&V+>-aN17#s@P^zW zqa1VGNBq5`kZt-)VPk|_#n9;-?xZZj#iUv+IX?$2mnn=zoGkU z8ra@F{s*k)_{LF*@(_A@y8_1g6662$C;!84Pp`CGEB|WS=HE*4pS4c<|1+(B>6CcP zufK)`2lz8}eu9SF4P)?Ra^GDA11hZaXwb&OA;Wv&CXfY5*dK<%n}Q8Bp7#0n+i#l? z2;*Ppqe z{3S%V`BQuPQglEoE4c(fsZu|2SBL~)#~HXYW$chye@>2SCyV|rI#CT1a#Diq2*J>s zw3l_iUVF5=?f(J#&y|8z_-yO_wJ+tr_C^1nS1S4cIxKZpa~CN`dviHQM_YHNe=Ryj zRaX&580}jkL4V~*rJAax)kYUgcU|XNeQ{{AGC~cC2wKrB6uOq7%S_wuQ?rN6#%5tu z;8BvDe^7;XEVygDW7@OV^<--5R!}eiL~EcjNO@}9t8kAxS2~;;%E&-M?==ERqMm5K zBPbrI2gyfSLd8mcKo(jSE=l+O`CXXCaAUmIvE*?PS;B>LZ#m+WaU!^nS zD&N&P!L%JiYu+6$XY_-^cn``U~K4{}|&=T`ZS%%V`_7HH{mFf$7g>K1hRAOa1A=w1=Mo4-lJhuF_d%r@>ei=#uD}(YG%< zCSYstx?J*_a8}54xAAY^K(@q+q5P!JjBT~o$!2=cS-0^lIm#~8Ju&%7yb;Apov1Fx zB&;Xk-6$P?ge*+fty6RJ7hANx!S>rQ)OAz~#vpwMQeAS#T- zpwu-JE1i7!5^(gkaSr{??U*ciS$`sdix2m^vz#IpQhF`O5o}4x(O{m#pZ5d9*o?VY zj08iwZCRQ11vU%K#1W$h z&?F2xA>qtunt*i@76o7hq@%19$5Qglx|6A1pZX}eyODj6hp3#UFq4SiX` zCMcya#RS!0dE^yhszal}@%Cs`u)4~mWwTe=-CYoIuvP;a|**P4j-@0vv zHyF(xO@wh(dD-;sC(V{b3S^m+vu%*e6`i3_HVPf^GOMmCfDev|B3jt zgD23#H@4;3c1eF1UJ;$%#K8^mpRYQG8)nd1y0wuXO|y8q^IslzU+7mK*ibe%QIbEi z5a!ojQq#>Ndi7nw1L(@OX2o&I{WKZmgL0L!t|4KiWi;Mqk-*Zb({5%dVOgo$a`z2w zjHY^EAsA4=?y6S};uDm?X6#R_cfL2nr?&CYXfZ=Gnxry`zHZQ*jUL0o454BeOYe^g z=q5Wf?$M>OT5Q1NK5Ef4d^C5l$UEXym`B!#xe0bZ6q#WxsG)m z!i$8$b98d zgpGaNCq&03SmvfthlZ4XWI7`Z9^@t}VW6|uO;kKXi$5w_+Z4h_h0Q7^IG86-J?cPF*gICBWmEHMOG?$k# z1gHti;z?EJm4LlX2-s4C=4I4K{wZvIhE@Nja!nuHvpB@G-u7hIx|frbL8JcE$|{+k z5-SWD{t!oe2%lP#ub>l1BOX^kyJ9^s&dHvmW;HFv2v8`ZgGDbGqIj?d=s0;{7FL)&1Z)S~Y>JWW9luGO#a5(gOc}cI> zrt>}W!HAj@azn?d?O;N^DGH~Xj9Rz0T=Cse4$zGYw=S^AC`Kvvw_TBHUr7k`3@E%< z4Y(ESA1GMgxJ#X*GCD(dbNmSd2Jlb6+nUn2Gy1}PYP-^3s9e|jYm()U4?VDL|FA2y$PmfLjyJy7%j zobI7!jNnz}C#^R0F`$&v*W^8M*=(CFyU@j{EX!M)*yWqWlGrrNMCa{%&xHR3FH`Ua z5($67%NxZ17QATwD|Qlfbg;0tbayd!vvzd&FYuCNuZSv)H2P=rbR}3ID6p`QUTq6&5A`X(dCezECIn+)~W&ui8toZ3~6 zjogmihUnQ`Z$CTl5rrd7{nMiD8y^Zw;w`^Tm=%WZZl#)W$Jq}T)dru2u%^@!<@rUL zvO|X2imR&3u*yHrl@gL&iI!sP$Q5mt`=kN8DzR#W3WAa|xBOx$R_*2CqKnr)zALa< zw*hJ`d)nVP@aS;!28Ip{b?g^iy3_9`H&rp5?(o55tZ>-@#NV{{>}c_;N(ZG=F#uqA z@g1(GJj0Y)9>W8P%ef6$CgATdAeHd@2(;nX>jFk7L$qEpKki3RqwKsZrW_Bd*|E4w z!Tv{;D=_DtWG-QrXjS9vZ;T1XBz%cPJ8$w=8zNtOrBdZ-8F7qRya{_kKJuE~1oIYiUbP6JL$

*Ou)7mZ_?&1& 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 literal 0 HcmV?d00001 diff --git a/samples/standalone/yml/http-server/gradle/wrapper/gradle-wrapper.properties b/samples/standalone/yml/http-server/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..95d7279349 --- /dev/null +++ b/samples/standalone/yml/http-server/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Aug 19 15:38:58 CEST 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.0-bin.zip diff --git a/samples/standalone/yml/http-server/gradlew b/samples/standalone/yml/http-server/gradlew new file mode 100755 index 0000000000..27309d9231 --- /dev/null +++ b/samples/standalone/yml/http-server/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/samples/standalone/yml/http-server/gradlew.bat b/samples/standalone/yml/http-server/gradlew.bat new file mode 100644 index 0000000000..832fdb6079 --- /dev/null +++ b/samples/standalone/yml/http-server/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/samples/standalone/yml/http-server/mvnw b/samples/standalone/yml/http-server/mvnw new file mode 100755 index 0000000000..fc7efd17d0 --- /dev/null +++ b/samples/standalone/yml/http-server/mvnw @@ -0,0 +1,234 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # + # Look for the Apple JDKs first to preserve the existing behaviour, and then look + # for the new JDKs provided by Oracle. + # + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then + # + # Apple JDKs + # + export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then + # + # Oracle JDKs + # + export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home + fi + + if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then + # + # Apple JDKs + # + export JAVA_HOME=`/usr/libexec/java_home` + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` +fi + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + local basedir=$(pwd) + local wdir=$(pwd) + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + wdir=$(cd "$wdir/.."; pwd) + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CMD_LINE_ARGS + diff --git a/samples/standalone/yml/http-server/mvnw.cmd b/samples/standalone/yml/http-server/mvnw.cmd new file mode 100644 index 0000000000..001048081d --- /dev/null +++ b/samples/standalone/yml/http-server/mvnw.cmd @@ -0,0 +1,145 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %* + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/samples/standalone/yml/http-server/pom.xml b/samples/standalone/yml/http-server/pom.xml new file mode 100644 index 0000000000..271415af62 --- /dev/null +++ b/samples/standalone/yml/http-server/pom.xml @@ -0,0 +1,256 @@ + + + 4.0.0 + + com.example + http-server-yml + 0.0.1-SNAPSHOT + + Spring Cloud Contract Verifier Http Server Sample + Spring Cloud Contract Verifier Http Server Sample + + + org.springframework.boot + spring-boot-starter-parent + 1.5.9.RELEASE + + + + + UTF-8 + 1.8 + 1.2.2.BUILD-SNAPSHOT + Edgware.BUILD-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.cloud + spring-cloud-starter-contract-verifier + test + + + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud-dependencies.version} + pom + import + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.springframework.cloud + spring-cloud-contract-maven-plugin + ${spring-cloud-contract.version} + true + + com.example.fraud + + + + + maven-deploy-plugin + 2.8.2 + + true + + + + org.apache.maven.plugins + maven-clean-plugin + 3.0.0 + + + + build + + + target + + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.springframework.cloud + spring-cloud-contract-maven-plugin + [${spring-cloud-contract.version},) + + convert + generateTests + + + + + + + + + + + + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + spring-releases + Spring Releases + https://repo.spring.io/release + + false + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + spring-releases + Spring Releases + https://repo.spring.io/release + + false + + + + + + + + integration + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + + gradle + test + + ./gradlew + + clean + build + publishToMavenLocal + -PverifierVersion=${spring-cloud-contract.version} + + + + exec + + + + + + + + + windows + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + + gradle + test + + gradlew.bat + + clean + build + publishToMavenLocal + -PverifierVersion=${spring-cloud-contract.version} + + + + exec + + + + + + + + + diff --git a/samples/standalone/yml/http-server/settings.gradle b/samples/standalone/yml/http-server/settings.gradle new file mode 100644 index 0000000000..c2d11e3eb9 --- /dev/null +++ b/samples/standalone/yml/http-server/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'http-server-yml-gradle' diff --git a/samples/standalone/yml/http-server/src/main/java/com/example/fraud/Application.java b/samples/standalone/yml/http-server/src/main/java/com/example/fraud/Application.java new file mode 100644 index 0000000000..9117009b09 --- /dev/null +++ b/samples/standalone/yml/http-server/src/main/java/com/example/fraud/Application.java @@ -0,0 +1,17 @@ +package com.example.fraud; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableAutoConfiguration +@ComponentScan +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/samples/standalone/yml/http-server/src/main/java/com/example/fraud/FraudDetectionController.java b/samples/standalone/yml/http-server/src/main/java/com/example/fraud/FraudDetectionController.java new file mode 100644 index 0000000000..764f8a6e96 --- /dev/null +++ b/samples/standalone/yml/http-server/src/main/java/com/example/fraud/FraudDetectionController.java @@ -0,0 +1,40 @@ +package com.example.fraud; + +import java.math.BigDecimal; + +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.fraud.model.FraudCheck; +import com.example.fraud.model.FraudCheckResult; +import com.example.fraud.model.FraudCheckStatus; + +import static org.springframework.web.bind.annotation.RequestMethod.PUT; + +@RestController +public class FraudDetectionController { + + private static final String NO_REASON = null; + private static final String AMOUNT_TOO_HIGH = "Amount too high"; + private static final BigDecimal MAX_AMOUNT = new BigDecimal("5000"); + + // tag::server_api[] + @RequestMapping(value = "/fraudcheck", method = PUT) + public FraudCheckResult fraudCheck(@RequestBody FraudCheck fraudCheck) { + // end::server_api[] + // tag::new_impl[] + if (amountGreaterThanThreshold(fraudCheck)) { + return new FraudCheckResult(FraudCheckStatus.FRAUD, AMOUNT_TOO_HIGH); + } + // end::new_impl[] + // tag::initial_impl[] + return new FraudCheckResult(FraudCheckStatus.OK, NO_REASON); + // end::initial_impl[] + } + + private boolean amountGreaterThanThreshold(FraudCheck fraudCheck) { + return MAX_AMOUNT.compareTo(fraudCheck.getLoanAmount()) < 0; + } + +} diff --git a/samples/standalone/yml/http-server/src/main/java/com/example/fraud/FraudNameController.java b/samples/standalone/yml/http-server/src/main/java/com/example/fraud/FraudNameController.java new file mode 100644 index 0000000000..4ba6881594 --- /dev/null +++ b/samples/standalone/yml/http-server/src/main/java/com/example/fraud/FraudNameController.java @@ -0,0 +1,69 @@ +package com.example.fraud; + +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author Marcin Grzejszczak + */ +@RestController +class FraudNameController { + + private final FraudVerifier fraudVerifier; + + FraudNameController(FraudVerifier fraudVerifier) { + this.fraudVerifier = fraudVerifier; + } + + @PutMapping(value = "/frauds/name") + public NameResponse checkByName(@RequestBody NameRequest request) { + boolean fraud = this.fraudVerifier.isFraudByName(request.getName()); + if (fraud) { + return new NameResponse("Sorry " + request.getName() + " but you're a fraud"); + } + return new NameResponse("Don't worry " + request.getName() + " you're not a fraud"); + } +} + +interface FraudVerifier { + boolean isFraudByName(String name); +} + +class NameRequest { + private String name; + + public NameRequest(String name) { + this.name = name; + } + + public NameRequest() { + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } +} + +class NameResponse { + private String result; + + public NameResponse(String result) { + this.result = result; + } + + public NameResponse() { + } + + public String getResult() { + return this.result; + } + + public void setResult(String result) { + this.result = result; + } +} diff --git a/samples/standalone/yml/http-server/src/main/java/com/example/fraud/FraudStatsController.java b/samples/standalone/yml/http-server/src/main/java/com/example/fraud/FraudStatsController.java new file mode 100644 index 0000000000..ac9340611c --- /dev/null +++ b/samples/standalone/yml/http-server/src/main/java/com/example/fraud/FraudStatsController.java @@ -0,0 +1,51 @@ +package com.example.fraud; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class FraudStatsController { + + private final StatsProvider statsProvider; + + public FraudStatsController(StatsProvider statsProvider) { + this.statsProvider = statsProvider; + } + + @GetMapping(value = "/frauds") + public Response countAllFrauds() { + return new Response(this.statsProvider.count(FraudType.ALL)); + } + + @GetMapping(value = "/drunks") + public Response countAllDrunks() { + return new Response(this.statsProvider.count(FraudType.DRUNKS)); + } +} + +enum FraudType { + DRUNKS, ALL +} + +interface StatsProvider { + int count(FraudType fraudType); +} + +class Response { + private int count; + + public Response(int count) { + this.count = count; + } + + public Response() { + } + + public int getCount() { + return this.count; + } + + public void setCount(int count) { + this.count = count; + } +} diff --git a/samples/standalone/yml/http-server/src/main/java/com/example/fraud/model/FraudCheck.java b/samples/standalone/yml/http-server/src/main/java/com/example/fraud/model/FraudCheck.java new file mode 100644 index 0000000000..e98c2b60d2 --- /dev/null +++ b/samples/standalone/yml/http-server/src/main/java/com/example/fraud/model/FraudCheck.java @@ -0,0 +1,32 @@ +package com.example.fraud.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.math.BigDecimal; + +public class FraudCheck { + + @JsonProperty("client.id") + private String clientId; + + private BigDecimal loanAmount; + + public FraudCheck() { + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public BigDecimal getLoanAmount() { + return loanAmount; + } + + public void setLoanAmount(BigDecimal loanAmount) { + this.loanAmount = loanAmount; + } +} diff --git a/samples/standalone/yml/http-server/src/main/java/com/example/fraud/model/FraudCheckResult.java b/samples/standalone/yml/http-server/src/main/java/com/example/fraud/model/FraudCheckResult.java new file mode 100644 index 0000000000..2f30912063 --- /dev/null +++ b/samples/standalone/yml/http-server/src/main/java/com/example/fraud/model/FraudCheckResult.java @@ -0,0 +1,35 @@ +package com.example.fraud.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class FraudCheckResult { + + private FraudCheckStatus fraudCheckStatus; + + @JsonProperty("rejection.reason") + private String rejectionReason; + + public FraudCheckResult() { + } + + public FraudCheckResult(FraudCheckStatus fraudCheckStatus, String rejectionReason) { + this.fraudCheckStatus = fraudCheckStatus; + this.rejectionReason = rejectionReason; + } + + public FraudCheckStatus getFraudCheckStatus() { + return fraudCheckStatus; + } + + public void setFraudCheckStatus(FraudCheckStatus fraudCheckStatus) { + this.fraudCheckStatus = fraudCheckStatus; + } + + public String getRejectionReason() { + return rejectionReason; + } + + public void setRejectionReason(String rejectionReason) { + this.rejectionReason = rejectionReason; + } +} diff --git a/samples/standalone/yml/http-server/src/main/java/com/example/fraud/model/FraudCheckStatus.java b/samples/standalone/yml/http-server/src/main/java/com/example/fraud/model/FraudCheckStatus.java new file mode 100644 index 0000000000..5a6adcf274 --- /dev/null +++ b/samples/standalone/yml/http-server/src/main/java/com/example/fraud/model/FraudCheckStatus.java @@ -0,0 +1,5 @@ +package com.example.fraud.model; + +public enum FraudCheckStatus { + OK, FRAUD +} diff --git a/samples/standalone/yml/http-server/src/main/resources/application.yml b/samples/standalone/yml/http-server/src/main/resources/application.yml new file mode 100644 index 0000000000..1c421cf2b7 --- /dev/null +++ b/samples/standalone/yml/http-server/src/main/resources/application.yml @@ -0,0 +1 @@ +server.port=0 \ No newline at end of file diff --git a/samples/standalone/yml/http-server/src/test/java/com/example/fraud/FraudBase.java b/samples/standalone/yml/http-server/src/test/java/com/example/fraud/FraudBase.java new file mode 100644 index 0000000000..142bffa8df --- /dev/null +++ b/samples/standalone/yml/http-server/src/test/java/com/example/fraud/FraudBase.java @@ -0,0 +1,29 @@ +package com.example.fraud; + +import org.junit.Before; + +import io.restassured.module.mockmvc.RestAssuredMockMvc; + +public class FraudBase { + @Before + public void setup() { + RestAssuredMockMvc.standaloneSetup(new FraudDetectionController(), + new FraudStatsController(stubbedStatsProvider())); + } + + private StatsProvider stubbedStatsProvider() { + return fraudType -> { + switch (fraudType) { + case DRUNKS: + return 100; + case ALL: + return 200; + } + return 0; + }; + } + + public void assertThatRejectionReasonIsNull(Object rejectionReason) { + assert rejectionReason == null; + } +} \ No newline at end of file diff --git a/samples/standalone/yml/http-server/src/test/java/com/example/fraud/FraudnameBase.java b/samples/standalone/yml/http-server/src/test/java/com/example/fraud/FraudnameBase.java new file mode 100644 index 0000000000..ab8288eeed --- /dev/null +++ b/samples/standalone/yml/http-server/src/test/java/com/example/fraud/FraudnameBase.java @@ -0,0 +1,17 @@ +package com.example.fraud; + +import org.junit.Before; + +import io.restassured.module.mockmvc.RestAssuredMockMvc; + +public class FraudnameBase { + + private static final String FRAUD_NAME = "fraud"; + + FraudVerifier fraudVerifier = FRAUD_NAME::equals; + + @Before + public void setup() { + RestAssuredMockMvc.standaloneSetup(new FraudNameController(this.fraudVerifier)); + } +} \ No newline at end of file diff --git a/samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.yml b/samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.yml new file mode 100644 index 0000000000..5180a42bd6 --- /dev/null +++ b/samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsFraud.yml @@ -0,0 +1,52 @@ +request: # (1) + method: PUT # (2) + url: /fraudcheck # (3) + body: # (4) + "client.id": 1234567890 + loanAmount: 99999 + headers: # (5) + Content-Type: application/json + matchers: + body: + - path: $.['client.id'] # (6) + type: by_regex + value: "[0-9]{10}" +response: # (7) + status: 200 # (8) + body: # (9) + fraudCheckStatus: "FRAUD" + "rejection.reason": "Amount too high" + headers: # (10) + Content-Type: application/json;charset=UTF-8 + + +#From the Consumer perspective, when shooting a request in the integration test: +# +#(1) - If the consumer sends a request +#(2) - With the "PUT" method +#(3) - to the URL "/fraudcheck" +#(4) - with the JSON body that +# * has a field `clientId` +# * has a field `loanAmount` that is equal to `99999` +#(5) - with header `Content-Type` equal to `application/json` +#(6) - and a `client.id` json entry matches the regular expression `[0-9]{10}` +#(7) - then the response will be sent with +#(8) - status equal `200` +#(9) - and JSON body equal to +# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" } +#(10) - with header `Content-Type` equal to `application/json` +# +#From the Producer perspective, in the autogenerated producer-side test: +# +#(1) - A request will be sent to the producer +#(2) - With the "PUT" method +#(3) - to the URL "/fraudcheck" +#(4) - with the JSON body that +# * has a field `clientId` `1234567890` +# * has a field `loanAmount` that is equal to `99999` +#(5) - with header `Content-Type` equal to `application/json` +#(7) - then the test will assert if the response has been sent with +#(8) - status equal `200` +#(9) - and JSON body equal to +# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" } +#(10) - with header `Content-Type` equal to `application/json;charset=UTF-8` \ No newline at end of file diff --git a/samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsNotFraud.yaml b/samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsNotFraud.yaml new file mode 100644 index 0000000000..7857099211 --- /dev/null +++ b/samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldMarkClientAsNotFraud.yaml @@ -0,0 +1,25 @@ +request: + method: PUT + url: /fraudcheck + body: + "client.id": 1234567890 + loanAmount: 123.123 + headers: + Content-Type: application/json + matchers: + body: + - path: $.['client.id'] + type: by_regex + value: "[0-9]{10}" +response: + status: 200 + body: + fraudCheckStatus: "OK" + "rejection.reason": null + headers: + Content-Type: application/json;charset=UTF-8 + matchers: + body: + - path: $.['rejection.reason'] + type: by_command + value: assertThatRejectionReasonIsNull($it) \ No newline at end of file diff --git a/samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldReturnFraudStats.yml b/samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldReturnFraudStats.yml new file mode 100644 index 0000000000..f66eb51be3 --- /dev/null +++ b/samples/standalone/yml/http-server/src/test/resources/contracts/fraud/shouldReturnFraudStats.yml @@ -0,0 +1,21 @@ +--- +name: "should count all frauds" +request: + method: GET + url: /frauds +response: + status: 200 + body: + count: 200 + headers: + Content-Type: application/json;charset=UTF-8 +--- +request: + method: GET + url: /drunks +response: + status: 200 + body: + count: 100 + headers: + Content-Type: application/json;charset=UTF-8 \ No newline at end of file diff --git a/samples/standalone/yml/http-server/src/test/resources/contracts/fraudname/shouldReturnAFraudForTheName.yml b/samples/standalone/yml/http-server/src/test/resources/contracts/fraudname/shouldReturnAFraudForTheName.yml new file mode 100644 index 0000000000..29859f0b97 --- /dev/null +++ b/samples/standalone/yml/http-server/src/test/resources/contracts/fraudname/shouldReturnAFraudForTheName.yml @@ -0,0 +1,15 @@ +# highest priority +priority: 1 +request: + method: PUT + url: /frauds/name + body: + name: "fraud" + headers: + Content-Type: application/json;charset=UTF-8 +response: + status: 200 + body: + result: "Sorry {{{ jsonpath this '$.name' }}} but you're a fraud" + headers: + Content-Type: "{{{ request.headers.Content-Type.0 }}}" \ No newline at end of file diff --git a/samples/standalone/yml/http-server/src/test/resources/contracts/fraudname/shouldReturnNonFraudForTheName.yml b/samples/standalone/yml/http-server/src/test/resources/contracts/fraudname/shouldReturnNonFraudForTheName.yml new file mode 100644 index 0000000000..af59b2ad65 --- /dev/null +++ b/samples/standalone/yml/http-server/src/test/resources/contracts/fraudname/shouldReturnNonFraudForTheName.yml @@ -0,0 +1,18 @@ +request: + method: PUT + url: /frauds/name + body: + name: "non fraud" + headers: + Content-Type: application/json + matchers: + body: + - path: $.name + type: by_regex + predefined: only_alpha_unicode +response: + status: 200 + body: + result: "Don't worry {{{ jsonpath this '$.name' }}} you're not a fraud" + headers: + Content-Type: "{{{ request.headers.Content-Type.0 }}};charset=UTF-8" \ No newline at end of file diff --git a/samples/standalone/yml/pom.xml b/samples/standalone/yml/pom.xml new file mode 100644 index 0000000000..cc41273a7f --- /dev/null +++ b/samples/standalone/yml/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + + org.springframework.cloud + spring-cloud-contract-samples-standalone + 1.2.2.BUILD-SNAPSHOT + .. + + + spring-cloud-contract-samples-yml + pom + + Spring Cloud Contract Standalone Dsl Test Samples + Spring Cloud Contract Standalone Test Samples used for end to end tests + + + 1.2.2.BUILD-SNAPSHOT + + + + http-server + http-client + + + + + + maven-deploy-plugin + 2.8.2 + + true + + + + + + diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Body.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Body.groovy index 1b7eecd32c..100210ca99 100644 --- a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Body.groovy +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Body.groovy @@ -31,7 +31,8 @@ import groovy.transform.ToString class Body extends DslProperty { Body(Map body) { - super(extractValue(body, { DslProperty p -> p.clientValue}), extractValue(body, {DslProperty p -> p.serverValue})) + super(extractValue(body, { p -> p instanceof DslProperty ? ((DslProperty) p).clientValue : p }), + extractValue(body, { p -> p instanceof DslProperty ? ((DslProperty) p).serverValue : p })) } private static Map extractValue(Map body, Closure valueProvider) { diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/BodyMatchers.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/BodyMatchers.groovy index b865c32d62..f2b5374e0a 100644 --- a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/BodyMatchers.groovy +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/BodyMatchers.groovy @@ -15,7 +15,7 @@ import groovy.transform.ToString @ToString(includeFields = true, includePackage = false) class BodyMatchers { private final RegexPatterns regexPatterns = new RegexPatterns() - private final List jsonPathRegexMatchers = [] + protected final List jsonPathRegexMatchers = [] void jsonPath(String path, MatchingTypeValue matchingType) { this.jsonPathRegexMatchers << new JsonPathBodyMatcher(path, matchingType) diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Headers.groovy b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Headers.groovy index 61278dbeba..1798ad43c9 100644 --- a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Headers.groovy +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/internal/Headers.groovy @@ -91,6 +91,18 @@ class Headers { } as Map } + /** + * Converts the headers into their stub side representations and returns as + * a map of String key => Object value. + */ + Map asTestSideMap() { + def acc = [:].withDefault { [] as Collection } + return entries.inject(acc as Map) { Map map, Header header -> + map[header.name] = header.serverValue + return map + } as Map + } + boolean equals(o) { if (this.is(o)) return true if (getClass() != o.class) return false diff --git a/spring-cloud-contract-spec/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy b/spring-cloud-contract-spec/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy index 397d1d93ae..e69d20ad88 100644 --- a/spring-cloud-contract-spec/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy +++ b/spring-cloud-contract-spec/src/test/groovy/org/springframework/cloud/contract/spec/internal/ContractSpec.groovy @@ -94,6 +94,15 @@ then: // end::description[] } + def 'should set a name'() { + given: + // tag::name[] + org.springframework.cloud.contract.spec.Contract.make { + name("some_special_name") + } + // end::name[] + } + def 'should mark a contract ignored'() { given: // tag::ignored[] diff --git a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java index 8e4ee733b2..9a80136acd 100644 --- a/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java +++ b/spring-cloud-contract-stub-runner/src/main/java/org/springframework/cloud/contract/stubrunner/StubRepository.java @@ -34,6 +34,7 @@ import org.slf4j.LoggerFactory; import org.springframework.cloud.contract.spec.Contract; import org.springframework.cloud.contract.spec.ContractConverter; import org.springframework.cloud.contract.stubrunner.provider.wiremock.WireMockHttpServerStub; +import org.springframework.cloud.contract.verifier.converter.YamlContractConverter; import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter; import org.springframework.core.io.support.SpringFactoriesLoader; @@ -165,11 +166,15 @@ class StubRepository { BasicFileAttributes attrs) throws IOException { File file = path.toFile(); ContractConverter converter = contractConverter(file); - if (isContractDescriptor(file) && isStubPerConsumerPathMatching(file)) { - contractDescriptors - .addAll(ContractVerifierDslConverter.convertAsCollection(file.getParentFile(), file)); - } else if (converter != null && isStubPerConsumerPathMatching(file)) { - contractDescriptors.addAll(converter.convertFrom(file)); + if (isStubPerConsumerPathMatching(file)) { + if (isContractDescriptor(file)) { + contractDescriptors + .addAll(ContractVerifierDslConverter.convertAsCollection(file.getParentFile(), file)); + } else if (YamlContractConverter.INSTANCE.isAccepted(file)) { + contractDescriptors.addAll(YamlContractConverter.INSTANCE.convertFrom(file)); + } else if (converter != null) { + contractDescriptors.addAll(converter.convertFrom(file)); + } } return super.visitFile(path, attrs); } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle index 5674941167..2d383f25d3 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/build.gradle @@ -23,7 +23,7 @@ ext { ] } -project.version = findProperty('verifierVersion') ?: '1.5.4.RELEASE' +project.version = findProperty('verifierVersion') ?: '1.5.9.RELEASE' apply plugin: 'groovy' apply from: "$rootDir/gradle/release.gradle" apply plugin: 'eclipse' diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle index e1f690423a..aa5fa89e2d 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleJerseyProject/build.gradle @@ -20,7 +20,7 @@ buildscript { mavenCentral() } dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE") + classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE") } } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle index c690f91b42..80388a96d9 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/sampleProject/build.gradle @@ -20,7 +20,7 @@ buildscript { mavenLocal() } dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE") + classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE") } } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/build.gradle b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/build.gradle index 12068579b3..25b5133ae9 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/build.gradle +++ b/spring-cloud-contract-tools/spring-cloud-contract-gradle-plugin/src/test/resources/functionalTest/scenarioProject/build.gradle @@ -20,7 +20,7 @@ buildscript { mavenLocal() } dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.4.RELEASE") + classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.9.RELEASE") } } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateStubsMojo.java b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateStubsMojo.java index b7ee1636fa..3398186708 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateStubsMojo.java +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/main/java/org/springframework/cloud/contract/maven/verifier/GenerateStubsMojo.java @@ -40,7 +40,9 @@ import org.codehaus.plexus.archiver.jar.JarArchiver; public class GenerateStubsMojo extends AbstractMojo { private static final String STUB_MAPPING_FILE_PATTERN = "**/*.json"; - private static final String CONTRACT_FILE_PATTERN = "**/*.groovy"; + private static final String GROOVY_CONTRACT_FILE_PATTERN = "**/*.groovy"; + private static final String YAML_CONTRACT_FILE_PATTERN = "**/*.yaml"; + private static final String YML_CONTRACT_FILE_PATTERN = "**/*.yml"; @Parameter(defaultValue = "${project.build.directory}", readonly = true, required = true) @@ -110,7 +112,10 @@ public class GenerateStubsMojo extends AbstractMojo { try { if (this.attachContracts) { this.archiver.addDirectory(stubsOutputDir, - new String[] { STUB_MAPPING_FILE_PATTERN, CONTRACT_FILE_PATTERN }, + new String[] { STUB_MAPPING_FILE_PATTERN, + GROOVY_CONTRACT_FILE_PATTERN, + YAML_CONTRACT_FILE_PATTERN, + YML_CONTRACT_FILE_PATTERN }, excludedFilesEmpty() ? new String[0] : this.excludedFiles); } else { @@ -134,7 +139,9 @@ public class GenerateStubsMojo extends AbstractMojo { private String[] excludes() { List excludes = new ArrayList<>(); - excludes.add(CONTRACT_FILE_PATTERN); + excludes.add(GROOVY_CONTRACT_FILE_PATTERN); + excludes.add(YAML_CONTRACT_FILE_PATTERN); + excludes.add(YML_CONTRACT_FILE_PATTERN); if (!excludedFilesEmpty()) { excludes.addAll(Arrays.asList(this.excludedFiles)); } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/plugin-extension/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/plugin-extension/pom.xml index 3c718f96f0..d83b95cf92 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/plugin-extension/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/plugin-extension/pom.xml @@ -28,7 +28,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.4.RELEASE + 1.5.9.RELEASE diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/pom.xml index 77aacd7970..fe239312b9 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-groovy/pom.xml @@ -28,7 +28,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.4.RELEASE + 1.5.9.RELEASE diff --git a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-java/pom.xml b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-java/pom.xml index 3c718f96f0..d83b95cf92 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-java/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-maven-plugin/src/test/projects/spring-boot-java/pom.xml @@ -28,7 +28,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.4.RELEASE + 1.5.9.RELEASE diff --git a/spring-cloud-contract-verifier/pom.xml b/spring-cloud-contract-verifier/pom.xml index c34abb4dd3..558d557f0f 100644 --- a/spring-cloud-contract-verifier/pom.xml +++ b/spring-cloud-contract-verifier/pom.xml @@ -21,6 +21,10 @@ org.springframework.boot spring-boot-test-autoconfigure + + com.fasterxml.jackson.core + jackson-databind + org.springframework spring-messaging @@ -81,6 +85,10 @@ com.github.jknack handlebars + + org.yaml + snakeyaml + cglib cglib @@ -105,11 +113,6 @@ slf4j-simple test - - org.yaml - snakeyaml - test - io.rest-assured rest-assured diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JavaTestGenerator.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JavaTestGenerator.groovy index eb725aab4e..c3de1c34a7 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JavaTestGenerator.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/JavaTestGenerator.groovy @@ -187,7 +187,6 @@ class ClassPresenceChecker { boolean isClassPresent(String className) { try { - Class.forName(className) return true } catch (ClassNotFoundException e) { diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilder.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilder.groovy index 66e97b2e83..4984a2e0a0 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilder.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/MethodBuilder.groovy @@ -63,12 +63,24 @@ class MethodBuilder { static String methodName(ContractMetadata contract, File stubsFile, Contract stubContent) { if (stubContent.name) { - return NamesUtil.camelCase(NamesUtil.convertIllegalPackageChars(stubContent.name)) + String name = NamesUtil.camelCase(NamesUtil.convertIllegalPackageChars(stubContent.name)) + if (log.isDebugEnabled()) { + log.debug("Overriding the default test name with [" + name + "]") + } + return name } else if (contract.convertedContract.size() > 1) { int index = contract.convertedContract.findIndexOf { it == stubContent} - return "${camelCasedMethodFromFileName(stubsFile)}_${index}" + String name = "${camelCasedMethodFromFileName(stubsFile)}_${index}" + if (log.isDebugEnabled()) { + log.debug("Scenario found. The method name will be [" + name + "]") + } + return name } - return camelCasedMethodFromFileName(stubsFile) + String name = camelCasedMethodFromFileName(stubsFile) + if (log.isDebugEnabled()) { + log.debug("The method name will be [" + name + "]") + } + return name } private static String camelCasedMethodFromFileName(File stubsFile) { diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContract.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContract.groovy new file mode 100644 index 0000000000..df528651fe --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContract.groovy @@ -0,0 +1,177 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.verifier.converter + +import groovy.transform.CompileStatic + +/** + * YAML representation of a {@link org.springframework.cloud.contract.spec.Contract} + * + * @since 1.2.1 + * @author Marcin Grzejszczak + */ +@CompileStatic +class YamlContract { + public Request request + public Response response + public Input input + public OutputMessage outputMessage + public String description + public String label + public String name + public Integer priority + public boolean ignored + + @CompileStatic + static class Request { + public String method + public String url + public String urlPath + public Map queryParameters = [:] + public Map headers = [:] + public Object body + public String bodyFromFile + public StubMatchers matchers = new StubMatchers() + public Multipart multipart + } + + @CompileStatic + static class Multipart { + public Map params = [:] + public List named = [] + } + + @CompileStatic + static class Named { + public String paramName + public String fileName + public String fileContent + } + + @CompileStatic + static class StubMatchers { + public List body = [] + public List headers = [] + public MultipartStubMatcher multipart + } + + @CompileStatic + static class BodyStubMatcher { + public String path + public StubMatcherType type + public String value + public PredefinedRegex predefined + } + + @CompileStatic + static class MultipartStubMatcher { + public List params = [] + public List named = [] + } + + @CompileStatic + static class MultipartNamedStubMatcher { + public String paramName + public ValueMatcher fileName + public ValueMatcher fileContent + } + + @CompileStatic + static class ValueMatcher { + public String regex + public PredefinedRegex predefined + } + + @CompileStatic + static class BodyTestMatcher { + public String path + public TestMatcherType type + public String value + public Integer minOccurrence + public Integer maxOccurrence + public PredefinedRegex predefined + } + + @CompileStatic + static class KeyValueMatcher { + public String key + public String regex + public PredefinedRegex predefined + } + + @CompileStatic + static class TestHeaderMatcher { + public String key + public String regex + public String command + public PredefinedRegex predefined + } + + @CompileStatic + static enum PredefinedRegex { + only_alpha_unicode, number, any_boolean, ip_address, hostname, + email, url, uuid, iso_date, iso_date_time, iso_time, + iso_8601_with_offset, non_empty, non_blank + } + + @CompileStatic + static enum StubMatcherType { + by_date, by_time, by_timestamp, by_regex, by_equality + } + + @CompileStatic + static enum TestMatcherType { + by_date, by_time, by_timestamp, by_regex, by_equality, by_type, by_command + } + + @CompileStatic + static class Response { + public int status + public Map headers = [:] + public Object body + public String bodyFromFile + public TestMatchers matchers = new TestMatchers() + public Boolean async + } + + @CompileStatic + static class TestMatchers { + public List body = [] + public List headers = [] + } + + @CompileStatic + static class Input { + public String messageFrom + public String triggeredBy + public Map messageHeaders = [:] + public Object messageBody + public String messageBodyFromFile + public String assertThat + public StubMatchers matchers = new StubMatchers() + } + + @CompileStatic + static class OutputMessage { + public String sentTo + public Map headers = [:] + public Object body + public String bodyFromFile + public String assertThat + public TestMatchers matchers = new TestMatchers() + } +} \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy new file mode 100644 index 0000000000..ab600fca7a --- /dev/null +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy @@ -0,0 +1,556 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.contract.verifier.converter + +import java.nio.file.Files +import java.util.regex.Pattern + +import com.fasterxml.jackson.databind.ObjectMapper +import groovy.transform.CompileStatic +import org.yaml.snakeyaml.Yaml + +import org.springframework.cloud.contract.spec.Contract +import org.springframework.cloud.contract.spec.ContractConverter +import org.springframework.cloud.contract.spec.internal.BodyMatcher +import org.springframework.cloud.contract.spec.internal.DslProperty +import org.springframework.cloud.contract.spec.internal.ExecutionProperty +import org.springframework.cloud.contract.spec.internal.Headers +import org.springframework.cloud.contract.spec.internal.MatchingType +import org.springframework.cloud.contract.spec.internal.MatchingTypeValue +import org.springframework.cloud.contract.spec.internal.NamedProperty +import org.springframework.cloud.contract.spec.internal.RegexPatterns +import org.springframework.cloud.contract.verifier.converter.YamlContract.BodyStubMatcher +import org.springframework.cloud.contract.verifier.converter.YamlContract.BodyTestMatcher +import org.springframework.cloud.contract.verifier.converter.YamlContract.Input +import org.springframework.cloud.contract.verifier.converter.YamlContract.Named +import org.springframework.cloud.contract.verifier.converter.YamlContract.OutputMessage +import org.springframework.cloud.contract.verifier.converter.YamlContract.PredefinedRegex +import org.springframework.cloud.contract.verifier.converter.YamlContract.Request +import org.springframework.cloud.contract.verifier.converter.YamlContract.Response +import org.springframework.cloud.contract.verifier.converter.YamlContract.KeyValueMatcher +import org.springframework.cloud.contract.verifier.converter.YamlContract.StubMatcherType +import org.springframework.cloud.contract.verifier.converter.YamlContract.StubMatchers +import org.springframework.cloud.contract.verifier.converter.YamlContract.TestHeaderMatcher +import org.springframework.cloud.contract.verifier.converter.YamlContract.TestMatcherType +import org.springframework.cloud.contract.verifier.util.MapConverter + +/** + * Simple converter from and to a {@link YamlContract} to a collection of {@link Contract} + * + * @since 1.2.1 + * @author Marcin Grzejszczak + */ +@CompileStatic +class YamlContractConverter implements ContractConverter> { + + public static final YamlContractConverter INSTANCE = new YamlContractConverter() + + @Override + boolean isAccepted(File file) { + String name = file.getName() + return name.endsWith(".yml") || name.endsWith(".yaml") + } + + @Override + Collection convertFrom(File contractFile) { + ClassLoader classLoader = YamlContractConverter.getClassLoader() + ObjectMapper mapper = new ObjectMapper() + try { + return new Yaml().loadAll(Files.newInputStream(contractFile.toPath())).collect { + YamlContract yamlContract = mapper.convertValue(it, YamlContract.class); + Thread.currentThread().setContextClassLoader(updatedClassLoader(contractFile.getParentFile(), classLoader)) + return Contract.make { + if (yamlContract.description) description(yamlContract.description) + if (yamlContract.label) label(yamlContract.label) + if (yamlContract.name) name(yamlContract.name) + if (yamlContract.priority) priority(yamlContract.priority) + if (yamlContract.ignored) ignored() + if (yamlContract.request?.method) { + request { + method(yamlContract.request?.method) + if (yamlContract.request?.url) { + url(yamlContract.request?.url) { + if (yamlContract.request.queryParameters) { + queryParameters { + yamlContract.request.queryParameters.each { String key, Object value -> + if (value instanceof List) { + ((List) value).each { + parameter(key, it) + } + } else { + parameter(key, value) + } + } + } + } + } + } + if (yamlContract.request?.urlPath) { + urlPath(yamlContract.request?.urlPath) { + if (yamlContract.request.queryParameters) { + queryParameters { + yamlContract.request.queryParameters.each { String key, Object value -> + if (value instanceof List) { + ((List) value).each { + parameter(key, it) + } + } else { + parameter(key, value) + } + } + } + } + } + } + if (yamlContract.request?.headers) { + headers { + yamlContract.request?.headers?.each { String key, Object value -> + KeyValueMatcher matcher = yamlContract.request.matchers.headers.find { it.key == key } + if (value instanceof List) { + ((List) value).each { + Object clientValue = clientValue(it, matcher, key) + header(key, new DslProperty(clientValue, it)) + } + } else { + Object clientValue = clientValue(value, matcher, key) + header(key, new DslProperty(clientValue, value)) + } + } + } + } + if (yamlContract.request.body) body(yamlContract.request.body) + if (yamlContract.request.bodyFromFile) body(file(yamlContract.request.bodyFromFile)) + if (yamlContract.request.multipart) { + Map multipartMap = [:] + Map multiPartParams = yamlContract.request.multipart.params.collectEntries { String paramKey, String paramValue -> + KeyValueMatcher matcher = yamlContract.request.matchers.multipart.params.find { + it.key == paramKey + } + Object value = paramValue + if (matcher) { + value = matcher.regex ? Pattern.compile(matcher.regex) : + predefinedToPattern(matcher.predefined) + } + return [(paramKey), new DslProperty<>(value, paramValue)] + } as Map + multipartMap.putAll(multiPartParams) + yamlContract.request.multipart.named.each { Named namedParam -> + YamlContract.MultipartNamedStubMatcher matcher = yamlContract.request.matchers.multipart.named.find { + it.paramName == namedParam.paramName + } + Object fileNameValue = namedParam.fileName + Object fileContentValue = namedParam.fileContent + if (matcher && matcher.fileName) { + fileNameValue = matcher.fileName.regex ? Pattern.compile(matcher.fileName.regex) : + predefinedToPattern(matcher.fileName.predefined) + } + if (matcher && matcher.fileContent) { + fileContentValue = matcher.fileContent.regex ? Pattern.compile(matcher.fileContent.regex) : + predefinedToPattern(matcher.fileContent.predefined) + } + multipartMap.put(namedParam.paramName, new NamedProperty(new DslProperty<>(fileNameValue, namedParam.fileName), + new DslProperty<>(fileContentValue, namedParam.fileContent))) + } + multipart(multipartMap) + } + stubMatchers { + yamlContract.request.matchers?.body?.each { BodyStubMatcher matcher -> + MatchingTypeValue value = null + switch (matcher.type) { + case StubMatcherType.by_date: + value = byDate() + break + case StubMatcherType.by_time: + value = byTime() + break + case StubMatcherType.by_timestamp: + value = byTimestamp() + break + case StubMatcherType.by_regex: + String regex = matcher.value + if (matcher.predefined) { + regex = predefinedToPattern(matcher.predefined).pattern() + } + value = byRegex(regex) + break + case StubMatcherType.by_equality: + value = byEquality() + break + } + jsonPath(matcher.path, value) + } + } + } + response { + status(yamlContract.response.status) + headers { + yamlContract.response?.headers?.each { String key, Object value -> + TestHeaderMatcher matcher = yamlContract.response.matchers.headers.find { it.key == key } + if (value instanceof List) { + ((List) value).each { + Object serverValue = serverValue(it, matcher, key) + header(key, new DslProperty(it, serverValue)) + } + } else { + Object serverValue = serverValue(value, matcher, key) + header(key, new DslProperty(value, serverValue)) + } + } + } + if (yamlContract.response.body) body(yamlContract.response.body) + if (yamlContract.response.bodyFromFile) body(file(yamlContract.response.bodyFromFile)) + if (yamlContract.response.async) async() + testMatchers { + yamlContract.response?.matchers?.body?.each { BodyTestMatcher testMatcher -> + MatchingTypeValue value = null + switch (testMatcher.type) { + case TestMatcherType.by_date: + value = byDate() + break + case TestMatcherType.by_time: + value = byTime() + break + case TestMatcherType.by_timestamp: + value = byTimestamp() + break + case TestMatcherType.by_regex: + String regex = testMatcher.value + if (testMatcher.predefined) { + regex = predefinedToPattern(testMatcher.predefined).pattern() + } + value = byRegex(regex) + break + case TestMatcherType.by_equality: + value = byEquality() + break + case TestMatcherType.by_type: + value = byType() { + if (testMatcher.minOccurrence != null) minOccurrence(testMatcher.minOccurrence) + if (testMatcher.maxOccurrence != null) maxOccurrence(testMatcher.maxOccurrence) + } + break + case TestMatcherType.by_command: + value = byCommand(testMatcher.value) + break + } + jsonPath(testMatcher.path, value) + } + } + } + } + if (yamlContract.input) { + input { + if (yamlContract.input.messageFrom) messageFrom(yamlContract.input.messageFrom) + if (yamlContract.input.assertThat) assertThat(yamlContract.input.assertThat) + if (yamlContract.input.triggeredBy) triggeredBy(yamlContract.input.triggeredBy) + messageHeaders { + yamlContract.input?.messageHeaders?.each { String key, Object value -> + KeyValueMatcher matcher = yamlContract.input.matchers?.headers?.find { it.key == key } + Object clientValue = clientValue(value, matcher, key) + header(key, new DslProperty(clientValue, value)) + } + } + if (yamlContract.input.messageBody) messageBody(yamlContract.input.messageBody) + if (yamlContract.input.messageBodyFromFile) messageBody(file(yamlContract.input.messageBodyFromFile)) + stubMatchers { + yamlContract.input.matchers.body?.each { BodyStubMatcher matcher -> + MatchingTypeValue value = null + switch (matcher.type) { + case StubMatcherType.by_date: + value = byDate() + break + case StubMatcherType.by_time: + value = byTime() + break + case StubMatcherType.by_timestamp: + value = byTimestamp() + break + case StubMatcherType.by_regex: + value = byRegex(matcher.value) + break + case StubMatcherType.by_equality: + value = byEquality() + break + } + jsonPath(matcher.path, value) + } + } + } + } + OutputMessage outputMsg = yamlContract.outputMessage + if (outputMsg) { + outputMessage { + if (outputMsg.assertThat) assertThat(outputMsg.assertThat) + if (outputMsg.sentTo) sentTo(outputMsg.sentTo) + headers { + outputMsg.headers?.each { String key, Object value -> + TestHeaderMatcher matcher = outputMsg.matchers?.headers?.find { it.key == key } + Object serverValue = serverValue(value, matcher, key) + header(key, new DslProperty(value, serverValue)) + } + } + if (outputMsg.body) body(outputMsg.body) + if (outputMsg.bodyFromFile) body(file(outputMsg.bodyFromFile)) + if (outputMsg.matchers) { + testMatchers { + yamlContract.outputMessage?.matchers?.body?.each { BodyTestMatcher testMatcher -> + MatchingTypeValue value = null + switch (testMatcher.type) { + case TestMatcherType.by_date: + value = byDate() + break + case TestMatcherType.by_time: + value = byTime() + break + case TestMatcherType.by_timestamp: + value = byTimestamp() + break + case TestMatcherType.by_regex: + value = byRegex(testMatcher.value) + break + case TestMatcherType.by_equality: + value = byEquality() + break + case TestMatcherType.by_type: + value = byType() { + if (testMatcher.minOccurrence != null) minOccurrence(testMatcher.minOccurrence) + if (testMatcher.maxOccurrence != null) maxOccurrence(testMatcher.maxOccurrence) + } + break + case TestMatcherType.by_command: + value = byCommand(testMatcher.value) + break + } + jsonPath(testMatcher.path, value) + } + } + } + } + } + } + } + } + catch (FileNotFoundException e) { + throw new IllegalStateException(e) + } + catch (IllegalStateException ise) { + throw ise + } + catch (Exception e1) { + throw new IllegalStateException("Exception occurred while processing the file [" + contractFile + "]", e1) + } finally { + Thread.currentThread().setContextClassLoader(classLoader) + } + } + + protected Object serverValue(Object value, TestHeaderMatcher matcher, String key) { + Object serverValue = value + if (matcher?.regex) { + serverValue = Pattern.compile(matcher.regex) + Pattern pattern = (Pattern) serverValue + assertPatternMatched(pattern, value, key) + } else if (matcher?.predefined) { + Pattern pattern = predefinedToPattern(matcher.predefined) + serverValue = pattern + assertPatternMatched(pattern, value, key) + } else if (matcher?.command) { + serverValue = new ExecutionProperty(matcher.command) + } + return serverValue + } + + protected Object clientValue(Object value, KeyValueMatcher matcher, String key) { + Object clientValue = value + if (matcher?.regex) { + clientValue = Pattern.compile(matcher.regex) + Pattern pattern = (Pattern) clientValue + assertPatternMatched(pattern, value, key) + } else if (matcher?.predefined) { + Pattern pattern = predefinedToPattern(matcher.predefined) + clientValue = pattern + assertPatternMatched(pattern, value, key) + } + return clientValue + } + + private void assertPatternMatched(Pattern pattern, value, String key) { + boolean matches = pattern.matcher(value.toString()).matches() + if (!matches) throw new IllegalStateException("Broken headers! A header with " + + "key [${key}] with value [${value}] is not matched by regex [${pattern.pattern()}]") + } + + protected Pattern predefinedToPattern(PredefinedRegex predefinedRegex) { + RegexPatterns patterns = new RegexPatterns() + switch (predefinedRegex) { + case PredefinedRegex.only_alpha_unicode: + return Pattern.compile(patterns.onlyAlphaUnicode()) + case PredefinedRegex.number: + return Pattern.compile(patterns.number()) + case PredefinedRegex.any_boolean: + return Pattern.compile(patterns.anyBoolean()) + case PredefinedRegex.ip_address: + return Pattern.compile(patterns.ipAddress()) + case PredefinedRegex.hostname: + return Pattern.compile(patterns.hostname()) + case PredefinedRegex.email: + return Pattern.compile(patterns.email()) + case PredefinedRegex.url: + return Pattern.compile(patterns.url()) + case PredefinedRegex.uuid: + return Pattern.compile(patterns.uuid()) + case PredefinedRegex.iso_date: + return Pattern.compile(patterns.isoDate()) + case PredefinedRegex.iso_date_time: + return Pattern.compile(patterns.isoDateTime()) + case PredefinedRegex.iso_time: + return Pattern.compile(patterns.isoTime()) + case PredefinedRegex.iso_8601_with_offset: + return Pattern.compile(patterns.iso8601WithOffset()) + case PredefinedRegex.non_empty: + return Pattern.compile(patterns.nonEmpty()) + case PredefinedRegex.non_blank: + return Pattern.compile(patterns.nonBlank()) + } + } + + protected String file(String relativePath) { + URL resource = Thread.currentThread().getContextClassLoader().getResource(relativePath) + if (resource == null) { + throw new IllegalStateException("File [${relativePath}] is not present") + } + return new File(resource.toURI()).text + } + + protected static ClassLoader updatedClassLoader(File rootFolder, ClassLoader classLoader) { + ClassLoader urlCl = URLClassLoader + .newInstance([rootFolder.toURI().toURL()] as URL[], classLoader) + Thread.currentThread().setContextClassLoader(urlCl) + return urlCl + } + + @Override + List convertTo(Collection contracts) { + return contracts.collect { Contract contract -> + YamlContract yamlContract = new YamlContract() + if (contract?.request?.method) { + yamlContract.request = new Request() + yamlContract.request.with { + method = contract?.request?.method?.clientValue + url = contract?.request?.url?.clientValue + headers = (contract?.request?.headers as Headers)?.asTestSideMap() + body = MapConverter.getTestSideValues(contract?.request?.body) + matchers = new StubMatchers() + contract?.request?.matchers?.jsonPathMatchers()?.each { BodyMatcher matcher -> + matchers.body << new BodyStubMatcher( + path: matcher.path(), + type: stubMatcherType(matcher.matchingType()), + value: matcher.value()?.toString() + ) + } + } + yamlContract.response = new Response() + yamlContract.response.with { + status = contract?.response?.status?.clientValue as Integer + headers = (contract?.response?.headers as Headers)?.asStubSideMap() + body = MapConverter.getStubSideValues(contract?.response?.body) + contract?.response?.matchers?.jsonPathMatchers()?.each { BodyMatcher matcher -> + matchers.body << new BodyTestMatcher( + path: matcher.path(), + type: testMatcherType(matcher.matchingType()), + value: matcher.value()?.toString(), + minOccurrence: matcher.minTypeOccurrence(), + maxOccurrence: matcher.maxTypeOccurrence() + ) + } + } + } + if (contract.input) { + yamlContract.input = new Input() + yamlContract.input.assertThat = contract?.input?.assertThat?.toString() + yamlContract.input.triggeredBy = contract?.input?.triggeredBy?.toString() + yamlContract.input.messageHeaders = (contract?.input?.messageHeaders as Headers)?.asTestSideMap() + yamlContract.input.messageBody = MapConverter.getTestSideValues(contract?.input?.messageBody) + yamlContract.input.messageFrom = contract?.input?.messageFrom?.serverValue + yamlContract.input.matchers.body.each { + contract?.input?.matchers?.jsonPathMatchers()?.each { BodyMatcher matcher -> + yamlContract.input.matchers.body << new BodyStubMatcher( + path: matcher.path(), + type: stubMatcherType(matcher.matchingType()), + value: matcher.value()?.toString() + ) + } + } + } + if (contract.outputMessage) { + yamlContract.outputMessage = new OutputMessage() + yamlContract.outputMessage.headers = (contract?.outputMessage?.headers as Headers)?.asStubSideMap() + yamlContract.outputMessage.body = MapConverter.getStubSideValues(contract?.outputMessage?.body) + yamlContract.outputMessage.matchers.body.each { + contract?.input?.matchers?.jsonPathMatchers()?.each { BodyMatcher matcher -> + yamlContract.outputMessage.matchers.body << new BodyTestMatcher( + path: matcher.path(), + type: testMatcherType(matcher.matchingType()), + value: matcher.value()?.toString(), + minOccurrence: matcher.minTypeOccurrence(), + maxOccurrence: matcher.maxTypeOccurrence() + ) + } + } + } + return yamlContract + } + } + + protected TestMatcherType testMatcherType(MatchingType matchingType) { + switch (matchingType) { + case MatchingType.EQUALITY: + return TestMatcherType.by_equality + case MatchingType.TYPE: + return TestMatcherType.by_type + case MatchingType.COMMAND: + return TestMatcherType.by_command + case MatchingType.DATE: + return TestMatcherType.by_date + case MatchingType.TIME: + return TestMatcherType.by_time + case MatchingType.TIMESTAMP: + return TestMatcherType.by_timestamp + case MatchingType.REGEX: + return TestMatcherType.by_regex + } + return null + } + + protected StubMatcherType stubMatcherType(MatchingType matchingType) { + switch (matchingType) { + case MatchingType.EQUALITY: + return StubMatcherType.by_equality + case MatchingType.TYPE: + case MatchingType.COMMAND: + throw new UnsupportedOperationException("No type for client side") + case MatchingType.DATE: + return StubMatcherType.by_date + case MatchingType.TIME: + return StubMatcherType.by_time + case MatchingType.TIMESTAMP: + return StubMatcherType.by_timestamp + case MatchingType.REGEX: + return StubMatcherType.by_regex + } + return null + } +} diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockRequestStubStrategy.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockRequestStubStrategy.groovy index eecac825f5..1550e13773 100755 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockRequestStubStrategy.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockRequestStubStrategy.groovy @@ -26,6 +26,8 @@ import groovy.json.JsonOutput import groovy.transform.PackageScope import groovy.transform.TypeChecked import groovy.transform.TypeCheckingMode +import groovy.util.logging.Slf4j + import org.springframework.cloud.contract.spec.Contract import org.springframework.cloud.contract.spec.internal.Body import org.springframework.cloud.contract.spec.internal.DslProperty @@ -53,6 +55,7 @@ import static org.springframework.cloud.contract.verifier.util.RegexpBuilders.bu */ @TypeChecked @PackageScope +@Slf4j class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { private final Request request @@ -211,7 +214,12 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy { case MatchingStrategy.Type.ABSENT: return WireMock.absent() default: - return WireMock."${value.type.name}"(value.clientValue) + try { + return WireMock."${value.type.name}"(value.clientValue) + } catch (Throwable t) { + log.error("Exception occurred while trying to call WireMock.${value.type.name}(${value.clientValue})", t) + throw t + } } default: return WireMock.equalTo(object.toString()) diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScanner.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScanner.groovy index 37da2a9593..805608c63a 100755 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScanner.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScanner.groovy @@ -22,6 +22,7 @@ import groovy.transform.CompileStatic import groovy.util.logging.Slf4j import org.springframework.cloud.contract.spec.Contract import org.springframework.cloud.contract.spec.ContractConverter +import org.springframework.cloud.contract.verifier.converter.YamlContractConverter import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter import org.springframework.core.io.support.SpringFactoriesLoader @@ -101,6 +102,8 @@ class ContractFileScanner { boolean included = includeMatcher ? file.absolutePath.matches(includeMatcher) : true if (contractFile && included) { addContractToTestGeneration(result, files, file, index, ContractVerifierDslConverter.convertAsCollection(baseDir, file)) + } else if (YamlContractConverter.INSTANCE.isAccepted(file) && included) { + addContractToTestGeneration(result, files, file, index, YamlContractConverter.INSTANCE.convertFrom(file)) } else if (!contractFile && included) { addContractToTestGeneration(converters, result, files, file, index) } else { @@ -144,8 +147,13 @@ class ContractFileScanner { if (hasScenarioFilenamePattern(path)) { order = index } - result.put(file.parentFile.toPath(), new ContractMetadata(path, matchesPattern(file, ignoreMatchers), - files.size(), order, convertedContract)) + Path parent = file.parentFile.toPath() + ContractMetadata metadata = new ContractMetadata(path, matchesPattern(file, ignoreMatchers), + files.size(), order, convertedContract) + if (log.isDebugEnabled()) { + log.debug("Creating a contract entry for path [" + path + "] and metadata [" + metadata + "]") + } + result.put(parent, metadata) } private boolean hasScenarioFilenamePattern(Path path) { diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContract.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContract.groovy deleted file mode 100644 index 92e729cf16..0000000000 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContract.groovy +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.verifier.converter - -/** - * Yaml representation of a {@link org.springframework.cloud.contract.spec.Contract} - */ -class YamlContract { - public Request request = new Request() - public Response response = new Response() - - static class Request { - public String method - public String url - public Map headers = [:] - public Map body = [:] - } - - static class Response { - public int status - public Map headers = [:] - public Map body = [:] - } -} \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy deleted file mode 100644 index f55114457b..0000000000 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2013-2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.contract.verifier.converter - -import java.nio.file.Files - -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 - -/** - * Simple converter from and to a {@link YamlContract} to a collection of {@link Contract} - */ -@CompileStatic -class YamlContractConverter implements ContractConverter> { - - @Override - public boolean isAccepted(File file) { - String name = file.getName() - return name.endsWith(".yml") || name.endsWith(".yaml") - } - - @Override - public Collection convertFrom(File file) { - try { - YamlContract yamlContract = new Yaml().loadAs( - Files.newInputStream(file.toPath()), 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?.response?.body) - } - }] - } - catch (FileNotFoundException e) { - throw new IllegalStateException(e) - } - } - - @Override - public List convertTo(Collection contracts) { - return contracts.collect { Contract contract -> - YamlContract yamlContract = new YamlContract() - yamlContract.request.with { - method = contract?.request?.method?.clientValue - url = contract?.request?.url?.clientValue - headers = (contract?.request?.headers as Headers)?.asStubSideMap() - body = contract?.request?.body?.clientValue as Map - } - yamlContract.response.with { - status = contract?.response?.status?.clientValue as Integer - headers = (contract?.response?.headers as Headers)?.asStubSideMap() - body = contract?.response?.body?.clientValue as Map - } - return yamlContract - } - } -} diff --git a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy index 5b0545316d..960c232065 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy @@ -16,38 +16,388 @@ package org.springframework.cloud.contract.verifier.converter -import org.springframework.cloud.contract.spec.Contract +import java.util.regex.Pattern + +import spock.lang.Shared import spock.lang.Specification +import org.springframework.cloud.contract.spec.Contract +import org.springframework.cloud.contract.spec.internal.ExecutionProperty +import org.springframework.cloud.contract.spec.internal.MatchingType +import org.springframework.cloud.contract.spec.internal.NamedProperty +import org.springframework.cloud.contract.spec.internal.RegexPatterns +import org.springframework.cloud.contract.spec.internal.Url +import org.springframework.cloud.contract.verifier.util.MapConverter + /** * @author Marcin Grzejszczak */ class YamlContractConverterSpec extends Specification { - URL ymlUrl = YamlContractConverterSpec.getResource("/contract.yml") - File yml = new File(ymlUrl.toURI()) + @Shared URL ymlUrl = YamlContractConverterSpec.getResource("/yml/contract.yml") + @Shared File ymlWithRest = new File(ymlUrl.toURI()) + @Shared URL ymlUrl2 = YamlContractConverterSpec.getResource("/yml/contract_rest.yml") + @Shared File ymlWithRest2 = new File(ymlUrl2.toURI()) + @Shared URL ymlUrl3 = YamlContractConverterSpec.getResource("/yml/contract_rest_with_path.yml") + @Shared File ymlWithRest3 = new File(ymlUrl3.toURI()) + URL ymlMsgUrl = YamlContractConverterSpec.getResource("/yml/contract_message.yml") + File ymlMessaging = new File(ymlMsgUrl.toURI()) + URL ymlMsgMethodUrl = YamlContractConverterSpec.getResource("/yml/contract_message_method.yml") + File ymlMessagingMethod = new File(ymlMsgMethodUrl.toURI()) + URL ymlMsgMsgUrl = YamlContractConverterSpec.getResource("/yml/contract_message_input_message.yml") + File ymlMessagingMsg = new File(ymlMsgMsgUrl.toURI()) + URL ymlBodyFile = YamlContractConverterSpec.getResource("/yml/contract_from_file.yml") + File ymlBody = new File(ymlBodyFile.toURI()) + URL ymlReferenceFile = YamlContractConverterSpec.getResource("/yml/contract_reference_request.yml") + File ymlReference = new File(ymlReferenceFile.toURI()) + URL ymlMatchersFile = YamlContractConverterSpec.getResource("/yml/contract_matchers.yml") + File ymlMatchers = new File(ymlMatchersFile.toURI()) + URL ymlMultipleFile = YamlContractConverterSpec.getResource("/yml/multiple_contracts.yml") + File ymlMultiple = new File(ymlMultipleFile.toURI()) YamlContractConverter converter = new YamlContractConverter() - def "should convert YAML to DSL"() { + def "should convert YAML with REST to DSL for [#yamlFile]"() { given: + assert converter.isAccepted(yamlFile) + when: + Collection contracts = converter.convertFrom(yamlFile) + then: + contracts.size() == 1 + Contract contract = contracts.first() + contract.description == "Some description" + contract.name == "some name" + contract.priority == 8 + contract.ignored == true + Url url = yamlFile == ymlWithRest3 ? + contract.request.urlPath : contract.request.url + url.clientValue == "/foo" + url.queryParameters.parameters[0].name == "a" + url.queryParameters.parameters[0].serverValue == "b" + url.queryParameters.parameters[1].name == "b" + url.queryParameters.parameters[1].serverValue == "c" + contract.request.method.clientValue == "PUT" + contract.request.headers.entries.find { it.name == "foo" && + ((Pattern) it.clientValue).pattern == "bar" && it.serverValue == "bar" } + contract.request.headers.entries.find { it.name == "fooReq" && + it.serverValue == "baz" } + contract.request.body.clientValue == [foo: "bar"] + contract.request.matchers.jsonPathRegexMatchers[0].path() == '$.foo' + contract.request.matchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX + contract.request.matchers.jsonPathRegexMatchers[0].value() == 'bar' + and: + contract.response.status.clientValue == 200 + contract.response.headers.entries.find { it.name == "foo2" && + ((Pattern) it.serverValue).pattern == "bar" && it.clientValue == "bar" } + contract.response.headers.entries.find { it.name == "foo3" && + ((ExecutionProperty) it.serverValue).insertValue('foo') == "andMeToo(foo)" } + contract.response.headers.entries.find { it.name == "fooRes" && + it.clientValue == "baz" } + contract.response.body.clientValue == [foo2: "bar", foo3: "baz"] + contract.response.matchers.jsonPathRegexMatchers[0].path() == '$.foo2' + contract.response.matchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX + contract.response.matchers.jsonPathRegexMatchers[0].value() == 'bar' + contract.response.matchers.jsonPathRegexMatchers[1].path() == '$.foo3' + contract.response.matchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.COMMAND + contract.response.matchers.jsonPathRegexMatchers[1].value() == new ExecutionProperty('executeMe($it)') + where: + yamlFile << [ymlWithRest, ymlWithRest2, ymlWithRest3] + } + + def "should convert YAML with REST to DSL with advanced request referencing"() { + given: + assert converter.isAccepted(ymlReference) + when: + Collection contracts = converter.convertFrom(ymlReference) + then: + contracts.size() == 1 + Contract contract = contracts.first() + Url url = contract.request.url + url.clientValue == "/api/v1/xxxx" + url.queryParameters.parameters[0].name == "foo" + url.queryParameters.parameters[0].serverValue == "bar" + url.queryParameters.parameters[1].name == "foo" + url.queryParameters.parameters[1].serverValue == "bar2" + contract.request.method.clientValue == "GET" + contract.request.headers.entries.findAll { it.name == "Authorization" } + .collect { it.clientValue } == ["secret", "secret2"] + contract.request.body.clientValue == [foo: "bar", baz: 5] + and: + contract.response.status.clientValue == 200 + contract.response.headers.entries + .find { it.name == "Authorization" }.clientValue == '''foo {{{ request.headers.Authorization.0 }}} bar''' + with(MapConverter.getTestSideValues(contract.response.body)) { + it.url == "{{{ request.url }}}" + it.path == "{{{ request.path }}}" + it.pathIndex == "{{{ request.path.1 }}}" + it.param == "{{{ request.query.foo }}}" + it.paramIndex == "{{{ request.query.foo.1 }}}" + it.authorization == "{{{ request.headers.Authorization.0 }}}" + it.authorization2 == "{{{ request.headers.Authorization.1 }}" + it.fullBody == "{{{ request.body }}}" + it.responseFoo == '''{{{ jsonpath this '$.foo' }}}''' + it.responseBaz == '''{{{ jsonpath this '$.baz' }}}''' + it.responseBaz2 == '''Bla bla {{{ jsonpath this '$.foo' }}} bla bla''' + } + } + + def "should convert YAML with REST matchers to DSL"() { + given: + assert converter.isAccepted(ymlMatchers) + when: + Collection contracts = converter.convertFrom(ymlMatchers) + then: + contracts.size() == 1 + Contract contract = contracts.first() + RegexPatterns patterns = new RegexPatterns() + contract.request.headers.entries.find { it.name == "Content-Type" && + ((Pattern) it.clientValue).pattern == "application/json.*" && it.serverValue == "application/json" } + contract.request.matchers.jsonPathRegexMatchers[0].path() == '$.duck' + contract.request.matchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX + contract.request.matchers.jsonPathRegexMatchers[0].value() == '[0-9]{3}' + contract.request.matchers.jsonPathRegexMatchers[1].path() == '$.duck' + contract.request.matchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.EQUALITY + contract.request.matchers.jsonPathRegexMatchers[2].path() == '$.alpha' + contract.request.matchers.jsonPathRegexMatchers[2].matchingType() == MatchingType.REGEX + contract.request.matchers.jsonPathRegexMatchers[2].value() == patterns.onlyAlphaUnicode() + contract.request.matchers.jsonPathRegexMatchers[3].path() == '$.alpha' + contract.request.matchers.jsonPathRegexMatchers[3].matchingType() == MatchingType.EQUALITY + contract.request.matchers.jsonPathRegexMatchers[4].path() == '$.number' + contract.request.matchers.jsonPathRegexMatchers[4].matchingType() == MatchingType.REGEX + contract.request.matchers.jsonPathRegexMatchers[4].value() == patterns.number() + contract.request.matchers.jsonPathRegexMatchers[5].path() == '$.aBoolean' + contract.request.matchers.jsonPathRegexMatchers[5].matchingType() == MatchingType.REGEX + contract.request.matchers.jsonPathRegexMatchers[5].value() == patterns.anyBoolean() + contract.request.matchers.jsonPathRegexMatchers[6].path() == '$.date' + contract.request.matchers.jsonPathRegexMatchers[6].matchingType() == MatchingType.DATE + contract.request.matchers.jsonPathRegexMatchers[6].value() == patterns.isoDate() + contract.request.matchers.jsonPathRegexMatchers[7].path() == '$.dateTime' + contract.request.matchers.jsonPathRegexMatchers[7].matchingType() == MatchingType.TIMESTAMP + contract.request.matchers.jsonPathRegexMatchers[7].value() == patterns.isoDateTime() + contract.request.matchers.jsonPathRegexMatchers[8].path() == '$.time' + contract.request.matchers.jsonPathRegexMatchers[8].matchingType() == MatchingType.TIME + contract.request.matchers.jsonPathRegexMatchers[8].value() == patterns.isoTime() + contract.request.matchers.jsonPathRegexMatchers[9].path() == "\$.['key'].['complex.key']" + contract.request.matchers.jsonPathRegexMatchers[9].matchingType() == MatchingType.EQUALITY + and: + contract.response.status.clientValue == 200 + contract.response.matchers.jsonPathRegexMatchers[0].path() == '$.duck' + contract.response.matchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX + contract.response.matchers.jsonPathRegexMatchers[0].value() == '[0-9]{3}' + contract.response.matchers.jsonPathRegexMatchers[1].path() == '$.duck' + contract.response.matchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.EQUALITY + contract.response.matchers.jsonPathRegexMatchers[2].path() == '$.alpha' + contract.response.matchers.jsonPathRegexMatchers[2].matchingType() == MatchingType.REGEX + contract.response.matchers.jsonPathRegexMatchers[2].value() == patterns.onlyAlphaUnicode() + contract.response.matchers.jsonPathRegexMatchers[3].path() == '$.alpha' + contract.response.matchers.jsonPathRegexMatchers[3].matchingType() == MatchingType.EQUALITY + contract.response.matchers.jsonPathRegexMatchers[4].path() == '$.number' + contract.response.matchers.jsonPathRegexMatchers[4].matchingType() == MatchingType.REGEX + contract.response.matchers.jsonPathRegexMatchers[4].value() == patterns.number() + contract.response.matchers.jsonPathRegexMatchers[5].path() == '$.aBoolean' + contract.response.matchers.jsonPathRegexMatchers[5].matchingType() == MatchingType.REGEX + contract.response.matchers.jsonPathRegexMatchers[5].value() == patterns.anyBoolean() + contract.response.matchers.jsonPathRegexMatchers[6].path() == '$.date' + contract.response.matchers.jsonPathRegexMatchers[6].matchingType() == MatchingType.DATE + contract.response.matchers.jsonPathRegexMatchers[6].value() == patterns.isoDate() + contract.response.matchers.jsonPathRegexMatchers[7].path() == '$.dateTime' + contract.response.matchers.jsonPathRegexMatchers[7].matchingType() == MatchingType.TIMESTAMP + contract.response.matchers.jsonPathRegexMatchers[7].value() == patterns.isoDateTime() + contract.response.matchers.jsonPathRegexMatchers[8].path() == '$.time' + contract.response.matchers.jsonPathRegexMatchers[8].matchingType() == MatchingType.TIME + contract.response.matchers.jsonPathRegexMatchers[8].value() == patterns.isoTime() + contract.response.matchers.jsonPathRegexMatchers[9].path() == '$.valueWithTypeMatch' + contract.response.matchers.jsonPathRegexMatchers[9].matchingType() == MatchingType.TYPE + contract.response.matchers.jsonPathRegexMatchers[10].path() == '$.valueWithMin' + contract.response.matchers.jsonPathRegexMatchers[10].matchingType() == MatchingType.TYPE + contract.response.matchers.jsonPathRegexMatchers[10].minTypeOccurrence() == 1 + contract.response.matchers.jsonPathRegexMatchers[11].path() == '$.valueWithMax' + contract.response.matchers.jsonPathRegexMatchers[11].matchingType() == MatchingType.TYPE + contract.response.matchers.jsonPathRegexMatchers[11].maxTypeOccurrence() == 3 + contract.response.matchers.jsonPathRegexMatchers[12].path() == '$.valueWithMinMax' + contract.response.matchers.jsonPathRegexMatchers[12].matchingType() == MatchingType.TYPE + contract.response.matchers.jsonPathRegexMatchers[12].minTypeOccurrence() == 1 + contract.response.matchers.jsonPathRegexMatchers[12].maxTypeOccurrence() == 3 + contract.response.matchers.jsonPathRegexMatchers[13].path() == '$.valueWithMinEmpty' + contract.response.matchers.jsonPathRegexMatchers[13].matchingType() == MatchingType.TYPE + contract.response.matchers.jsonPathRegexMatchers[13].minTypeOccurrence() == 0 + contract.response.matchers.jsonPathRegexMatchers[14].path() == '$.valueWithMaxEmpty' + contract.response.matchers.jsonPathRegexMatchers[14].matchingType() == MatchingType.TYPE + contract.response.matchers.jsonPathRegexMatchers[14].maxTypeOccurrence() == 0 + contract.response.matchers.jsonPathRegexMatchers[15].path() == '$.duck' + contract.response.matchers.jsonPathRegexMatchers[15].matchingType() == MatchingType.COMMAND + contract.response.matchers.jsonPathRegexMatchers[15].value() == new ExecutionProperty('assertThatValueIsANumber($it)') + } + + def "should convert YAML with REST with response from request"() { + given: + assert converter.isAccepted(ymlBody) + when: + Collection contracts = converter.convertFrom(ymlBody) + then: + contracts.size() == 1 + Contract contract = contracts.first() + contract.request.body.clientValue == '''{ "hello" : "request" }''' + and: + contract.response.body.clientValue == '''{ "hello" : "response" }''' + } + + def "should convert YAML with REST with multipart"() { + given: + URL ymlUrl = YamlContractConverterSpec.getResource("/yml/contract_multipart.yml") + File yml = new File(ymlUrl.toURI()) + and: assert converter.isAccepted(yml) when: Collection contracts = converter.convertFrom(yml) then: contracts.size() == 1 Contract contract = contracts.first() - contract.request.url.clientValue == "/foo" - contract.request.method.clientValue == "PUT" - contract.request.headers.entries.find { it.name == "foo" && it.clientValue == "bar" } - contract.request.body.clientValue == [foo: "bar"] - contract.response.status.clientValue == 200 - contract.response.headers.entries.find { it.name == "foo2" && it.clientValue == "bar" } - contract.response.body.clientValue == [foo2: "bar"] + RegexPatterns patterns = new RegexPatterns() + def stubSide = MapConverter.getStubSideValues(contract.request.multipart) + stubSide.formParameter.pattern() == ".+" + stubSide.someBooleanParameter.pattern() == patterns.anyBoolean() + def testSide = MapConverter.getTestSideValues(contract.request.multipart) + testSide.formParameter == '"formParameterValue"' + testSide.someBooleanParameter == "true" + ((NamedProperty) testSide.file).name.serverValue == "filename.csv" + ((NamedProperty) testSide.file).name.clientValue.pattern() == patterns.nonEmpty() + ((NamedProperty) testSide.file).value.serverValue == "file content" + ((NamedProperty) testSide.file).value.clientValue.pattern() == patterns.nonEmpty() + and: + contract.response.status.serverValue == 200 + } + + def "should convert YAML with messaging to DSL"() { + given: + assert converter.isAccepted(ymlMessaging) + when: + Collection contracts = converter.convertFrom(ymlMessaging) + then: + contracts.size() == 1 + Contract contract = contracts.first() + contract.description == "Some description" + contract.name == "some name" + contract.label == "some_label" + contract.ignored == true + contract.input.assertThat.toString() == "bar()" + contract.input.messageFrom.serverValue == "foo" + contract.input.triggeredBy.toString() == "foo()" + contract.input.messageHeaders.entries.find { it.name == "foo" && + ((Pattern) it.clientValue).pattern == "bar" && it.serverValue == "bar" } + contract.input.messageBody.clientValue == [foo: "bar"] + contract.input.matchers.jsonPathRegexMatchers[0].path() == '$.bar' + contract.input.matchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX + contract.input.matchers.jsonPathRegexMatchers[0].value() == 'bar' + and: + contract.outputMessage.assertThat.toString() == "baz()" + contract.outputMessage.headers.entries.find { it.name == "foo2" && + ((Pattern) it.serverValue).pattern == "bar" && it.clientValue == "bar" } + contract.outputMessage.headers.entries.find { it.name == "foo3" && + ((ExecutionProperty) it.serverValue).insertValue('foo') == "andMeToo(foo)" } + contract.outputMessage.headers.entries.find { it.name == "fooRes" && + it.clientValue == "baz" } + contract.outputMessage.body.clientValue == [foo2: "bar", foo3: "baz"] + contract.outputMessage.matchers.jsonPathRegexMatchers[0].path() == '$.foo2' + contract.outputMessage.matchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX + contract.outputMessage.matchers.jsonPathRegexMatchers[0].value() == 'bar' + contract.outputMessage.matchers.jsonPathRegexMatchers[1].path() == '$.foo3' + contract.outputMessage.matchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.COMMAND + contract.outputMessage.matchers.jsonPathRegexMatchers[1].value() == new ExecutionProperty('executeMe($it)') + } + + def "should convert YAML with messaging triggered by a method to DSL"() { + given: + assert converter.isAccepted(ymlMessagingMethod) + when: + Collection contracts = converter.convertFrom(ymlMessagingMethod) + then: + contracts.size() == 1 + Contract contract = contracts.first() + contract.description == "Some description" + contract.label == "some_label" + contract.input.triggeredBy.toString() == "bookReturnedTriggered()" + and: + contract.outputMessage.sentTo.clientValue == "output" + contract.outputMessage.headers.entries.find { + it.name == "BOOK-NAME" && it.clientValue == "foo" } + contract.outputMessage.body.clientValue == [bookName: "foo"] + } + + def "should convert YAML with messaging triggered by a message to DSL"() { + given: + assert converter.isAccepted(ymlMessagingMsg) + when: + Collection contracts = converter.convertFrom(ymlMessagingMsg) + then: + contracts.size() == 1 + Contract contract = contracts.first() + contract.description == "Some description" + contract.label == "some_label" + contract.input.messageFrom.serverValue == "input" + contract.input.messageHeaders.entries.find { it.name == "sample" && + it.serverValue == "header" } + contract.input.messageBody.clientValue == [bookName: "foo"] + and: + contract.outputMessage.sentTo.clientValue == "output" + contract.outputMessage.headers.entries.find { + it.name == "BOOK-NAME" && it.clientValue == "foo" } + contract.outputMessage.body.clientValue == [bookName: "foo"] + } + + def "should assert request headers when converting YAML to DSL"() { + given: + File yml = new File(YamlContractConverterSpec.getResource("/yml/contract_broken_request_headers.yml").toURI()) + and: + assert converter.isAccepted(yml) + when: + converter.convertFrom(yml) + then: + IllegalStateException e = thrown(IllegalStateException) + e.message.contains("Broken headers! A header with key [foo] with value [bar] is not matched by regex [barrrr]") + } + + def "should assert response headers when converting YAML to DSL"() { + given: + File yml = new File(YamlContractConverterSpec.getResource("/yml/contract_broken_response_headers.yml").toURI()) + and: + assert converter.isAccepted(yml) + when: + converter.convertFrom(yml) + then: + IllegalStateException e = thrown(IllegalStateException) + e.message.contains("Broken headers! A header with key [foo2] with value [bar] is not matched by regex [barrrr]") + } + + def "should parse multiple documents into a list of contracts"() { + given: + assert converter.isAccepted(ymlMultiple) + when: + Collection contracts = converter.convertFrom(ymlMultiple) + then: + contracts.size() == 2 + and: + contracts.first().request.url.clientValue == "/users/1" + contracts.last().request.url.clientValue == "/users/2" + } + + def "should parse messaging contract for [#file]"() { + given: + assert converter.isAccepted(file) + when: + Collection contracts = converter.convertFrom(file) + then: + contracts.size() == 1 + and: + contracts.first().input != null || contracts.first().outputMessage != null + where: + file << [1,2,3].collect { + new File(YamlContractConverterSpec.getResource("/yml/contract_message_scenario${it}.yml").toURI()) + } } def "should convert DSL to YAML"() { given: - assert converter.isAccepted(yml) + assert converter.isAccepted(ymlWithRest) and: List contracts = [Contract.make { request { diff --git a/spring-cloud-contract-verifier/src/test/resources/META-INF/spring.factories b/spring-cloud-contract-verifier/src/test/resources/META-INF/spring.factories index 92ce449691..6a7c8460f0 100644 --- a/spring-cloud-contract-verifier/src/test/resources/META-INF/spring.factories +++ b/spring-cloud-contract-verifier/src/test/resources/META-INF/spring.factories @@ -1,7 +1,3 @@ -# Converters -org.springframework.cloud.contract.spec.ContractConverter=\ -org.springframework.cloud.contract.verifier.converter.YamlContractConverter - # tag::extension[] org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockExtensions=\ org.springframework.cloud.contract.verifier.dsl.wiremock.TestWireMockExtensions diff --git a/spring-cloud-contract-verifier/src/test/resources/contract.yml b/spring-cloud-contract-verifier/src/test/resources/contract.yml index 783dafde0e..d50e335f0b 100644 --- a/spring-cloud-contract-verifier/src/test/resources/contract.yml +++ b/spring-cloud-contract-verifier/src/test/resources/contract.yml @@ -1,13 +1,45 @@ +description: Some description +name: some name +priority: 8 +ignored: true request: url: /foo + queryParameters: + a: b + b: c method: PUT headers: foo: bar + fooReq: baz body: foo: bar + matchers: + body: + - path: $.foo + type: by_regex + value: bar + headers: + - key: foo + regex: bar response: status: 200 headers: foo2: bar + foo3: foo33 + fooRes: baz body: - foo2: bar \ No newline at end of file + foo2: bar + foo3: baz + matchers: + body: + - path: $.foo2 + type: by_regex + value: bar + - path: $.foo3 + type: by_command + value: executeMe($it) + headers: + - key: foo2 + regex: bar + - key: foo3 + command: andMeToo($it) \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract.yml new file mode 100644 index 0000000000..0e25f7399a --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract.yml @@ -0,0 +1,69 @@ +#tag::description[] +description: Some description +#end::description[] +#tag::name[] +name: some name +#end::name[] +#tag::priority[] +priority: 8 +#end::priority[] +#tag::ignored[] +ignored: true +#end::ignored[] +#tag::request[] +request: +#end::request[] +#tag::request_obligatory[] + method: PUT + url: /foo +#end::request_obligatory[] +#tag::query_params[] + queryParameters: + a: b + b: c +#tag::query_params[] +#tag::headers[] + headers: + foo: bar + fooReq: baz +#end::headers[] +#tag::body[] + body: + foo: bar +#end::body[] +#tag::request_matcher[] + matchers: + body: + - path: $.foo + type: by_regex + value: bar + headers: + - key: foo + regex: bar +#end::request_matcher[] +#tag::response[] +response: +#end::response[] +#tag::response_obligatory[] + status: 200 +#end::response_obligatory[] + headers: + foo2: bar + foo3: foo33 + fooRes: baz + body: + foo2: bar + foo3: baz + matchers: + body: + - path: $.foo2 + type: by_regex + value: bar + - path: $.foo3 + type: by_command + value: executeMe($it) + headers: + - key: foo2 + regex: bar + - key: foo3 + command: andMeToo($it) \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_broken_request_headers.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_broken_request_headers.yml new file mode 100644 index 0000000000..75bacd859d --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_broken_request_headers.yml @@ -0,0 +1,34 @@ +request: + url: /foo + queryParameters: + a: b + b: c + method: PUT + headers: + foo: bar + fooReq: baz + body: + foo: bar + matchers: + body: + - path: $.foo + type: by_regex + value: bar + headers: + - key: foo + regex: barrrr +response: + status: 200 + headers: + foo2: bar + fooRes: baz + body: + foo2: bar + matchers: + body: + - path: $.foo2 + type: by_regex + value: bar + headers: + - key: foo2 + regex: bar \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_broken_response_headers.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_broken_response_headers.yml new file mode 100644 index 0000000000..4683e4cf33 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_broken_response_headers.yml @@ -0,0 +1,12 @@ +request: + url: /foo + method: PUT +response: + status: 200 + headers: + foo2: bar + fooRes: baz + matchers: + headers: + - key: foo2 + regex: barrrr \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_from_file.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_from_file.yml new file mode 100644 index 0000000000..3743ce3f1a --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_from_file.yml @@ -0,0 +1,7 @@ +request: + method: GET + url: /foo + bodyFromFile: request.json +response: + status: 200 + bodyFromFile: response.json \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_matchers.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_matchers.yml new file mode 100644 index 0000000000..a9c8e9b161 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_matchers.yml @@ -0,0 +1,124 @@ +request: + method: GET + urlPath: /get + body: + duck: 123 + alpha: "abc" + number: 123 + aBoolean: true + date: "2017-01-01" + dateTime: "2017-01-01T01:23:45" + time: "01:02:34" + valueWithoutAMatcher: "foo" + valueWithTypeMatch: "string" + key: + "complex.key": 'foo' + matchers: + headers: + - key: Content-Type + regex: "application/json.*" + body: + - path: $.duck + type: by_regex + value: "[0-9]{3}" + - path: $.duck + type: by_equality + - path: $.alpha + type: by_regex + predefined: only_alpha_unicode + - path: $.alpha + type: by_equality + - path: $.number + type: by_regex + predefined: number + - path: $.aBoolean + type: by_regex + predefined: any_boolean + - path: $.date + type: by_date + - path: $.dateTime + type: by_timestamp + - path: $.time + type: by_time + - path: "$.['key'].['complex.key']" + type: by_equality + headers: + Content-Type: application/json +response: + status: 200 + body: + duck: 123 + alpha: "abc" + number: 123 + aBoolean: true + date: "2017-01-01" + dateTime: "2017-01-01T01:23:45" + time: "01:02:34" + valueWithoutAMatcher: "foo" + valueWithTypeMatch: "string" + valueWithMin: + - 1 + - 2 + - 3 + valueWithMax: + - 1 + - 2 + - 3 + valueWithMinMax: + - 1 + - 2 + - 3 + valueWithMinEmpty: [] + valueWithMaxEmpty: [] + key: + 'complex.key' : 'foo' + matchers: + headers: + - key: Content-Type + regex: "application/json.*" + body: + - path: $.duck + type: by_regex + value: "[0-9]{3}" + - path: $.duck + type: by_equality + - path: $.alpha + type: by_regex + predefined: only_alpha_unicode + - path: $.alpha + type: by_equality + - path: $.number + type: by_regex + predefined: number + - path: $.aBoolean + type: by_regex + predefined: any_boolean + - path: $.date + type: by_date + - path: $.dateTime + type: by_timestamp + - path: $.time + type: by_time + - path: $.valueWithTypeMatch + type: by_type + - path: $.valueWithMin + type: by_type + minOccurrence: 1 + - path: $.valueWithMax + type: by_type + maxOccurrence: 3 + - path: $.valueWithMinMax + type: by_type + minOccurrence: 1 + maxOccurrence: 3 + - path: $.valueWithMinEmpty + type: by_type + minOccurrence: 0 + - path: $.valueWithMaxEmpty + type: by_type + maxOccurrence: 0 + - path: $.duck + type: by_command + value: assertThatValueIsANumber($it) + headers: + Content-Type: application/json \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_message.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message.yml new file mode 100644 index 0000000000..5ea2bb9eeb --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message.yml @@ -0,0 +1,43 @@ +description: Some description +label: some_label +name: some name +ignored: true +input: + messageFrom: foo + triggeredBy: foo() + messageHeaders: + foo: bar + messageBody: + foo: bar + assertThat: bar() + matchers: + body: + - path: $.bar + type: by_regex + value: bar + headers: + - key: foo + regex: bar +outputMessage: + sentTo: bar + headers: + foo2: bar + foo3: bar3 + fooRes: baz + body: + foo2: bar + foo3: baz + assertThat: baz() + matchers: + body: + - path: $.foo2 + type: by_regex + value: bar + - path: $.foo3 + type: by_command + value: executeMe($it) + headers: + - key: foo2 + regex: bar + - key: foo3 + command: andMeToo($it) diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_input_message.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_input_message.yml new file mode 100644 index 0000000000..4155e7ef95 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_input_message.yml @@ -0,0 +1,23 @@ +# Human readable description +description: Some description +# Label by means of which the output message can be triggered +label: some_label +# input is a message +input: + messageFrom: input + # has the following body + messageBody: + bookName: 'foo' + # and the following headers + messageHeaders: + sample: 'header' +# output message of the contract +outputMessage: + # destination to which the output message will be sent + sentTo: output + # the body of the output message + body: + bookName: foo + # the headers of the output message + headers: + BOOK-NAME: foo \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_method.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_method.yml new file mode 100644 index 0000000000..5cb80ad6e6 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_method.yml @@ -0,0 +1,17 @@ +# Human readable description +description: Some description +# Label by means of which the output message can be triggered +label: some_label +input: + # the contract will be triggered by a method + triggeredBy: bookReturnedTriggered() +# output message of the contract +outputMessage: + # destination to which the output message will be sent + sentTo: output + # the body of the output message + body: + bookName: foo + # the headers of the output message + headers: + BOOK-NAME: foo \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario1.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario1.yml new file mode 100644 index 0000000000..8c1cea9f7d --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario1.yml @@ -0,0 +1,10 @@ +label: some_label +input: + triggeredBy: bookReturnedTriggered +outputMessage: + sentTo: activemq:output + body: + bookName: foo + headers: + BOOK-NAME: foo + contentType: application/json diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario2.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario2.yml new file mode 100644 index 0000000000..6edd133b6f --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario2.yml @@ -0,0 +1,13 @@ +label: some_label +input: + messageFrom: jms:input + messageBody: + bookName: 'foo' + messageHeaders: + sample: header +outputMessage: + sentTo: jms:output + body: + bookName: foo + headers: + BOOK-NAME: foo diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario3.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario3.yml new file mode 100644 index 0000000000..88037cf2fa --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_message_scenario3.yml @@ -0,0 +1,8 @@ +label: some_label +input: + messageFrom: jms:delete + messageBody: + bookName: 'foo' + messageHeaders: + sample: header + assertThat: bookWasDeleted() diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_multipart.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_multipart.yml new file mode 100644 index 0000000000..33b2ee1617 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_multipart.yml @@ -0,0 +1,29 @@ +request: + method: PUT + url: /multipart + headers: + Content-Type: multipart/form-data;boundary=AaB03x + multipart: + params: + # key (parameter name), value (parameter value) pair + formParameter: '"formParameterValue"' + someBooleanParameter: true + named: + - paramName: file + fileName: filename.csv + fileContent: file content + matchers: + multipart: + params: + - key: formParameter + regex: ".+" + - key: someBooleanParameter + predefined: any_boolean + named: + - paramName: file + fileName: + predefined: non_empty + fileContent: + predefined: non_empty +response: + status: 200 \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_reference_request.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_reference_request.yml new file mode 100644 index 0000000000..09d43ff13e --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_reference_request.yml @@ -0,0 +1,30 @@ +request: + method: GET + url: /api/v1/xxxx + queryParameters: + foo: + - bar + - bar2 + headers: + Authorization: + - secret + - secret2 + body: + foo: bar + baz: 5 +response: + status: 200 + headers: + Authorization: "foo {{{ request.headers.Authorization.0 }}} bar" + body: + url: "{{{ request.url }}}" + path: "{{{ request.path }}}" + pathIndex: "{{{ request.path.1 }}}" + param: "{{{ request.query.foo }}}" + paramIndex: "{{{ request.query.foo.1 }}}" + authorization: "{{{ request.headers.Authorization.0 }}}" + authorization2: "{{{ request.headers.Authorization.1 }}" + fullBody: "{{{ request.body }}}" + responseFoo: "{{{ jsonpath this '$.foo' }}}" + responseBaz: "{{{ jsonpath this '$.baz' }}}" + responseBaz2: "Bla bla {{{ jsonpath this '$.foo' }}} bla bla" \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_rest.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_rest.yml new file mode 100644 index 0000000000..d50e335f0b --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_rest.yml @@ -0,0 +1,45 @@ +description: Some description +name: some name +priority: 8 +ignored: true +request: + url: /foo + queryParameters: + a: b + b: c + method: PUT + headers: + foo: bar + fooReq: baz + body: + foo: bar + matchers: + body: + - path: $.foo + type: by_regex + value: bar + headers: + - key: foo + regex: bar +response: + status: 200 + headers: + foo2: bar + foo3: foo33 + fooRes: baz + body: + foo2: bar + foo3: baz + matchers: + body: + - path: $.foo2 + type: by_regex + value: bar + - path: $.foo3 + type: by_command + value: executeMe($it) + headers: + - key: foo2 + regex: bar + - key: foo3 + command: andMeToo($it) \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/contract_rest_with_path.yml b/spring-cloud-contract-verifier/src/test/resources/yml/contract_rest_with_path.yml new file mode 100644 index 0000000000..1d0ad18e6b --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/contract_rest_with_path.yml @@ -0,0 +1,47 @@ +description: Some description +name: some name +priority: 8 +ignored: true +#tag::url_path[] +request: + method: PUT + urlPath: /foo +#end::url_path[] + queryParameters: + a: b + b: c + headers: + foo: bar + fooReq: baz + body: + foo: bar + matchers: + body: + - path: $.foo + type: by_regex + value: bar + headers: + - key: foo + regex: bar +response: + status: 200 + headers: + foo2: bar + foo3: foo33 + fooRes: baz + body: + foo2: bar + foo3: baz + matchers: + body: + - path: $.foo2 + type: by_regex + value: bar + - path: $.foo3 + type: by_command + value: executeMe($it) + headers: + - key: foo2 + regex: bar + - key: foo3 + command: andMeToo($it) \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/multiple_contracts.yml b/spring-cloud-contract-verifier/src/test/resources/yml/multiple_contracts.yml new file mode 100644 index 0000000000..f77f3586a0 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/multiple_contracts.yml @@ -0,0 +1,14 @@ +--- +name: should post a user +request: + method: POST + url: /users/1 +response: + status: 200 + +--- +request: + method: POST + url: /users/2 +response: + status: 200 \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/request.json b/spring-cloud-contract-verifier/src/test/resources/yml/request.json new file mode 100644 index 0000000000..8cd6b86c42 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/request.json @@ -0,0 +1 @@ +{ "hello" : "request" } \ No newline at end of file diff --git a/spring-cloud-contract-verifier/src/test/resources/yml/response.json b/spring-cloud-contract-verifier/src/test/resources/yml/response.json new file mode 100644 index 0000000000..f90d205256 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/yml/response.json @@ -0,0 +1 @@ +{ "hello" : "response" } \ No newline at end of file From 2fc0c75c4462e7107b2e3e79c3c4eb2e81858b64 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Fri, 19 Jan 2018 00:42:16 +0100 Subject: [PATCH 13/14] Bumped parent --- samples/standalone/yml/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/standalone/yml/pom.xml b/samples/standalone/yml/pom.xml index cc41273a7f..6fe941ebeb 100644 --- a/samples/standalone/yml/pom.xml +++ b/samples/standalone/yml/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-contract-samples-standalone - 1.2.2.BUILD-SNAPSHOT + 1.2.3.BUILD-SNAPSHOT .. From f486eeb85b3043e95a42f5567e588fc209abd89c Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Fri, 19 Jan 2018 00:47:20 +0100 Subject: [PATCH 14/14] Added 1.2.x to whitelist --- docs/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pom.xml b/docs/pom.xml index f9a22cb634..16b428c128 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -16,7 +16,7 @@ spring-cloud-contract ${basedir}/.. 3.4 - 1.0.x,1.1.x + 1.0.x,1.1.x,1.2.x