Skip to content

Commit

Permalink
Added support for camel with Boot 2.0; gh-673
Browse files Browse the repository at this point in the history
  • Loading branch information
Stefan_Stus committed Nov 8, 2018
1 parent 4c3d0d2 commit a8aa7a0
Show file tree
Hide file tree
Showing 38 changed files with 1,862 additions and 7 deletions.
7 changes: 1 addition & 6 deletions docs/src/main/asciidoc/migrations.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,4 @@ version of the release train (and vice versa).
Done via https://github.com/spring-cloud/spring-cloud-contract/issues/267[issue 267]

[[cloud-verifier-1.2-2.0]]
=== 1.2.x -> 2.0.x

==== No Camel support

We will add back Apache Camel support only after this https://issues.apache.org/jira/browse/CAMEL-11430[issue]
gets fixed
=== 1.2.x -> 2.0.x
3 changes: 3 additions & 0 deletions docs/src/main/asciidoc/verifier_stubrunner_msg.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ frameworks:

* Spring Integration
* Spring Cloud Stream
* Apache Camel
* Spring AMQP

It also provides entry points to integrate with any other solution on the market.
Expand Down Expand Up @@ -65,6 +66,8 @@ include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/o
include::{tests_path}/spring-cloud-contract-stub-runner-stream/src/test/groovy/org/springframework/cloud/contract/stubrunner/messaging/stream/StreamStubRunnerSpec.groovy[tags=trigger_all,indent=0]
----

include::{tests_path}/spring-cloud-contract-stub-runner-camel/README.adoc[]

include::{tests_path}/spring-cloud-contract-stub-runner-integration/README.adoc[]

include::{tests_path}/spring-cloud-contract-stub-runner-stream/README.adoc[]
Expand Down
53 changes: 53 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
<inceptionYear>2016</inceptionYear>

