This commit is contained in:
Marcin Grzejszczak
2016-12-07 10:34:50 +01:00
parent 3e90c1ab9d
commit 57bf619471
14 changed files with 233 additions and 49 deletions

View File

@@ -569,7 +569,7 @@ If you want to generate stubs for other stub server than WireMock it's enough to
[source,groovy]
----
include::{converters_path}/src/main/groovy/org/springframework/cloud/contract/verifier/converter/SingleFileConverter.groovy[indent=0,lines=16..-1]
include::{converters_path}/src/main/groovy/org/springframework/cloud/contract/verifier/converter/StubGenerator.groovy[indent=0,lines=16..-1]
----
you can register your own implementation that generate Stubs. Again, it's enough to provide

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.wiremock
package org.springframework.cloud.contract.verifier.converter
import groovy.transform.CompileStatic
import org.springframework.cloud.contract.spec.ContractVerifierException

View File

@@ -0,0 +1,133 @@
/*
* 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 com.google.common.collect.ListMultimap
import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import org.springframework.cloud.contract.verifier.file.ContractFileScanner
import org.springframework.cloud.contract.verifier.file.ContractMetadata
import org.springframework.cloud.contract.verifier.util.NamesUtil
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
/**
* Recursively converts contracts into their stub representations
*
* @since 1.1.0
*/
@Slf4j
@CompileStatic
class RecursiveFilesConverter {
private final StubGeneratorHolder holder
private final ContractVerifierConfigProperties properties
private final File outMappingsDir
RecursiveFilesConverter(ContractVerifierConfigProperties properties, StubGeneratorHolder holder = null) {
this.properties = properties
this.outMappingsDir = properties.stubsOutputDir
this.holder = holder ?: new StubGeneratorHolder()
}
RecursiveFilesConverter(ContractVerifierConfigProperties properties, File outMappingsDir, StubGeneratorHolder holder = null) {
this.properties = properties
this.outMappingsDir = outMappingsDir
this.holder = holder ?: new StubGeneratorHolder()
}
@Deprecated
RecursiveFilesConverter(SingleFileConverter singleFileConverter, ContractVerifierConfigProperties properties) {
this.properties = properties
this.outMappingsDir = properties.stubsOutputDir
this.holder = new StubGeneratorHolder()
}
@Deprecated
RecursiveFilesConverter(SingleFileConverter singleFileConverter, ContractVerifierConfigProperties properties, File outMappingsDir) {
this.properties = properties
this.outMappingsDir = outMappingsDir
this.holder = new StubGeneratorHolder()
}
void processFiles() {
ContractFileScanner scanner = new ContractFileScanner(properties.contractsDslDir,
properties.excludedFiles as Set, [] as Set, properties.includedContracts)
ListMultimap<Path, ContractMetadata> contracts = scanner.findContracts()
if (log.isDebugEnabled()) {
log.debug("Found the following contracts $contracts")
}
contracts.asMap().entrySet().each { entry ->
entry.value.each { ContractMetadata contract ->
File sourceFile = contract.path.toFile()
StubGenerator stubGenerator = holder.converterForName(sourceFile.name);
try {
if (!contract.convertedContract && !stubGenerator) {
return
}
int contractsSize = contract.convertedContract.size()
Map<Contract, String> convertedContent = stubGenerator.convertContents(entry.key.last().toString(), contract)
if (!convertedContent) {
return
}
convertedContent.entrySet().eachWithIndex { Map.Entry<Contract, String> content, int index ->
Contract dsl = content.key
String converted = content.value
Path absoluteTargetPath = createAndReturnTargetDirectory(sourceFile)
File newJsonFile = createTargetFileWithProperName(stubGenerator, absoluteTargetPath,
sourceFile, contractsSize, index, dsl)
newJsonFile.setText(converted, StandardCharsets.UTF_8.toString())
}
} catch (Exception e) {
throw new ConversionContractVerifierException("Unable to make conversion of ${sourceFile.name}", e)
}
}
}
}
private Path createAndReturnTargetDirectory(File sourceFile) {
Path relativePath = Paths.get(properties.contractsDslDir.toURI()).relativize(sourceFile.parentFile.toPath())
Path absoluteTargetPath = outMappingsDir.toPath().resolve(relativePath)
Files.createDirectories(absoluteTargetPath)
return absoluteTargetPath
}
private File createTargetFileWithProperName(StubGenerator stubGenerator, Path absoluteTargetPath,
File sourceFile, int contractsSize, int index, Contract dsl) {
String name = generateName(dsl, contractsSize, stubGenerator, sourceFile, index)
File newJsonFile = new File(absoluteTargetPath.toFile(), name)
log.info("Creating new stub [$newJsonFile.path]")
return newJsonFile
}
private String generateName(Contract dsl, int contractsSize, StubGenerator converter,
File sourceFile, int index) {
String generatedName = converter.generateOutputFileNameForInput(sourceFile.name)
String extension = NamesUtil.afterLastDot(generatedName)
if (dsl.name) {
return "${dsl.name}.${extension}"
} else if (contractsSize == 1) {
return generatedName
}
return "${index}_${generatedName}"
}
}

