Convert stub runner integration to Java

This commit is contained in:
Dave Syer
2016-07-15 17:57:37 +01:00
parent f1634eb0ff
commit bbec38fa74
8 changed files with 210 additions and 173 deletions

View File

@@ -90,7 +90,7 @@ public class StubRunnerCamelPredicate implements Predicate {
Object valueInHeader = headers.get(name);
matches &= value instanceof Pattern ?
((Pattern) value).matcher(valueInHeader.toString()).matches() :
valueInHeader.equals(value);
valueInHeader!=null && valueInHeader.equals(value);
}
return matches;
}

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.cloud</groupId>
@@ -12,6 +12,9 @@
<packaging>jar</packaging>
<name>Spring Cloud Contract Stub Runner Integration</name>
<description>Spring Cloud Contract Stub Runner Integration</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
@@ -72,9 +75,6 @@
<executions>
<execution>
<goals>
<goal>addSources</goal>
<goal>addTestSources</goal>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>

View File

@@ -1,74 +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.stubrunner.messaging.integration
import groovy.transform.CompileStatic
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.stubrunner.StubConfiguration
import org.springframework.beans.factory.config.AutowireCapableBeanFactory
import org.springframework.cloud.contract.stubrunner.BatchStubRunner
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerConfiguration
import org.springframework.context.Lifecycle
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import org.springframework.integration.dsl.FilterEndpointSpec
import org.springframework.integration.dsl.GenericEndpointSpec
import org.springframework.integration.dsl.IntegrationFlowBuilder
import org.springframework.integration.dsl.IntegrationFlows
import org.springframework.messaging.Message
/**
* Spring Integration configuration that iterates over the downloaded Groovy DSLs
* and registers a flow for each DSL.
*
* @author Marcin Grzejszczak
*/
@Configuration
@CompileStatic
class StubRunnerIntegrationConfiguration {
@Bean
FlowRegistrar flowRegistrar(AutowireCapableBeanFactory beanFactory, BatchStubRunner batchStubRunner) {
Map<StubConfiguration, Collection<Contract>> contracts = batchStubRunner.contracts
contracts.each { StubConfiguration key, Collection<Contract> value ->
String name = "${key.groupId}_${key.artifactId}"
value.findAll { it?.input?.messageFrom?.clientValue }.each { Contract dsl ->
String flowName = "${name}_${dsl.label}_${dsl.hashCode()}"
IntegrationFlowBuilder builder = IntegrationFlows.from(dsl.input.messageFrom.clientValue)
.filter(new StubRunnerIntegrationMessageSelector(dsl), { FilterEndpointSpec e -> e.id("${flowName}.filter") } )
.transform(new StubRunnerIntegrationTransformer(dsl), { GenericEndpointSpec e -> e.id("${flowName}.transformer") })
if (dsl.outputMessage?.sentTo) {
builder = builder.channel(dsl.outputMessage.sentTo.clientValue)
} else {
builder = builder.handle(new DummyMessageHandler(), "handle")
}
beanFactory.initializeBean(builder.get(), flowName)
beanFactory.getBean("${flowName}.filter", Lifecycle.class).start();
beanFactory.getBean("${flowName}.transformer", Lifecycle.class).start();
}
}
return new FlowRegistrar()
}
@CompileStatic
private static class DummyMessageHandler {
void handle(Message message) {}
}
static class FlowRegistrar {}
}

View File