<properties>
<activemq.version>5.12.1</activemq.version>
<camel.version>2.22.1</camel.version>
<checkstyle.version>2.17</checkstyle.version>
<pact.version>3.5.13</pact.version>
<jsch-agent.version>0.0.9</jsch-agent.version>
<spring-cloud-build.version>2.1.0.BUILD-SNAPSHOT</spring-cloud-build.version>
Expand Down Expand Up @@ -71,6 +74,56 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring-boot</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-core</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-kafka</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jackson</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jms</artifactId>
<version>${camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-camel</artifactId>
<version>${activemq.version}</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
<version>${activemq.version}</version>
</dependency>
<dependency>
<groupId>net.sf.jopt-simple</groupId>
<artifactId>jopt-simple</artifactId>
Expand Down
15 changes: 15 additions & 0 deletions spring-cloud-contract-stub-runner/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@
<artifactId>jopt-simple</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring-boot</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jackson</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2013-2017 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.camel;

import org.apache.camel.RoutesBuilder;
import org.apache.camel.spring.SpringRouteBuilder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
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.annotation.*;

import java.util.Collection;
import java.util.Map;

/**
* Camel configuration that iterates over the downloaded Groovy DSLs and registers a route
* for each DSL.
*
* @author Marcin Grzejszczak
*/
@Configuration
@ConditionalOnClass(RoutesBuilder.class)
@ConditionalOnProperty(name = "stubrunner.camel.enabled", havingValue = "true", matchIfMissing = true)
public class StubRunnerCamelConfiguration {

@Bean
public RoutesBuilder myRouter(final BatchStubRunner batchStubRunner) {
return new SpringRouteBuilder() {
@Override
public void configure() throws Exception {
Map<StubConfiguration, Collection<Contract>> contracts = batchStubRunner
.getContracts();
for (Collection<Contract> list : contracts.values()) {
for (Contract it : list) {
if (it.getInput() != null
&& it.getInput().getMessageFrom() != null
&& it.getOutputMessage() != null
&& it.getOutputMessage().getSentTo() != null) {
from(it.getInput().getMessageFrom().getClientValue())
.filter(new StubRunnerCamelPredicate(it))
.process(new StubRunnerCamelProcessor(it))
.to(it.getOutputMessage().getSentTo()
.getClientValue());
}
}
}
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* Copyright 2013-2017 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.camel;

import java.util.Map;
import java.util.regex.Pattern;

import org.apache.camel.Exchange;
import org.apache.camel.Predicate;
import org.springframework.cloud.contract.spec.Contract;
import org.springframework.cloud.contract.spec.internal.BodyMatcher;
import org.springframework.cloud.contract.spec.internal.BodyMatchers;
import org.springframework.cloud.contract.spec.internal.Header;
import org.springframework.cloud.contract.verifier.util.MapConverter;
import org.springframework.cloud.contract.verifier.messaging.internal.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 com.fasterxml.jackson.core.JsonProcessingException;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import com.toomuchcoding.jsonassert.JsonAssertion;

/**
* Passes through a message that matches the one defined in the DSL
*
* @author Marcin Grzejszczak
*/
class StubRunnerCamelPredicate implements Predicate {

private final Contract groovyDsl;
private final ContractVerifierObjectMapper objectMapper = new ContractVerifierObjectMapper();

public StubRunnerCamelPredicate(Contract groovyDsl) {
this.groovyDsl = groovyDsl;
}

@Override
public boolean matches(Exchange exchange) {
if (!headersMatch(exchange.getIn().getHeaders())) {
return false;
}
Object inputMessage = exchange.getIn().getBody();
BodyMatchers matchers = this.groovyDsl.getInput().getBodyMatchers();
Object dslBody = MapConverter.getStubSideValues(this.groovyDsl.getInput().getMessageBody());

DocumentContext parsedJson = deserialize(inputMessage);
return matchMessage(matchers, dslBody, parsedJson);
}

private boolean matchMessage(BodyMatchers matchers, Object dslBody, DocumentContext parsedJson) {
boolean matches = true;
JsonPaths jsonPaths = getJsonPaths(matchers, dslBody);
for (MethodBufferingJsonVerifiable path : jsonPaths) {
matches &= matchesJsonPath(parsedJson, path.jsonPath());
}
if (matchers != null && matchers.hasMatchers()) {
for (BodyMatcher matcher : matchers.jsonPathMatchers()) {
String jsonPath = JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher, dslBody);
matches &= matchesJsonPath(parsedJson, jsonPath);
}
}
return matches;
}

private JsonPaths getJsonPaths(BodyMatchers matchers, Object dslBody) {
Object matchingInputMessage = JsonToJsonPathsConverter
.removeMatchingJsonPaths(dslBody, matchers);
return JsonToJsonPathsConverter
.transformToJsonPathWithStubsSideValuesAndNoArraySizeCheck(
matchingInputMessage);
}

private DocumentContext deserialize(Object inputMessage) {
DocumentContext parsedJson;
try {
parsedJson = JsonPath
.parse(this.objectMapper.writeValueAsString(inputMessage));
}
catch (JsonProcessingException e) {
throw new IllegalStateException("Cannot serialize to JSON", e);
}
return parsedJson;
}

private boolean matchesJsonPath(DocumentContext parsedJson, String jsonPath) {
try {
JsonAssertion.assertThat(parsedJson).matchesJsonPath(jsonPath);
return true;
} catch (Exception e) {
return false;
}
}

private boolean headersMatch(Map<String, Object> headers) {
boolean matches = true;
for (Header it : this.groovyDsl.getInput().getMessageHeaders().getEntries()) {
String name = it.getName();
Object value = it.getClientValue();
Object valueInHeader = headers.get(name);
matches &= matchValue(value, valueInHeader);
}
return matches;
}

private boolean matchValue(Object value, Object valueInHeader) {
return value instanceof Pattern ?
((Pattern) value).matcher(valueInHeader.toString()).matches() :
valueInHeader!=null && valueInHeader.equals(value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2013-2017 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.camel;

import org.springframework.cloud.contract.verifier.util.BodyExtractor;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.Processor;
import org.springframework.cloud.contract.spec.Contract;
import org.springframework.cloud.contract.spec.internal.Header;

/**
* Sends forward a message defined in the DSL. Also removes headers from the input message
* and provides the headers from the DSL.
*
* @author Marcin Grzejszczak
*/
class StubRunnerCamelProcessor implements Processor {

private final Contract groovyDsl;

StubRunnerCamelProcessor(Contract groovyDsl) {
this.groovyDsl = groovyDsl;
}

@Override
public void process(Exchange exchange) throws Exception {
Message input = exchange.getIn();
if (this.groovyDsl.getInput().getMessageHeaders() != null) {
for (Header entry : this.groovyDsl.getInput().getMessageHeaders().getEntries()) {
input.removeHeader(entry.getName());
}
}
if (this.groovyDsl.getOutputMessage() == null) {
return;
}
input.setBody(BodyExtractor
.extractStubValueFrom(this.groovyDsl.getOutputMessage().getBody()));
if (this.groovyDsl.getOutputMessage().getHeaders() != null) {
for (Header entry : this.groovyDsl.getOutputMessage().getHeaders().getEntries()) {
input.setHeader(entry.getName(), entry.getClientValue());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ org.springframework.cloud.contract.stubrunner.messaging.stream.StubRunnerStreamC
org.springframework.cloud.contract.stubrunner.spring.cloud.zookeeper.StubRunnerSpringCloudZookeeperAutoConfiguration,\
org.springframework.cloud.contract.stubrunner.spring.cloud.eureka.StubRunnerSpringCloudEurekaAutoConfiguration,\
org.springframework.cloud.contract.stubrunner.spring.cloud.consul.StubRunnerSpringCloudConsulAutoConfiguration,\
org.springframework.cloud.contract.stubrunner.messaging.StubRunnerStreamsIntegrationAutoConfiguration
org.springframework.cloud.contract.stubrunner.messaging.StubRunnerStreamsIntegrationAutoConfiguration,\
org.springframework.cloud.contract.stubrunner.messaging.camel.StubRunnerCamelConfiguration

# Test Execution Listeners
org.springframework.test.context.TestExecutionListener=\
Expand Down
Loading

0 comments on commit a8aa7a0

Please sign in to comment.