Fixed README generation and polished few tests

This commit is contained in:
Oleg Zhurakousky
2019-08-19 17:03:40 +02:00
parent 946365dcdc
commit 591b3bf531
4 changed files with 61 additions and 14 deletions

View File

@@ -45,9 +45,12 @@ https://www.reactive-streams.org/[Reactive Streams] `Publisher` from
https://projectreactor.io/[Project Reactor]. The function can be
accessed over HTTP or messaging.
*Features*
Spring Cloud Function has 4 main features:
In the nutshell Spring Cloud Function provides the following features:
1. Wrappers for `@Beans` of type `Function`, `Consumer` and
`Supplier`, exposing them to the outside world as either HTTP
endpoints and/or message stream listeners/publishers with RabbitMQ, Kafka etc.
* _Choice of programming styles - reactive, imperative or hybrid._
* _Function composition and adaptation (e.g., composing imperative functions with reactive)._
@@ -391,4 +394,4 @@ Go to `File` -> `Settings` -> `Other settings` -> `Checkstyle`. There click on t
- `checkstyle.suppressions.file` - default suppressions. Please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` URL.
- `checkstyle.additional.suppressions.file` - this variable corresponds to suppressions in your local project. E.g. you're working on `spring-cloud-contract`. Then point to the `project-root/src/checkstyle/checkstyle-suppressions.xml` folder. Example for `spring-cloud-contract` would be: `/home/username/spring-cloud-contract/src/checkstyle/checkstyle-suppressions.xml`.
IMPORTANT: Remember to set the `Scan Scope` to `All sources` since we apply checkstyle rules for production and test sources.
IMPORTANT: Remember to set the `Scan Scope` to `All sources` since we apply checkstyle rules for production and test sources.

View File

@@ -7,7 +7,7 @@ Spring Cloud Function is a project with the following high-level goals:
It abstracts away all of the transport details and
infrastructure, allowing the developer to keep all the familiar tools
and processes, and focus firmly on business logic.
and processes, and focus firmly on business logic.
Here's a complete, executable, testable Spring Boot application
(implementing a simple string manipulation):
@@ -37,15 +37,17 @@ accessed over HTTP or messaging.
Spring Cloud Function has 4 main features:
In the nutshell Spring Cloud Function provides the following features:
1. Wrappers for `@Beans` of type `Function`, `Consumer` and
`Supplier`, exposing them to the outside world as either HTTP
endpoints and/or message stream listeners/publishers with RabbitMQ, Kafka etc.
2. Compiling strings which are Java function bodies into bytecode, and
then turning them into `@Beans` that can be wrapped as above.
3. Deploying a JAR file containing such an application context with an
isolated classloader, so that you can pack them together in a single
JVM.
4. Adapters for https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-aws[AWS Lambda], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-azure[Azure], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk[Apache OpenWhisk] and possibly other "serverless" service providers.
* _Choice of programming styles - reactive, imperative or hybrid._
* _Function composition and adaptation (e.g., composing imperative functions with reactive)._
* _Support for reactive function with multiple inputs and outputs allowing merging, joining and other complex streaming operation to be handled by functions._
* _Transparent type conversion of inputs and outputs._
* _Packaging functions for deployments, specific to the target platform (e.g., Project Riff, AWS Lambda and more)_
* _Adapters to expose function to the outside world as HTTP endpoints etc._
* _Deploying a JAR file containing such an application context with an isolated classloader, so that you can pack them together in a single JVM._
* _Compiling strings which are Java function bodies into bytecode, and then turning them into `@Beans` that can be wrapped as above._
* _Adapters for https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-aws[AWS Lambda], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-azure[Azure], https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk[Apache OpenWhisk] and possibly other "serverless" service providers._

View File

@@ -430,6 +430,7 @@ public class BeanFactoryAwareFunctionRegistry
}
}
// Outputs will be converted only if we're told how (via acceptedOutputMimeTypes), otherwise output returned as is.
if (!ObjectUtils.isEmpty(this.acceptedOutputMimeTypes)) {
result = result instanceof Publisher
? this.convertOutputPublisherIfNecessary((Publisher<?>) result, this.acceptedOutputMimeTypes)
@@ -462,7 +463,7 @@ public class BeanFactoryAwareFunctionRegistry
}
convertedValue = Tuples.fromArray(convertedInputArray);
}
else {
else if (value != null) {
List<MimeType> acceptedContentTypes = MimeTypeUtils.parseMimeTypes(acceptedOutputMimeTypes[0].toString());
convertedValue = acceptedContentTypes.stream()

View File

@@ -40,6 +40,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.messaging.support.MessageBuilder;
import static org.assertj.core.api.Assertions.assertThat;
@@ -78,6 +79,28 @@ public class BeanFactoryAwareFunctionRegistryTests {
assertThat(messageResult.get(1).getPayload()).isEqualTo("\"UPPERCASEFLUX2\"".getBytes(StandardCharsets.UTF_8));
}
@Test
public void testConsumerFunction() { // function that returns Void, effectively a Consumer
FunctionCatalog catalog = this.configureCatalog();
Function<String, Void> consumerFunction = catalog.lookup("consumerFunction");
assertThat(consumerFunction.apply("hello")).isNull();
Function<Message<byte[]>, Void> consumerFunctionAsMessageA = catalog.lookup("consumerFunction");
assertThat(consumerFunctionAsMessageA.apply(new GenericMessage<byte[]>("\"hello\"".getBytes()))).isNull();
Function<Message<byte[]>, Void> consumerFunctionAsMessageB = catalog.lookup("consumerFunction", "application/json");
assertThat(consumerFunctionAsMessageB.apply(new GenericMessage<byte[]>("\"hello\"".getBytes()))).isNull();
}
@Test
public void testMessageToPojoConversion() {
FunctionCatalog catalog = this.configureCatalog();
Function<Message<String>, Person> uppercasePerson = catalog.lookup("uppercasePerson");
Person person = uppercasePerson.apply(MessageBuilder.withPayload("{\"name\":\"bill\",\"id\":2}").build());
assertThat(person.getName()).isEqualTo("BILL");
}
/*
* When invoking imperative function as reactive the rules are
* - the input wrapper must match the output wrapper (e.g., <Flux, Flux> or <Mono, Mono>)
@@ -249,6 +272,13 @@ public class BeanFactoryAwareFunctionRegistryTests {
@Configuration
protected static class SampleFunctionConfiguration {
@Bean
public Function<Person, Person> uppercasePerson() {
return person -> {
return new Person(person.getName().toUpperCase(), person.getId());
};
}
@Bean
public Supplier<String> numberword() {
return () -> "one";
@@ -267,6 +297,14 @@ public class BeanFactoryAwareFunctionRegistryTests {
return v -> v.toUpperCase();
}
@Bean
public Function<String, Void> consumerFunction() {
return v -> {
System.out.println("Value: " + v);
return null;
};
}
@Bean
public Function<Flux<String>, Flux<String>> uppercaseFlux() {
return flux -> flux.map(v -> v.toUpperCase());
@@ -392,10 +430,13 @@ public class BeanFactoryAwareFunctionRegistryTests {
}
}
private static class Person {
public static class Person {
private String name;
private int id;
Person(String name, int id) {
public Person() {
}
public Person(String name, int id) {
this.name = name;
this.id = id;
}