@@ -1,78 +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.stubrunner.messaging.integration
import com.jayway.jsonpath.DocumentContext
import com.jayway.jsonpath.JsonPath
import com.toomuchcoding.jsonassert.JsonAssertion
import com.toomuchcoding.jsonassert.JsonVerifiable
import groovy.transform.CompileStatic
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.messaging.ContractVerifierObjectMapper
import org.springframework.cloud.contract.verifier.util.JsonPaths
import org.springframework.cloud.contract.verifier.util.JsonToJsonPathsConverter
import org.springframework.integration.core.MessageSelector
import org.springframework.messaging.Message
import java.util.regex.Pattern
/**
* Passes through a message that matches the one defined in the DSL
*
* @author Marcin Grzejszczak
*/
@CompileStatic
class StubRunnerIntegrationMessageSelector implements MessageSelector {
private final Contract groovyDsl
private final ContractVerifierObjectMapper objectMapper = new ContractVerifierObjectMapper()
StubRunnerIntegrationMessageSelector(Contract groovyDsl) {
this.groovyDsl = groovyDsl
}
@Override
boolean accept(Message<?> message) {
if(!headersMatch(message)){
return false
}
Object inputMessage = message.getPayload()
JsonPaths jsonPaths = JsonToJsonPathsConverter.transformToJsonPathWithStubsSideValues(groovyDsl.input.messageBody)
DocumentContext parsedJson = JsonPath.parse(objectMapper.writeValueAsString(inputMessage))
return jsonPaths.every { matchesJsonPath(parsedJson, it) }
}
private boolean matchesJsonPath(DocumentContext parsedJson, JsonVerifiable jsonVerifiable) {
try {
JsonAssertion.assertThat(parsedJson).matchesJsonPath(jsonVerifiable.jsonPath())
return true
} catch (Exception e) {
return false
}
}
private boolean headersMatch(Message message) {
Map<String, Object> headers = message.getHeaders()
return groovyDsl.input.messageHeaders.entries.every {
String name = it.name
Object value = it.clientValue
Object valueInHeader = headers.get(name)
return value instanceof Pattern ?
value.matcher(valueInHeader.toString()).matches() :
valueInHeader == value
}
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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.stubrunner.messaging.integration;
import java.util.Collection;
import java.util.Map;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.cloud.contract.spec.Contract;
import org.springframework.cloud.contract.stubrunner.BatchStubRunner;
import org.springframework.cloud.contract.stubrunner.StubConfiguration;
import org.springframework.context.Lifecycle;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.dsl.IntegrationFlowBuilder;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.messaging.Message;
/**
* Spring Integration configuration that iterates over the downloaded Groovy DSLs and
* registers a flow for each DSL.
*
* @author Marcin Grzejszczak
*/
@Configuration
public class StubRunnerIntegrationConfiguration {
@Bean
public FlowRegistrar flowRegistrar(AutowireCapableBeanFactory beanFactory,
BatchStubRunner batchStubRunner) {
Map<StubConfiguration, Collection<Contract>> contracts = batchStubRunner
.getContracts();
contracts.entrySet().stream().forEach(entry -> {
String name = entry.getKey().getGroupId() + "_"
+ entry.getKey().getArtifactId();
entry.getValue().stream().forEach(dsl -> {
if (dsl.getInput() != null && dsl.getInput().getMessageFrom() != null
&& dsl.getInput().getMessageFrom().getClientValue() != null) {
String flowName = name + "_" + dsl.getLabel() + "_" + dsl.hashCode();
IntegrationFlowBuilder builder = IntegrationFlows
.from(dsl.getInput().getMessageFrom().getClientValue())
.filter(new StubRunnerIntegrationMessageSelector(dsl),
e -> e.id(flowName + ".filter"))
.transform(new StubRunnerIntegrationTransformer(dsl),
e -> e.id(flowName + ".transformer"));
if (dsl.getOutputMessage() != null) {
builder = builder.channel(
dsl.getOutputMessage().getSentTo().getClientValue());
}
else {
builder = builder.handle(new DummyMessageHandler(), "handle");
}
beanFactory.initializeBean(builder.get(), flowName);
beanFactory.getBean(flowName + ".filter", Lifecycle.class).start();
beanFactory.getBean(flowName + ".transformer", Lifecycle.class)
.start();
}
});
});
return new FlowRegistrar();
}
private static class DummyMessageHandler {
@SuppressWarnings("unused")
public void handle(Message<?> message) {
}
}
static class FlowRegistrar {
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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.stubrunner.messaging.integration;
import java.util.Map;
import java.util.regex.Pattern;
import org.springframework.cloud.contract.spec.Contract;
import org.springframework.cloud.contract.spec.internal.Header;
import org.springframework.cloud.contract.verifier.messaging.ContractVerifierObjectMapper;
import org.springframework.cloud.contract.verifier.util.JsonPaths;
import org.springframework.cloud.contract.verifier.util.JsonToJsonPathsConverter;
import org.springframework.cloud.contract.verifier.util.MethodBufferingJsonVerifiable;
import org.springframework.integration.core.MessageSelector;
import org.springframework.messaging.Message;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import com.toomuchcoding.jsonassert.JsonAssertion;
import com.toomuchcoding.jsonassert.JsonVerifiable;
import groovy.transform.CompileStatic;
/**
* Passes through a message that matches the one defined in the DSL
*
* @author Marcin Grzejszczak
*/
@CompileStatic
public class StubRunnerIntegrationMessageSelector implements MessageSelector {
private final Contract groovyDsl;
private final ContractVerifierObjectMapper objectMapper = new ContractVerifierObjectMapper();
StubRunnerIntegrationMessageSelector(Contract groovyDsl) {
this.groovyDsl = groovyDsl;
}
@Override
public boolean accept(Message<?> message) {
if (!headersMatch(message)) {
return false;
}
Object inputMessage = message.getPayload();
JsonPaths jsonPaths = JsonToJsonPathsConverter
.transformToJsonPathWithStubsSideValues(
groovyDsl.getInput().getMessageBody());
DocumentContext parsedJson;
try {
parsedJson = JsonPath.parse(objectMapper.writeValueAsString(inputMessage));
}
catch (JsonProcessingException e) {
throw new IllegalStateException("Cannot serialize to JSON", e);
}
boolean matches = true;
for (MethodBufferingJsonVerifiable path : jsonPaths) {
matches &= matchesJsonPath(parsedJson, path);
}
return matches;
}
private boolean matchesJsonPath(DocumentContext parsedJson,
JsonVerifiable jsonVerifiable) {
try {
JsonAssertion.assertThat(parsedJson)
.matchesJsonPath(jsonVerifiable.jsonPath());
return true;
}
catch (Exception e) {
return false;
}
}
private boolean headersMatch(Message<?> message) {
Map<String, Object> headers = message.getHeaders();
boolean matches = true;
for (Header it : groovyDsl.getInput().getMessageHeaders().getEntries()) {
String name = it.getName();
Object value = it.getClientValue();
Object valueInHeader = headers.get(name);
matches &= value instanceof Pattern ?
((Pattern) value).matcher(valueInHeader.toString()).matches() :
valueInHeader!=null && valueInHeader.equals(value);
}
return matches;
}
}

View File

@@ -14,35 +14,37 @@
* limitations under the License.
*/
package org.springframework.cloud.contract.stubrunner.messaging.integration
package org.springframework.cloud.contract.stubrunner.messaging.integration;
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.util.BodyExtractor
import org.springframework.integration.transformer.GenericTransformer
import org.springframework.messaging.Message
import org.springframework.messaging.MessageHeaders
import org.springframework.messaging.support.MessageBuilder
import java.util.Map;
import org.springframework.cloud.contract.spec.Contract;
import org.springframework.cloud.contract.verifier.util.BodyExtractor;
import org.springframework.integration.transformer.GenericTransformer;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.MessageBuilder;
/**
* Sends forward a message defined in the DSL.
*
* @author Marcin Grzejszczak
*/
class StubRunnerIntegrationTransformer implements GenericTransformer<Message<?>, Message<?>> {
public class StubRunnerIntegrationTransformer implements GenericTransformer<Message<?>, Message<?>> {
private final Contract groovyDsl
private final Contract groovyDsl;
StubRunnerIntegrationTransformer(Contract groovyDsl) {
this.groovyDsl = groovyDsl
this.groovyDsl = groovyDsl;
}
@Override
Message<?> transform(Message<?> source) {
if (!groovyDsl.outputMessage) {
return source
public Message<?> transform(Message<?> source) {
if (groovyDsl.getOutputMessage()==null) {
return source;
}
String payload = BodyExtractor.extractStubValueFrom(groovyDsl.outputMessage.body)
Map<String, Object> headers = groovyDsl.outputMessage.headers.asStubSideMap()
return MessageBuilder.createMessage(payload, new MessageHeaders(headers))
String payload = BodyExtractor.extractStubValueFrom(groovyDsl.getOutputMessage().getBody());
Map<String, Object> headers = groovyDsl.getOutputMessage().getHeaders().asStubSideMap();
return MessageBuilder.createMessage(payload, new MessageHeaders(headers));
}
}