From d63a557a29838cb2fc74c5d61d8464e9df36a2ce Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Mon, 5 Dec 2016 12:34:59 +0100 Subject: [PATCH] Added Contract converter + Yaml example --- .../contract/spec/ContractConverter.java | 40 +++++++++++ .../spring-cloud-contract-converters/pom.xml | 9 +++ .../contract/verifier/dsl/YamlContract.java | 29 ++++++++ .../verifier/dsl/YamlContractConverter.groovy | 72 +++++++++++++++++++ .../dsl/YamlContractConverterSpec.groovy | 62 ++++++++++++++++ .../src/test/resources/contract.yml | 13 ++++ spring-cloud-contract-verifier/pom.xml | 4 ++ .../verifier/file/ContractFileScanner.groovy | 2 +- 8 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/ContractConverter.java create mode 100644 spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContract.java create mode 100644 spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContractConverter.groovy create mode 100644 spring-cloud-contract-tools/spring-cloud-contract-converters/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContractConverterSpec.groovy create mode 100644 spring-cloud-contract-tools/spring-cloud-contract-converters/src/test/resources/contract.yml diff --git a/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/ContractConverter.java b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/ContractConverter.java new file mode 100644 index 0000000000..964436c0bb --- /dev/null +++ b/spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/ContractConverter.java @@ -0,0 +1,40 @@ +package org.springframework.cloud.contract.spec; + +import java.io.File; + +/** + * Converter to be used to convert FROM {@link File} TO {@link Contract} + * and from {@link Contract} to {@link T} + * + * @param - type to which we want to convert the contract + * + * @author Marcin Grzejszczak + * @since 1.0.3 + */ +public interface ContractConverter { + + /** + * Should this file be accepted by the converter. Can use the file extension + * to check if the conversion is possible. + * + * @param file - file to be considered for conversion + * @return - {@code true} if the given implementation can convert the file + */ + boolean isAccepted(File file); + + /** + * Converts the given {@link File} to its {@link Contract} representation + * + * @param file - file to convert + * @return - {@link Contract} representation of the file + */ + Contract convertFrom(File file); + + /** + * Converts the given {@link Contract} to a {@link T} representation + * + * @param contract - the parsed contract + * @return - {@link T} the type to which we do the conversion + */ + T convertTo(Contract contract); +} 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 98abb68bbf..2dc4823de6 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml @@ -18,6 +18,10 @@ org.springframework spring-context + + org.springframework.cloud + spring-cloud-contract-spec + org.springframework.cloud spring-cloud-contract-verifier @@ -34,6 +38,11 @@ com.github.tomakehurst wiremock + + org.yaml + snakeyaml + optional + org.spockframework spock-core diff --git a/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContract.java b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContract.java new file mode 100644 index 0000000000..4910d8d84b --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContract.java @@ -0,0 +1,29 @@ +package org.springframework.cloud.contract.verifier.dsl; + +import java.util.HashMap; +import java.util.Map; + +/** + * Yaml representation of a {@link org.springframework.cloud.contract.spec.Contract} + * + * @author Marcin Grzejszczak + * @since 1.0.3 + */ +//TODO: Perform full conversion +public class YamlContract { + public Request request = new Request(); + public Response response = new Response(); + + static class Request { + public String method; + public String url; + public Map headers = new HashMap<>(); + public Map body = new HashMap<>(); + } + + static class Response { + public int status; + public Map headers = new HashMap<>(); + public Map body = new HashMap<>(); + } +} \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContractConverter.groovy b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContractConverter.groovy new file mode 100644 index 0000000000..4071cb5d51 --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContractConverter.groovy @@ -0,0 +1,72 @@ +package org.springframework.cloud.contract.verifier.dsl + +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 + +/** + * Converter from and to a {@link YamlContract} to a + * @author Marcin Grzejszczak + * @since 1.0.3 + */ +//TODO: Perform full conversion +@CompileStatic +class YamlContractConverter implements ContractConverter { + + @Override + public boolean isAccepted(File file) { + String name = file.getName() + return name.endsWith(".yml") || name.endsWith(".yaml") + } + + @Override + public Contract convertFrom(File file) { + try { + YamlContract yamlContract = new Yaml().loadAs(new FileInputStream(file), YamlContract.class) + return Contract.make { + request { + method(yamlContract.request.method) + url(yamlContract.request.url) + headers { + yamlContract.request.headers.each { String key, Object value -> + header(key, value) + } + } + body(yamlContract.request.body) + } + response { + status(yamlContract.response.status) + headers { + yamlContract.response.headers.each { String key, Object value -> + header(key, value) + } + } + body(yamlContract.response.body) + } + } + } + catch (FileNotFoundException e) { + throw new IllegalStateException(e) + } + } + + @Override + public YamlContract convertTo(Contract contract) { + // TODO: Pick one of the sides - consumer / producer + 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-tools/spring-cloud-contract-converters/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContractConverterSpec.groovy b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContractConverterSpec.groovy new file mode 100644 index 0000000000..ba9e9dc955 --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContractConverterSpec.groovy @@ -0,0 +1,62 @@ +package org.springframework.cloud.contract.verifier.dsl + +import org.springframework.cloud.contract.spec.Contract +import spock.lang.Specification + +/** + * @author Marcin Grzejszczak + */ +class YamlContractConverterSpec extends Specification { + + URL ymlUrl = YamlContractConverterSpec.getResource("/contract.yml") + File yml = new File(ymlUrl.toURI()) + YamlContractConverter converter = new YamlContractConverter() + + def "should convert YAML to DSL"() { + given: + assert converter.isAccepted(yml) + when: + Contract contract = converter.convertFrom(yml) + then: + 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"] + } + + def "should convert DSL to YAML"() { + given: + assert converter.isAccepted(yml) + and: + Contract contract = Contract.make { + request { + url("/foo") + method("PUT") + headers { + header("foo", "bar") + } + body([foo: "bar"]) + } + response { + status(200) + headers { + header("foo2", "bar") + } + body([foo2: "bar"]) + } + } + when: + YamlContract yamlContract = converter.convertTo(contract) + then: + yamlContract.request.url == "/foo" + yamlContract.request.method == "PUT" + yamlContract.request.headers.find { it.key == "foo" && it.value == "bar" } + yamlContract.request.body == [foo: "bar"] + yamlContract.response.status == 200 + yamlContract.response.headers.find { it.key == "foo2" && it.value == "bar" } + yamlContract.response.body == [foo2: "bar"] + } +} \ No newline at end of file diff --git a/spring-cloud-contract-tools/spring-cloud-contract-converters/src/test/resources/contract.yml b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/test/resources/contract.yml new file mode 100644 index 0000000000..783dafde0e --- /dev/null +++ b/spring-cloud-contract-tools/spring-cloud-contract-converters/src/test/resources/contract.yml @@ -0,0 +1,13 @@ +request: + url: /foo + method: PUT + headers: + foo: bar + body: + foo: bar +response: + status: 200 + headers: + foo2: bar + body: + foo2: bar \ No newline at end of file diff --git a/spring-cloud-contract-verifier/pom.xml b/spring-cloud-contract-verifier/pom.xml index 4ab32ddd51..f18b506d13 100644 --- a/spring-cloud-contract-verifier/pom.xml +++ b/spring-cloud-contract-verifier/pom.xml @@ -17,6 +17,10 @@ org.springframework.cloud spring-cloud-contract-spec + + org.springframework + spring-core + org.springframework.boot spring-boot-test-autoconfigure 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 f7d2b420d1..45c33004bf 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 @@ -82,7 +82,7 @@ class ContractFileScanner { files.sort().eachWithIndex { File file, int index -> boolean excluded = matchesPattern(file, excludeMatchers) if (!excluded) { - boolean contractFile = isContractFile(file); + boolean contractFile = isContractFile(file) boolean included = includeMatcher ? file.absolutePath.matches(includeMatcher) : true if (contractFile && included) { Path path = file.toPath()