View File

@@ -24,8 +24,10 @@ import org.springframework.cloud.contract.verifier.file.ContractMetadata
* Converts contracts into their stub representation.
*
* @since 1.0.0
* @deprecated use {@link StubGenerator}
*/
@CompileStatic
@Deprecated
interface SingleFileConverter {
/**

View File

@@ -0,0 +1,53 @@
/*
* 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
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.file.ContractMetadata
/**
* Converts contracts into their stub representation.
*
* @since 1.0.0
*/
@CompileStatic
interface StubGenerator {
/**
* Returns {@code true} if the converter can handle the file to convert it into a stub.
*/
boolean canHandleFileName(String fileName)
/**
* Returns the collection of converted contracts into stubs. One contract can
* result in multiple stubs.
*/
Map<Contract, String> convertContents(String rootName, ContractMetadata content)
/**
* Returns the name of the converted stub file. If you have multiple contracts
* in a single file then a prefix will be added to the generated file. If you
* provide the {@link Contract#name} field then that field will override the
* generated file name.
*
* Example: name of file with 2 contracts is {@code foo.groovy}, it will be
* converted by the implementation to {@code foo.json}. The recursive file
* converter will create two files {@code 0_foo.json} and {@code 1_foo.json}
*/
String generateOutputFileNameForInput(String inputFileName)
}

View File

@@ -10,19 +10,19 @@ import org.springframework.core.io.support.SpringFactoriesLoader
* @since 1.1.0
*/
@CompileStatic
class SingleFileConvertersHolder {
class StubGeneratorHolder {
private final List<SingleFileConverter> converters = []
private final List<StubGenerator> converters = []
SingleFileConvertersHolder() {
this.converters.addAll(SpringFactoriesLoader.loadFactories(SingleFileConverter, null))
StubGeneratorHolder() {
this.converters.addAll(SpringFactoriesLoader.loadFactories(StubGenerator, null))
}
SingleFileConvertersHolder(List<SingleFileConverter> converters) {
StubGeneratorHolder(List<StubGenerator> converters) {
this.converters.addAll(converters)
}
SingleFileConverter converterForName(String fileName) {
StubGenerator converterForName(String fileName) {
return this.converters.find { it.canHandleFileName(fileName) }
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.cloud.contract.verifier.wiremock
import groovy.transform.CompileStatic
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubStrategy
@@ -31,7 +32,6 @@ import java.nio.charset.StandardCharsets
@CompileStatic
class DslToWireMockClientConverter extends DslToWireMockConverter {
@Override
@Deprecated
String convertContent(String rootName, ContractMetadata contract) {
return convertASingleContract(rootName, contract, contract.convertedContract.first() ?: createGroovyDSLFromStringContent(

View File

@@ -18,16 +18,16 @@ package org.springframework.cloud.contract.verifier.wiremock
import groovy.transform.CompileStatic
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.converter.SingleFileConverter
import org.springframework.cloud.contract.verifier.converter.StubGenerator
import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter
/**
* WireMock implementation of the {@link SingleFileConverter}
* WireMock implementation of the {@link StubGenerator}
*
* @since 1.0.0
*/
@CompileStatic
abstract class DslToWireMockConverter implements SingleFileConverter {
abstract class DslToWireMockConverter implements StubGenerator {
@Override
boolean canHandleFileName(String fileName) {

View File

@@ -21,8 +21,10 @@ import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import org.springframework.cloud.contract.verifier.converter.ConversionContractVerifierException
import org.springframework.cloud.contract.verifier.converter.SingleFileConverter
import org.springframework.cloud.contract.verifier.converter.SingleFileConvertersHolder
import org.springframework.cloud.contract.verifier.converter.StubGenerator
import org.springframework.cloud.contract.verifier.converter.StubGeneratorHolder
import org.springframework.cloud.contract.verifier.file.ContractFileScanner
import org.springframework.cloud.contract.verifier.file.ContractMetadata
import org.springframework.cloud.contract.verifier.util.NamesUtil
@@ -36,40 +38,41 @@ import java.nio.file.Paths
* Recursively converts contracts into their stub representations
*
* @since 1.0.0
* @deprecated use {@link org.springframework.cloud.contract.verifier.converter.RecursiveFilesConverter}
*/
//TODO: Move out of here to converter package
@Slf4j
@CompileStatic
@Deprecated
class RecursiveFilesConverter {
private final SingleFileConvertersHolder holder
private final StubGeneratorHolder holder
private final ContractVerifierConfigProperties properties
private final File outMappingsDir
RecursiveFilesConverter(ContractVerifierConfigProperties properties, SingleFileConvertersHolder holder = null) {
RecursiveFilesConverter(ContractVerifierConfigProperties properties, StubGeneratorHolder holder = null) {
this.properties = properties
this.outMappingsDir = properties.stubsOutputDir
this.holder = holder ?: new SingleFileConvertersHolder()
this.holder = holder ?: new StubGeneratorHolder()
}
RecursiveFilesConverter(ContractVerifierConfigProperties properties, File outMappingsDir, SingleFileConvertersHolder holder = null) {
RecursiveFilesConverter(ContractVerifierConfigProperties properties, File outMappingsDir, StubGeneratorHolder holder = null) {
this.properties = properties
this.outMappingsDir = outMappingsDir
this.holder = holder ?: new SingleFileConvertersHolder()
this.holder = holder ?: new StubGeneratorHolder()
}
@Deprecated
RecursiveFilesConverter(SingleFileConverter singleFileConverter, ContractVerifierConfigProperties properties) {
this.properties = properties
this.outMappingsDir = properties.stubsOutputDir
this.holder = new SingleFileConvertersHolder()
this.holder = new StubGeneratorHolder()
}
@Deprecated
RecursiveFilesConverter(SingleFileConverter singleFileConverter, ContractVerifierConfigProperties properties, File outMappingsDir) {
this.properties = properties
this.outMappingsDir = outMappingsDir
this.holder = new SingleFileConvertersHolder()
this.holder = new StubGeneratorHolder()
}
void processFiles() {
@@ -82,13 +85,13 @@ class RecursiveFilesConverter {
contracts.asMap().entrySet().each { entry ->
entry.value.each { ContractMetadata contract ->
File sourceFile = contract.path.toFile()
SingleFileConverter singleFileConverter = holder.converterForName(sourceFile.name);
StubGenerator stubGenerator = holder.converterForName(sourceFile.name);
try {
if (!contract.convertedContract && !singleFileConverter) {
if (!contract.convertedContract && !stubGenerator) {
return
}
int contractsSize = contract.convertedContract.size()
Map<Contract, String> convertedContent = singleFileConverter.convertContents(entry.key.last().toString(), contract)
Map<Contract, String> convertedContent = stubGenerator.convertContents(entry.key.last().toString(), contract)
if (!convertedContent) {
return
}
@@ -96,7 +99,7 @@ class RecursiveFilesConverter {
Contract dsl = content.key
String converted = content.value
Path absoluteTargetPath = createAndReturnTargetDirectory(sourceFile)
File newJsonFile = createTargetFileWithProperName(singleFileConverter, absoluteTargetPath,
File newJsonFile = createTargetFileWithProperName(stubGenerator, absoluteTargetPath,
sourceFile, contractsSize, index, dsl)
newJsonFile.setText(converted, StandardCharsets.UTF_8.toString())
}
@@ -114,15 +117,15 @@ class RecursiveFilesConverter {
return absoluteTargetPath
}
private File createTargetFileWithProperName(SingleFileConverter singleFileConverter, Path absoluteTargetPath,
private File createTargetFileWithProperName(StubGenerator stubGenerator, Path absoluteTargetPath,
File sourceFile, int contractsSize, int index, Contract dsl) {
String name = generateName(dsl, contractsSize, singleFileConverter, sourceFile, index)
String name = generateName(dsl, contractsSize, stubGenerator, sourceFile, index)
File newJsonFile = new File(absoluteTargetPath.toFile(), name)
log.info("Creating new stub [$newJsonFile.path]")
return newJsonFile
}
private String generateName(Contract dsl, int contractsSize, SingleFileConverter converter,
private String generateName(Contract dsl, int contractsSize, StubGenerator converter,
File sourceFile, int index) {
String generatedName = converter.generateOutputFileNameForInput(sourceFile.name)
String extension = NamesUtil.afterLastDot(generatedName)

View File

@@ -1,3 +1,3 @@
# Stub converters
org.springframework.cloud.contract.verifier.converter.SingleFileConverter=\
org.springframework.cloud.contract.verifier.converter.StubGenerator=\
org.springframework.cloud.contract.verifier.wiremock.DslToWireMockClientConverter

View File

@@ -14,14 +14,12 @@
* limitations under the License.
*/
package org.springframework.cloud.contract.verifier.wiremock
package org.springframework.cloud.contract.verifier.converter
import groovy.io.FileType
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import org.springframework.cloud.contract.verifier.converter.SingleFileConverter
import org.springframework.cloud.contract.verifier.converter.SingleFileConvertersHolder
import org.springframework.util.FileSystemUtils
import spock.lang.Specification
@@ -68,12 +66,7 @@ class RecursiveFilesConverterSpec extends Specification {
properties.excludedFiles = ["dir1/**"]
FileSystemUtils.copyRecursively(originalSourceRootDirectory, properties.contractsDslDir)
and:
def singleFileConverterStub = Stub(SingleFileConverter)
singleFileConverterStub.canHandleFileName(_) >> { String fileName -> fileName.endsWith(".groovy") }
singleFileConverterStub.convertContent(_, _) >> { "converted" }
singleFileConverterStub.generateOutputFileNameForInput(_) >> { String inputFileName -> inputFileName.replaceAll('.groovy', '.json') }
RecursiveFilesConverter recursiveFilesConverter = new RecursiveFilesConverter(singleFileConverterStub, properties)
RecursiveFilesConverter recursiveFilesConverter = new RecursiveFilesConverter(properties)
when:
recursiveFilesConverter.processFiles()
then:
@@ -82,21 +75,21 @@ class RecursiveFilesConverterSpec extends Specification {
Set<String> relativizedCreatedFiles = getRelativePathsForFilesInDirectory(createdFiles, properties.stubsOutputDir)
[Paths.get("dslRoot.json"), Paths.get("dir2/dsl2.json")] as Set == relativizedCreatedFiles as Set
and:
createdFiles.each { it.text == "converted" }
createdFiles.each { assert it.text.contains("uuid") }
}
def "on failure should break processing and throw meaningful exception"() {
given:
def sourceFile = tmpFolder.newFile("test.groovy")
and:
def singleFileConverterStub = Stub(SingleFileConverter)
singleFileConverterStub.canHandleFileName(_) >> { true }
singleFileConverterStub.convertContents(_, _) >> { throw new NullPointerException("Test conversion error") }
singleFileConverterStub.generateOutputFileNameForInput(_) >> { String inputFileName -> "${inputFileName}2" }
def stubGenerator = Stub(StubGenerator)
stubGenerator.canHandleFileName(_) >> { true }
stubGenerator.convertContents(_, _) >> { throw new NullPointerException("Test conversion error") }
stubGenerator.generateOutputFileNameForInput(_) >> { String inputFileName -> "${inputFileName}2" }
ContractVerifierConfigProperties properties = new ContractVerifierConfigProperties()
properties.contractsDslDir = tmpFolder.root
properties.stubsOutputDir = tmpFolder.root
RecursiveFilesConverter recursiveFilesConverter = new RecursiveFilesConverter(properties, new SingleFileConvertersHolder([singleFileConverterStub]))
RecursiveFilesConverter recursiveFilesConverter = new RecursiveFilesConverter(properties, new StubGeneratorHolder([stubGenerator]))
when:
recursiveFilesConverter.processFiles()
then:

View File

@@ -29,7 +29,7 @@ class WiremockScenarioConverterSpec extends Specification {
DslToWireMockClientConverter converter = new DslToWireMockClientConverter()
Path dsl = Paths.get(this.getClass().getResource("/converter/scenario/main_scenario/01_login.groovy").toURI())
when:
String content = converter.convertContent("Test", new ContractMetadata(dsl, false, 3, 0))
String content = converter.convertContents("Test", new ContractMetadata(dsl, false, 3, 0)).values().first()
then:
content.contains('"requiredScenarioState" : "Started"')
content.contains('"newScenarioState" : "Step1"')
@@ -41,7 +41,7 @@ class WiremockScenarioConverterSpec extends Specification {
DslToWireMockClientConverter converter = new DslToWireMockClientConverter()
Path dsl = Paths.get(this.getClass().getResource("/converter/scenario/main_scenario/02_showCart.groovy").toURI())
when:
String content = converter.convertContent("Test", new ContractMetadata(dsl, false, 3, 1))
String content = converter.convertContents("Test", new ContractMetadata(dsl, false, 3, 1)).values().first()
then:
content.contains('"requiredScenarioState" : "Step1"')
content.contains('"newScenarioState" : "Step2"')
@@ -53,7 +53,7 @@ class WiremockScenarioConverterSpec extends Specification {
DslToWireMockClientConverter converter = new DslToWireMockClientConverter()
Path dsl = Paths.get(this.getClass().getResource("/converter/scenario/main_scenario/03_logout.groovy").toURI())
when:
String content = converter.convertContent("Test", new ContractMetadata(dsl, false, 3, 2))
String content = converter.convertContents("Test", new ContractMetadata(dsl, false, 3, 2)).values().first()
then:
content.contains('"requiredScenarioState" : "Step2"')
!content.contains('"newScenarioState"')

View File

@@ -21,7 +21,7 @@ import org.gradle.api.internal.ConventionTask
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import org.springframework.cloud.contract.verifier.wiremock.RecursiveFilesConverter
import org.springframework.cloud.contract.verifier.converter.RecursiveFilesConverter
import static org.springframework.cloud.contract.verifier.plugin.SpringCloudContractVerifierGradlePlugin.COPY_CONTRACTS_TASK_NAME
//TODO: Implement as an incremental task: https://gradle.org/docs/current/userguide/custom_tasks.html#incremental_tasks ?

View File

@@ -32,7 +32,7 @@ import org.apache.maven.shared.filtering.MavenResourcesFiltering;
import org.eclipse.aether.RepositorySystemSession;
import org.springframework.cloud.contract.maven.verifier.stubrunner.AetherStubDownloaderFactory;
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties;
import org.springframework.cloud.contract.verifier.wiremock.RecursiveFilesConverter;
import org.springframework.cloud.contract.verifier.converter.RecursiveFilesConverter;
/**
* Convert Spring Cloud Contract Verifier contracts into WireMock stubs mappings.