Using factories

This commit is contained in:
Marcin Grzejszczak
2016-12-05 14:03:08 +01:00
parent d63a557a29
commit f148c5b8bf
15 changed files with 126 additions and 40 deletions

View File

@@ -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]
}

View File

@@ -0,0 +1,29 @@
package org.springframework.cloud.contract.verifier.converter;
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<String, Object> headers = new HashMap<>();
public Map<String, Object> body = new HashMap<>();
}
static class Response {
public int status;
public Map<String, Object> headers = new HashMap<>();
public Map<String, Object> body = new HashMap<>();
}
}

View File

@@ -0,0 +1,72 @@
package org.springframework.cloud.contract.verifier.converter
import groovy.transform.CompileStatic
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.ContractConverter
import org.springframework.cloud.contract.spec.internal.Headers
import org.yaml.snakeyaml.Yaml
/**
* 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<YamlContract> {
@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
}
}

View File

@@ -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<Path, ContractMetadata> result) {
List<ContractConverter> 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<ContractConverter> converters, ListMultimap<Path, ContractMetadata> 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<Path, ContractMetadata> 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()
}

View File

@@ -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 +
'}'
}
}

View File

@@ -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
org.springframework.cloud.contract.verifier.messaging.noop.NoOpContractVerifierAutoConfiguration
# Converters
org.springframework.cloud.contract.spec.ContractConverter=\
org.springframework.cloud.contract.verifier.converter.YamlContractConverter

View File

@@ -0,0 +1,62 @@
package org.springframework.cloud.contract.verifier.converter
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"]
}
}

View File

@@ -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<Path, ContractMetadata> 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 }
}
}

View File

@@ -0,0 +1,13 @@
request:
url: /foo
method: PUT
headers:
foo: bar
body:
foo: bar
response:
status: 200
headers:
foo2: bar
body:
foo2: bar

View File

@@ -0,0 +1,13 @@
request:
url: /foo
method: PUT
headers:
foo: bar
body:
foo: bar
response:
status: 200
headers:
foo2: bar
body:
foo2: bar

View File

@@ -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.
*/