Added Stream integration
This commit is contained in:
@@ -11,6 +11,7 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
compile project(':accurest-messaging-root:accurest-messaging-core')
|
||||
compile 'org.springframework:spring-messaging:[4.0.0.RELEASE,)'
|
||||
compile 'org.springframework.cloud:spring-cloud-stream:[1.0.0.RC2,)'
|
||||
// for MessageCollector
|
||||
compile 'org.springframework.cloud:spring-cloud-stream-test-support:[1.0.0.RC2,)'
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
package io.codearte.accurest.messaging.stream;
|
||||
|
||||
import io.codearte.accurest.messaging.AccurestMessage;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
|
||||
import io.codearte.accurest.messaging.AccurestMessage;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@@ -12,6 +13,9 @@ public class StreamMessage<T> implements AccurestMessage<T, Message<T>> {
|
||||
private final Message<T> delegate;
|
||||
|
||||
public StreamMessage(Message<T> delegate) {
|
||||
if (delegate == null) {
|
||||
throw new IllegalArgumentException("Message can't be null");
|
||||
}
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
repositories {
|
||||
mavenLocal()
|
||||
jcenter()
|
||||
maven {
|
||||
url "http://repo.spring.io/snapshot"
|
||||
}
|
||||
maven {
|
||||
url "http://repo.spring.io/milestone"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':stub-runner-root:stub-runner-spring')
|
||||
compile project(':accurest-messaging-root:accurest-messaging-stream')
|
||||
compile 'org.springframework.integration:spring-integration-java-dsl:[1.1.2.RELEASE,)'
|
||||
|
||||
testCompile 'org.springframework.cloud:spring-cloud-stream-test-support:1.0.0.RC2'
|
||||
testCompile 'org.springframework.boot:spring-boot-starter-test:1.3.3.RELEASE'
|
||||
testCompile('org.spockframework:spock-spring:1.0-groovy-2.4') {
|
||||
exclude(group: 'org.codehaus.groovy')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package io.codearte.accurest.stubrunner.messaging.stream
|
||||
|
||||
import io.codearte.accurest.dsl.GroovyDsl
|
||||
import io.codearte.accurest.stubrunner.BatchStubRunner
|
||||
import io.codearte.accurest.stubrunner.StubConfiguration
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory
|
||||
import org.springframework.cloud.stream.annotation.EnableBinding
|
||||
import org.springframework.cloud.stream.binding.ChannelBindingService
|
||||
import org.springframework.context.Lifecycle
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.integration.dsl.FilterEndpointSpec
|
||||
import org.springframework.integration.dsl.GenericEndpointSpec
|
||||
import org.springframework.integration.dsl.IntegrationFlow
|
||||
import org.springframework.integration.dsl.IntegrationFlows
|
||||
import org.springframework.messaging.MessageChannel
|
||||
import org.springframework.stereotype.Service
|
||||
/**
|
||||
* Spring Cloud Stream configuration that iterates over the downloaded Groovy DSLs
|
||||
* and registers a flow for each DSL.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@Configuration
|
||||
@EnableBinding
|
||||
class StubRunnerStreamConfiguration {
|
||||
|
||||
@Bean
|
||||
FlowRegistrar service(AutowireCapableBeanFactory beanFactory, BatchStubRunner batchStubRunner, ChannelBindingService channelBindingService) {
|
||||
Map<StubConfiguration, Collection<GroovyDsl>> accurestContracts = batchStubRunner.accurestContracts
|
||||
accurestContracts.each { StubConfiguration key, Collection<GroovyDsl> value ->
|
||||
String name = "${key.groupId}_${key.artifactId}"
|
||||
value.findAll { it?.input?.messageFrom && it?.outputMessage?.sentTo }.each { GroovyDsl dsl ->
|
||||
String flowName = "${name}_${dsl.label}_${dsl.hashCode()}"
|
||||
IntegrationFlow integrationFlow = IntegrationFlows.from(dsl.input.messageFrom)
|
||||
.filter(new StubRunnerStreamMessageSelector(dsl), { FilterEndpointSpec e -> e.id("${flowName}.filter") } )
|
||||
.transform(new StubRunnerStreamTransformer(dsl), { GenericEndpointSpec e -> e.id("${flowName}.transformer") })
|
||||
.channel(dsl.outputMessage.sentTo)
|
||||
.get()
|
||||
beanFactory.initializeBean(integrationFlow, flowName)
|
||||
beanFactory.getBean("${flowName}.filter", Lifecycle.class).start();
|
||||
beanFactory.getBean("${flowName}.transformer", Lifecycle.class).start();
|
||||
channelBindingService.bindConsumer(beanFactory.getBean(dsl.input.messageFrom, MessageChannel.class), dsl.input.messageFrom)
|
||||
channelBindingService.bindProducer(beanFactory.getBean(dsl.outputMessage.sentTo, MessageChannel.class), dsl.outputMessage.sentTo)
|
||||
}
|
||||
}
|
||||
return new FlowRegistrar()
|
||||
}
|
||||
|
||||
@Service
|
||||
static class FlowRegistrar {}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package io.codearte.accurest.stubrunner.messaging.stream
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
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 io.codearte.accurest.dsl.GroovyDsl
|
||||
import io.codearte.accurest.util.JsonPaths
|
||||
import io.codearte.accurest.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 StubRunnerStreamMessageSelector implements MessageSelector {
|
||||
|
||||
private final GroovyDsl groovyDsl
|
||||
private final ObjectMapper objectMapper = new ObjectMapper()
|
||||
|
||||
StubRunnerStreamMessageSelector(GroovyDsl 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package io.codearte.accurest.stubrunner.messaging.stream
|
||||
|
||||
import io.codearte.accurest.builder.BodyAsString
|
||||
import io.codearte.accurest.dsl.GroovyDsl
|
||||
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 StubRunnerStreamTransformer implements GenericTransformer<Message<?>, Message<?>> {
|
||||
|
||||
private final GroovyDsl groovyDsl
|
||||
|
||||
StubRunnerStreamTransformer(GroovyDsl groovyDsl) {
|
||||
this.groovyDsl = groovyDsl
|
||||
}
|
||||
|
||||
@Override
|
||||
Message<?> transform(Message<?> source) {
|
||||
String payload = BodyAsString.extractClientValueFrom(groovyDsl.outputMessage.body)
|
||||
Map<String, Object> headers = groovyDsl.outputMessage.headers.asStubSideMap()
|
||||
return MessageBuilder.createMessage(payload, new MessageHeaders(headers))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# Auto Configuration
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
io.codearte.accurest.stubrunner.messaging.stream.StubRunnerStreamConfiguration
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.codearte.accurest.stubrunner.messaging.stream
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
|
||||
@CompileStatic
|
||||
@EqualsAndHashCode
|
||||
class BookReturned implements Serializable {
|
||||
final String bookName
|
||||
|
||||
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
|
||||
BookReturned(String bookName) {
|
||||
this.bookName = bookName
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package io.codearte.accurest.stubrunner.messaging.stream
|
||||
|
||||
import groovy.json.JsonOutput
|
||||
import groovy.json.JsonSlurper
|
||||
import io.codearte.accurest.messaging.AccurestMessage
|
||||
import io.codearte.accurest.messaging.AccurestMessaging
|
||||
import io.codearte.accurest.stubrunner.StubFinder
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
import org.springframework.boot.test.SpringApplicationContextLoader
|
||||
import org.springframework.context.annotation.ComponentScan
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.test.context.ContextConfiguration
|
||||
import spock.lang.Specification
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan
|
||||
@EnableAutoConfiguration
|
||||
@ContextConfiguration(classes = StreamStubRunnerSpec, loader = SpringApplicationContextLoader)
|
||||
class StreamStubRunnerSpec extends Specification {
|
||||
|
||||
@Autowired StubFinder stubFinder
|
||||
@Autowired AccurestMessaging messaging
|
||||
|
||||
def 'should download the stub and register a route for it'() {
|
||||
when:
|
||||
messaging.send(new BookReturned('foo'), [sample: 'header'], 'input')
|
||||
then:
|
||||
AccurestMessage receivedMessage = messaging.receiveMessage('output')
|
||||
and:
|
||||
receivedMessage != null
|
||||
assertJsons(receivedMessage.payload)
|
||||
receivedMessage.headers.get('BOOK-NAME') == 'foo'
|
||||
}
|
||||
|
||||
def 'should trigger a message by label'() {
|
||||
when:
|
||||
stubFinder.trigger('return_book_1')
|
||||
then:
|
||||
AccurestMessage receivedMessage = messaging.receiveMessage('output')
|
||||
and:
|
||||
receivedMessage != null
|
||||
assertJsons(receivedMessage.payload)
|
||||
receivedMessage.headers.get('BOOK-NAME') == 'foo'
|
||||
}
|
||||
|
||||
def 'should trigger a label for the existing groupId:artifactId'() {
|
||||
when:
|
||||
stubFinder.trigger('io.codearte.accurest.stubs:streamService', 'return_book_1')
|
||||
then:
|
||||
AccurestMessage receivedMessage = messaging.receiveMessage('output')
|
||||
and:
|
||||
receivedMessage != null
|
||||
assertJsons(receivedMessage.payload)
|
||||
receivedMessage.headers.get('BOOK-NAME') == 'foo'
|
||||
}
|
||||
|
||||
def 'should trigger a label for the existing artifactId'() {
|
||||
when:
|
||||
stubFinder.trigger('streamService', 'return_book_1')
|
||||
then:
|
||||
AccurestMessage receivedMessage = messaging.receiveMessage('output')
|
||||
and:
|
||||
receivedMessage != null
|
||||
assertJsons(receivedMessage.payload)
|
||||
receivedMessage.headers.get('BOOK-NAME') == 'foo'
|
||||
}
|
||||
|
||||
def 'should not run any wrong trigger when missing label is passed'() {
|
||||
given:
|
||||
stubFinder.trigger('missing label')
|
||||
when:
|
||||
messaging.receiveMessage('output', 100, TimeUnit.MILLISECONDS)
|
||||
then:
|
||||
RuntimeException e = thrown(RuntimeException)
|
||||
e.cause.message.contains("Message can't be null")
|
||||
}
|
||||
|
||||
def 'should not run any wrong trigger when missing label and artifactid is passed'() {
|
||||
given:
|
||||
stubFinder.trigger('some:service', 'return_book_1')
|
||||
when:
|
||||
messaging.receiveMessage('output', 100, TimeUnit.MILLISECONDS)
|
||||
then:
|
||||
RuntimeException e = thrown(RuntimeException)
|
||||
e.cause.message.contains("Message can't be null")
|
||||
}
|
||||
|
||||
def 'should trigger messages by running all triggers'() {
|
||||
when:
|
||||
stubFinder.trigger()
|
||||
then:
|
||||
AccurestMessage receivedMessage = messaging.receiveMessage('output')
|
||||
and:
|
||||
receivedMessage != null
|
||||
assertJsons(receivedMessage.payload)
|
||||
receivedMessage.headers.get('BOOK-NAME') == 'foo'
|
||||
}
|
||||
|
||||
private boolean assertJsons(Object payload) {
|
||||
String objectAsString = payload instanceof String ? payload :
|
||||
JsonOutput.toJson(payload)
|
||||
def json = new JsonSlurper().parseText(objectAsString)
|
||||
return json.bookName == 'foo'
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
stubrunner.stubs.repository.root: classpath:m2repo/repository/
|
||||
stubrunner.stubs.ids: io.codearte.accurest.stubs:streamService
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<metadata>
|
||||
<groupId>io.codearte.accurest.stubs</groupId>
|
||||
<artifactId>streamService</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<versioning>
|
||||
<snapshot>
|
||||
<localCopy>true</localCopy>
|
||||
</snapshot>
|
||||
<lastUpdated>20160409062112</lastUpdated>
|
||||
</versioning>
|
||||
</metadata>
|
||||
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>io.codearte.accurest.stubs</groupId>
|
||||
<artifactId>streamService</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
</project>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<metadata>
|
||||
<groupId>io.codearte.accurest.stubs</groupId>
|
||||
<artifactId>streamService</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<versioning>
|
||||
<versions>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
</versions>
|
||||
<lastUpdated>20160409062112</lastUpdated>
|
||||
</versioning>
|
||||
</metadata>
|
||||
Reference in New Issue
Block a user