diff --git a/pom.xml b/pom.xml index 8027811e6b..2556a94a19 100644 --- a/pom.xml +++ b/pom.xml @@ -136,6 +136,12 @@ pom import + + + org.springframework + spring-core + 4.3.4.RELEASE + org.springframework.cloud spring-cloud-netflix-dependencies 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.groovy similarity index 80% rename from spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/ContractConverter.java rename to spring-cloud-contract-spec/src/main/groovy/org/springframework/cloud/contract/spec/ContractConverter.groovy index 964436c0bb..68c0d8fe45 100644 --- 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.groovy @@ -1,10 +1,8 @@ -package org.springframework.cloud.contract.spec; - -import java.io.File; +package org.springframework.cloud.contract.spec /** * Converter to be used to convert FROM {@link File} TO {@link Contract} - * and from {@link Contract} to {@link T} + * and from {@link Contract} to {@code T} * * @param - type to which we want to convert the contract * @@ -20,7 +18,7 @@ public interface ContractConverter { * @param file - file to be considered for conversion * @return - {@code true} if the given implementation can convert the file */ - boolean isAccepted(File file); + boolean isAccepted(File file) /** * Converts the given {@link File} to its {@link Contract} representation @@ -28,7 +26,7 @@ public interface ContractConverter { * @param file - file to convert * @return - {@link Contract} representation of the file */ - Contract convertFrom(File file); + Contract convertFrom(File file) /** * Converts the given {@link Contract} to a {@link T} representation @@ -36,5 +34,5 @@ public interface ContractConverter { * @param contract - the parsed contract * @return - {@link T} the type to which we do the conversion */ - T convertTo(Contract contract); + 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 2dc4823de6..dc77abad72 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml +++ b/spring-cloud-contract-tools/spring-cloud-contract-converters/pom.xml @@ -38,11 +38,6 @@ com.github.tomakehurst wiremock - - org.yaml - snakeyaml - optional - org.spockframework spock-core diff --git a/spring-cloud-contract-verifier/pom.xml b/spring-cloud-contract-verifier/pom.xml index f18b506d13..a95c66a130 100644 --- a/spring-cloud-contract-verifier/pom.xml +++ b/spring-cloud-contract-verifier/pom.xml @@ -45,6 +45,11 @@ spring-rabbit true + + org.yaml + snakeyaml + optional + javax.inject javax.inject diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGenerator.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGenerator.groovy index 0109024833..70a61e458b 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGenerator.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/builder/SingleTestGenerator.groovy @@ -123,7 +123,7 @@ class SingleTestGenerator { if (log.isDebugEnabled()) { log.debug("Stub content from file [${stubsFile.text}]") } - Contract stubContent = ContractVerifierDslConverter.convert(stubsFile) + Contract stubContent = it.convertedContract ?: ContractVerifierDslConverter.convert(stubsFile) TestType testType = (stubContent.input || stubContent.outputMessage) ? TestType.MESSAGING : TestType.HTTP return [(new ParsedDsl(it, stubContent, stubsFile)): testType] } 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-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContract.java similarity index 91% rename from spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContract.java rename to spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContract.java index 4910d8d84b..72fb0bbfb4 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContract.java +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContract.java @@ -1,4 +1,4 @@ -package org.springframework.cloud.contract.verifier.dsl; +package org.springframework.cloud.contract.verifier.converter; import java.util.HashMap; import java.util.Map; 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-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy similarity index 61% rename from spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContractConverter.groovy rename to spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy index 4071cb5d51..c9b6aed109 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-converters/src/main/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContractConverter.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverter.groovy @@ -1,4 +1,4 @@ -package org.springframework.cloud.contract.verifier.dsl +package org.springframework.cloud.contract.verifier.converter import groovy.transform.CompileStatic import org.springframework.cloud.contract.spec.Contract @@ -27,23 +27,23 @@ class YamlContractConverter implements ContractConverter { YamlContract yamlContract = new Yaml().loadAs(new FileInputStream(file), YamlContract.class) return Contract.make { request { - method(yamlContract.request.method) - url(yamlContract.request.url) + method(yamlContract?.request?.method) + url(yamlContract?.request?.url) headers { - yamlContract.request.headers.each { String key, Object value -> + yamlContract?.request?.headers?.each { String key, Object value -> header(key, value) } } - body(yamlContract.request.body) + body(yamlContract?.request?.body) } response { - status(yamlContract.response.status) + status(yamlContract?.response?.status) headers { - yamlContract.response.headers.each { String key, Object value -> + yamlContract?.response?.headers?.each { String key, Object value -> header(key, value) } } - body(yamlContract.response.body) + body(yamlContract?.response?.body) } } } @@ -57,15 +57,15 @@ class YamlContractConverter implements ContractConverter { // 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 + 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 + 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/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 45c33004bf..31c8d06434 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 @@ -21,6 +21,9 @@ import com.google.common.collect.ListMultimap import groovy.transform.CompileStatic import groovy.util.logging.Slf4j import org.apache.commons.lang3.SystemUtils +import org.springframework.cloud.contract.spec.Contract +import org.springframework.cloud.contract.spec.ContractConverter +import org.springframework.core.io.support.SpringFactoriesLoader import java.nio.file.FileSystem import java.nio.file.FileSystems @@ -74,7 +77,12 @@ class ContractFileScanner { return result } + /** + * We iterate over found contracts, filter out those that should be excluded + * and try to convert via pluggable Contract Converters any possible contracts + */ private void appendRecursively(File baseDir, ListMultimap result) { + List converters = SpringFactoriesLoader.loadFactories(ContractConverter, null) File[] files = baseDir.listFiles() if (!files) { return; @@ -85,12 +93,9 @@ class ContractFileScanner { boolean contractFile = isContractFile(file) boolean included = includeMatcher ? file.absolutePath.matches(includeMatcher) : true if (contractFile && included) { - Path path = file.toPath() - Integer order = null - if (hasScenarioFilenamePattern(path)) { - order = index - } - result.put(file.parentFile.toPath(), new ContractMetadata(path, matchesPattern(file, ignoreMatchers), files.size(), order)) + addContractToTestGeneration(result, files, file, index) + } else if (!contractFile && included) { + addContractToTestGeneration(converters, result, files, file, index) } else { appendRecursively(file, result) if (log.isDebugEnabled()) { @@ -105,6 +110,35 @@ class ContractFileScanner { } } + private void addContractToTestGeneration(List converters, ListMultimap result, + File[] files, File file, int index) { + boolean converted = false + for (ContractConverter converter : converters) { + if (converter.isAccepted(file)) { + addContractToTestGeneration(result, files, file, index, converter.convertFrom(file)) + converted = true + break + } + } + if (!converted) { + appendRecursively(file, result) + if (log.isDebugEnabled()) { + log.debug("File [$file] wasn't ignored but no converter was applicable.") + } + } + } + + private void addContractToTestGeneration(ListMultimap result, File[] files, File file, + int index, Contract convertedContract = null) { + Path path = file.toPath() + Integer order = null + if (hasScenarioFilenamePattern(path)) { + order = index + } + result.put(file.parentFile.toPath(), new ContractMetadata(path, matchesPattern(file, ignoreMatchers), + files.size(), order, convertedContract)) + } + private boolean hasScenarioFilenamePattern(Path path) { return SCENARIO_STEP_FILENAME_PATTERN.matcher(path.fileName.toString()).matches() } diff --git a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractMetadata.groovy b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractMetadata.groovy index 5bc5a668ec..05c17673b9 100644 --- a/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractMetadata.groovy +++ b/spring-cloud-contract-verifier/src/main/groovy/org/springframework/cloud/contract/verifier/file/ContractMetadata.groovy @@ -17,9 +17,9 @@ package org.springframework.cloud.contract.verifier.file import groovy.transform.CompileStatic +import org.springframework.cloud.contract.spec.Contract import java.nio.file.Path - /** * Contains metadata for a particular file with a DSL * @@ -33,12 +33,14 @@ class ContractMetadata { final boolean ignored final int groupSize final Integer order + final Contract convertedContract - ContractMetadata(Path path, boolean ignored, int groupSize, Integer order) { + ContractMetadata(Path path, boolean ignored, int groupSize, Integer order, Contract convertedContract = null) { this.groupSize = groupSize this.path = path this.ignored = ignored this.order = order + this.convertedContract = convertedContract } @Override @@ -48,6 +50,7 @@ class ContractMetadata { ", ignored=" + ignored + ", groupSize=" + groupSize + ", order=" + order + + ", convertedContract=" + convertedContract + '}' } } diff --git a/spring-cloud-contract-verifier/src/main/resources/META-INF/spring.factories b/spring-cloud-contract-verifier/src/main/resources/META-INF/spring.factories index 0e6be1c75d..505fcced9c 100644 --- a/spring-cloud-contract-verifier/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-contract-verifier/src/main/resources/META-INF/spring.factories @@ -5,4 +5,8 @@ org.springframework.cloud.contract.verifier.messaging.integration.ContractVerifi org.springframework.cloud.contract.verifier.messaging.amqp.ContractVerifierAmqpAutoConfiguration,\ org.springframework.cloud.contract.verifier.messaging.amqp.RabbitMockConnectionFactoryAutoConfiguration,\ org.springframework.cloud.contract.verifier.messaging.camel.ContractVerifierCamelConfiguration,\ -org.springframework.cloud.contract.verifier.messaging.noop.NoOpContractVerifierAutoConfiguration \ No newline at end of file +org.springframework.cloud.contract.verifier.messaging.noop.NoOpContractVerifierAutoConfiguration + +# Converters +org.springframework.cloud.contract.spec.ContractConverter=\ +org.springframework.cloud.contract.verifier.converter.YamlContractConverter \ No newline at end of file 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-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy similarity index 96% rename from spring-cloud-contract-tools/spring-cloud-contract-converters/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContractConverterSpec.groovy rename to spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy index ba9e9dc955..419a01521b 100644 --- a/spring-cloud-contract-tools/spring-cloud-contract-converters/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/YamlContractConverterSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/converter/YamlContractConverterSpec.groovy @@ -1,4 +1,4 @@ -package org.springframework.cloud.contract.verifier.dsl +package org.springframework.cloud.contract.verifier.converter import org.springframework.cloud.contract.spec.Contract import spock.lang.Specification @@ -59,4 +59,4 @@ class YamlContractConverterSpec extends Specification { 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-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScannerSpec.groovy b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScannerSpec.groovy index 73e7fc85c0..e501184cbf 100644 --- a/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScannerSpec.groovy +++ b/spring-cloud-contract-verifier/src/test/groovy/org/springframework/cloud/contract/verifier/file/ContractFileScannerSpec.groovy @@ -73,4 +73,16 @@ class ContractFileScannerSpec extends Specification { contracts.values().find { it.path.fileName.toString().startsWith('02') }.order == 1 contracts.values().find { it.path.fileName.toString().startsWith('03') }.order == 2 } + + def "should find contract files with converters"() { + given: + File baseDir = new File(this.getClass().getResource("/directory/with/mixed").toURI()) + ContractFileScanner scanner = new ContractFileScanner(baseDir, null, null) + when: + ListMultimap result = scanner.findContracts() + then: + result.keySet().size() == 1 + result.entries().find { it.value.convertedContract && it.value.convertedContract.request.method.clientValue == "PUT" } + result.entries().find { !it.value.convertedContract && !it.value.ignored } + } } diff --git a/spring-cloud-contract-tools/spring-cloud-contract-converters/src/test/resources/contract.yml b/spring-cloud-contract-verifier/src/test/resources/contract.yml similarity index 100% rename from spring-cloud-contract-tools/spring-cloud-contract-converters/src/test/resources/contract.yml rename to spring-cloud-contract-verifier/src/test/resources/contract.yml diff --git a/spring-cloud-contract-verifier/src/test/resources/directory/with/mixed/contract.yml b/spring-cloud-contract-verifier/src/test/resources/directory/with/mixed/contract.yml new file mode 100644 index 0000000000..783dafde0e --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/directory/with/mixed/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/src/test/resources/directory/with/mixed/dsl.groovy b/spring-cloud-contract-verifier/src/test/resources/directory/with/mixed/dsl.groovy new file mode 100644 index 0000000000..958ccd0673 --- /dev/null +++ b/spring-cloud-contract-verifier/src/test/resources/directory/with/mixed/dsl.groovy @@ -0,0 +1,16 @@ +/* + * 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. + */ +