Faster JSON building + separate auto-gen jar + tests. (#55)

This commit is contained in:
Artur Souza 2019-12-19 12:58:58 -08:00 committed by GitHub
parent 14f2304c8c
commit 9a10a960e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 563 additions and 186 deletions

View File

@ -7,12 +7,12 @@
<parent>
<groupId>io.dapr</groupId>
<artifactId>dapr-sdk-parent</artifactId>
<version>0.3.0-preview01</version>
<version>0.2.0-preview01</version>
</parent>
<artifactId>dapr-sdk-examples</artifactId>
<packaging>jar</packaging>
<version>0.3.0-preview01</version>
<version>0.2.0-preview01</version>
<name>dapr-sdk-examples</name>
<properties>
@ -62,7 +62,7 @@
<dependency>
<groupId>io.dapr</groupId>
<artifactId>dapr-sdk</artifactId>
<version>0.3.0-preview01</version>
<version>0.2.0-preview01</version>
</dependency>
</dependencies>

View File

@ -7,7 +7,7 @@
<groupId>io.dapr</groupId>
<artifactId>dapr-sdk-parent</artifactId>
<packaging>pom</packaging>
<version>0.3.0-preview01</version>
<version>0.2.0-preview01</version>
<name>dapr-sdk-parent</name>
<description>SDK for Dapr.</description>
<url>https://dapr.io</url>
@ -77,6 +77,7 @@
</scm>
<modules>
<module>sdk-autogen</module>
<module>sdk</module>
<module>examples</module>
</modules>

113
sdk-autogen/pom.xml Normal file
View File

@ -0,0 +1,113 @@
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.dapr</groupId>
<artifactId>dapr-sdk-parent</artifactId>
<version>0.2.0-preview01</version>
</parent>
<artifactId>dapr-sdk-autogen</artifactId>
<packaging>jar</packaging>
<version>0.2.0-preview01</version>
<name>dapr-sdk-autogen</name>
<description>Auto-generated SDK for Dapr</description>
<properties>
<protobuf.output.directory>${project.build.directory}/generated-sources</protobuf.output.directory>
<protobuf.input.directory>${project.parent.basedir}/proto</protobuf.input.directory>
</properties>
<dependencies>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-testing</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>${protobuf.version}</version>
</dependency>
<dependency>
<groupId>com.github.os72</groupId>
<artifactId>protoc-jar-maven-plugin</artifactId>
<version>3.10.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.github.os72</groupId>
<artifactId>protoc-jar-maven-plugin</artifactId>
<version>3.10.1</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<protocVersion>${protobuf.version}</protocVersion>
<addProtoSources>inputs</addProtoSources>
<includeMavenTypes>direct</includeMavenTypes>
<includeStdTypes>true</includeStdTypes>
<inputDirectories>
<include>${protobuf.input.directory}/dapr</include>
<include>${protobuf.input.directory}/daprclient</include>
</inputDirectories>
<outputTargets>
<outputTarget>
<type>java</type>
<outputDirectory>${protobuf.output.directory}</outputDirectory>
</outputTarget>
<outputTarget>
<type>grpc-java</type>
<outputDirectory>${protobuf.output.directory}</outputDirectory>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}</pluginArtifact>
</outputTarget>
</outputTargets>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

View File

View File

@ -7,21 +7,21 @@
<parent>
<groupId>io.dapr</groupId>
<artifactId>dapr-sdk-parent</artifactId>
<version>0.3.0-preview01</version>
<version>0.2.0-preview01</version>
</parent>
<artifactId>dapr-sdk</artifactId>
<packaging>jar</packaging>
<version>0.3.0-preview01</version>
<version>0.2.0-preview01</version>
<name>dapr-sdk</name>
<description>SDK for Dapr</description>
<properties>
<protobuf.output.directory>${project.build.directory}/generated-sources</protobuf.output.directory>
<protobuf.input.directory>${project.parent.basedir}/proto</protobuf.input.directory>
</properties>
<dependencies>
<dependency>
<groupId>io.dapr</groupId>
<artifactId>dapr-sdk-autogen</artifactId>
<version>0.2.0-preview01</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
@ -37,34 +37,11 @@
<artifactId>okhttp</artifactId>
<version>4.2.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-testing</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>${protobuf.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
@ -75,50 +52,10 @@
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.os72</groupId>
<artifactId>protoc-jar-maven-plugin</artifactId>
<version>3.10.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.github.os72</groupId>
<artifactId>protoc-jar-maven-plugin</artifactId>
<version>3.10.1</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<protocVersion>${protobuf.version}</protocVersion>
<addProtoSources>inputs</addProtoSources>
<includeMavenTypes>direct</includeMavenTypes>
<includeStdTypes>true</includeStdTypes>
<inputDirectories>
<include>${protobuf.input.directory}/dapr</include>
<include>${protobuf.input.directory}/daprclient</include>
</inputDirectories>
<outputTargets>
<outputTarget>
<type>java</type>
<outputDirectory>${protobuf.output.directory}</outputDirectory>
</outputTarget>
<outputTarget>
<type>grpc-java</type>
<outputDirectory>${protobuf.output.directory}</outputDirectory>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}</pluginArtifact>
</outputTarget>
</outputTargets>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>

View File

@ -152,20 +152,20 @@ public abstract class AbstractDaprClient {
public interface DaprHttpCallback {
/**
* called when the server response was not 2xx or when an exception was
* Called when the server response was not 2xx or when an exception was
* thrown in the process
*
* @param response - in case of server error (4xx, 5xx) this contains the
* @param call - in case of server error (4xx, 5xx) this contains the
* server response in case of IO exception this is null
* @param throwable - contains the exception. in case of server error (4xx,
* @param e - contains the exception. in case of server error (4xx,
* 5xx) this is null
*/
public void onFailure(Call call, Exception e);
/**
* contains the server response
* Contains the server response
*
* @param response
* @param response Success response.
*/
public void onSuccess(String response);
}

View File

@ -78,6 +78,7 @@ public class ActorRuntime {
* Registers an actor with the runtime.
*
* @param clazz The type of actor.
* @param <T> Actor class type.
*/
public <T extends AbstractActor> void RegisterActor(Class<T> clazz) {
RegisterActor(clazz, null);
@ -88,6 +89,7 @@ public class ActorRuntime {
*
* @param clazz The type of actor.
* @param actorFactory An optional factory to create actors.
* @param <T> Actor class type.
* This can be used for dependency injection into actors.
*/
public <T extends AbstractActor> void RegisterActor(Class<T> clazz, ActorFactory actorFactory) {
@ -95,7 +97,7 @@ public class ActorRuntime {
ActorFactory actualActorFactory = actorFactory != null ? actorFactory : new DefaultActorFactory<T>(actorTypeInfo);
// TODO: Refactor into a Builder class.
DaprStateAsyncProvider stateProvider = new DaprStateAsyncProvider(this.appToDaprAsyncClient, new ActorStateProviderSerializer());
DaprStateAsyncProvider stateProvider = new DaprStateAsyncProvider(this.appToDaprAsyncClient, new ActorStateSerializer());
ActorService actorService = new ActorServiceImpl(actorTypeInfo, stateProvider, actualActorFactory);
// Create ActorManagers, override existing entry if registered again.

View File

@ -5,8 +5,6 @@
package io.dapr.actors.runtime;
import java.io.IOException;
/**
* Represents a state change for an actor.
* @param <T> Type of the value being changed.

View File

@ -1,44 +0,0 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/
package io.dapr.actors.runtime;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
/**
* Serializes and deserializes an object.
*/
class ActorStateProviderSerializer {
/**
* Shared Json serializer/deserializer as per Jackson's documentation.
*/
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
/**
* Serializes a given state object into byte array.
*
* @param state State object to be serialized.
* @return Array of bytes[] with the serialized content.
* @throws IOException
*/
String serialize(Object state) throws IOException {
return OBJECT_MAPPER.writeValueAsString(state);
}
/**
* Deserializes the byte array into the original object.
*
* @param json String to be parsed.
* @param clazz Type of the object being deserialized.
* @param <T> Generic type of the object being deserialized.
* @return Object of type T.
* @throws IOException
*/
<T> T deserialize(String json, Class<T> clazz) throws IOException {
return OBJECT_MAPPER.readValue(json, clazz);
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/
package io.dapr.actors.runtime;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
/**
* Serializes and deserializes an object.
*/
class ActorStateSerializer {
/**
* Shared Json serializer/deserializer as per Jackson's documentation.
*/
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
/**
* Serializes a given state object into byte array.
*
* @param state State object to be serialized.
* @return Array of bytes[] with the serialized content.
* @throws IOException
*/
<T> String serialize(T state) throws IOException {
if (state == null) {
return null;
}
if (state.getClass() == String.class) {
return state.toString();
}
if (isPrimitive(state.getClass())) {
return state.toString();
}
// Not string, not primitive, so it is a complex type: we use JSON for that.
return OBJECT_MAPPER.writeValueAsString(state);
}
/**
* Deserializes the byte array into the original object.
*
* @param value String to be parsed.
* @param clazz Type of the object being deserialized.
* @param <T> Generic type of the object being deserialized.
* @return Object of type T.
* @throws IOException
*/
<T> T deserialize(String value, Class<T> clazz) throws IOException {
if (clazz == String.class) {
return (T) value;
}
if (isPrimitive(clazz)) {
return parse(value, clazz);
}
// Not string, not primitive, so it is a complex type: we use JSON for that.
return OBJECT_MAPPER.readValue(value, clazz);
}
private static boolean isPrimitive(Class<?> clazz) {
if (clazz == null) {
return false;
}
return (clazz.isPrimitive() ||
(clazz == Boolean.class) ||
(clazz == Character.class) ||
(clazz == Byte.class) ||
(clazz == Short.class) ||
(clazz == Integer.class) ||
(clazz == Long.class) ||
(clazz == Float.class) ||
(clazz == Double.class) ||
(clazz == Void.class));
}
private static <T> T parse(String value, Class<T> clazz) {
if (value == null) {
return null;
}
if ((Boolean.class == clazz) || (boolean.class == clazz)) return (T) Boolean.valueOf(value);
if ((Byte.class == clazz) || (byte.class == clazz)) return (T) Byte.valueOf(value);
if ((Short.class == clazz) || (short.class == clazz)) return (T) Short.valueOf(value);
if ((Integer.class == clazz) || (int.class == clazz)) return (T) Integer.valueOf(value);
if ((Long.class == clazz) || (long.class == clazz)) return (T) Long.valueOf(value);
if ((Float.class == clazz) || (float.class == clazz)) return (T) Float.valueOf(value);
if ((Double.class == clazz) || (double.class == clazz)) return (T) Double.valueOf(value);
return null;
}
}

View File

@ -1,10 +1,14 @@
package io.dapr.actors.runtime;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.time.Duration;
import java.util.function.Function;
@ -14,9 +18,9 @@ import java.util.function.Function;
class ActorTimerImpl implements ActorTimer {
/**
* Shared Json serializer/deserializer as per Jackson's documentation, used only for this class.
* Shared Json Factory as per Jackson's documentation, used only for this class.
*/
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final JsonFactory JSON_FACTORY = new JsonFactory();
/**
* Actor that owns this timer.
@ -118,9 +122,15 @@ class ActorTimerImpl implements ActorTimer {
* @return JSON.
*/
String serialize() throws IOException {
ObjectNode objectNode = OBJECT_MAPPER.createObjectNode();
objectNode.put("dueTime", ConverterUtils.ConvertDurationToDaprFormat(this.dueTime));
objectNode.put("period", ConverterUtils.ConvertDurationToDaprFormat(this.period));
return OBJECT_MAPPER.writeValueAsString(objectNode);
try (Writer writer = new StringWriter()) {
JsonGenerator generator = JSON_FACTORY.createGenerator(writer);
generator.writeStartObject();
generator.writeStringField("dueTime", ConverterUtils.ConvertDurationToDaprFormat(this.dueTime));
generator.writeStringField("period", ConverterUtils.ConvertDurationToDaprFormat(this.period));
generator.writeEndObject();
generator.close();
writer.flush();
return writer.toString();
}
}
}

View File

@ -5,14 +5,13 @@
package io.dapr.actors.runtime;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import reactor.core.publisher.Mono;
import java.io.IOException;
import java.util.Collection;
import java.io.StringWriter;
import java.io.Writer;
/**
* State Provider to interact with Dapr runtime to handle state.
@ -20,15 +19,15 @@ import java.util.Collection;
class DaprStateAsyncProvider {
/**
* Shared Json serializer/deserializer as per Jackson's documentation, used only for this class.
* Shared Json Factory as per Jackson's documentation, used only for this class.
*/
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final JsonFactory JSON_FACTORY = new JsonFactory();
private final AppToDaprAsyncClient daprAsyncClient;
private final ActorStateProviderSerializer serializer;
private final ActorStateSerializer serializer;
DaprStateAsyncProvider(AppToDaprAsyncClient daprAsyncClient, ActorStateProviderSerializer serializer) {
DaprStateAsyncProvider(AppToDaprAsyncClient daprAsyncClient, ActorStateSerializer serializer) {
this.daprAsyncClient = daprAsyncClient;
this.serializer = serializer;
}
@ -36,11 +35,9 @@ class DaprStateAsyncProvider {
<T> Mono<T> load(String actorType, String actorId, String stateName, Class<T> clazz) {
Mono<String> result = this.daprAsyncClient.getState(actorType, actorId, stateName);
return result.map(s -> {
if (s == null) {
return (T)null;
}
return result
.filter(s -> (s != null) && (!s.isEmpty()))
.map(s -> {
try {
return this.serializer.deserialize(s, clazz);
} catch (IOException e) {
@ -79,14 +76,20 @@ class DaprStateAsyncProvider {
* @param stateChanges Collection of changes to be performed transactionally.
* @return Void.
*/
Mono<Void> apply(String actorType, String actorId, Collection<ActorStateChange> stateChanges)
Mono<Void> apply(String actorType, String actorId, ActorStateChange... stateChanges)
{
if ((stateChanges == null) || stateChanges.isEmpty()) {
return Mono.just(null);
if ((stateChanges == null) || stateChanges.length == 0) {
return Mono.empty();
}
// Constructing the JSON "manually" to avoid creating transient classes to be parsed.
ArrayNode operations = OBJECT_MAPPER.createArrayNode();
int count = 0;
// Constructing the JSON via a stream API to avoid creating transient objects to be instantiated.
String payload = null;
try (Writer writer = new StringWriter()) {
JsonGenerator generator = JSON_FACTORY.createGenerator(writer);
// Start array
generator.writeStartArray();
for (ActorStateChange stateChange : stateChanges) {
if ((stateChange == null) || (stateChange.getChangeKind() == null)) {
continue;
@ -97,32 +100,41 @@ class DaprStateAsyncProvider {
continue;
}
try {
ObjectNode operation = OBJECT_MAPPER.createObjectNode();
operation.set("operation", operation.textNode(operationName));
ObjectNode request = OBJECT_MAPPER.createObjectNode();
request.put("key", stateChange.getStateName());
count++;
// Start operation object.
generator.writeStartObject();
generator.writeStringField("operation", operationName);
// Start request object.
generator.writeObjectFieldStart("request");
generator.writeStringField("key", stateChange.getStateName());
if ((stateChange.getChangeKind() == ActorStateChangeKind.UPDATE) || (stateChange.getChangeKind() == ActorStateChangeKind.ADD)) {
request.put("value", this.serializer.serialize(stateChange.getValue()));
generator.writeStringField("value", this.serializer.serialize(stateChange.getValue()));
}
// End request object.
generator.writeEndObject();
// End operation object.
generator.writeEndObject();
}
operations.add(operation);
// End array
generator.writeEndArray();
generator.close();
writer.flush();
payload = writer.toString();
} catch (IOException e) {
e.printStackTrace();
return Mono.error(e);
}
}
if (operations.size() == 0) {
if (count == 0) {
// No-op since there is no operation to be performed.
Mono.just(null);
Mono.empty();
}
try {
return this.daprAsyncClient.saveStateTransactionally(actorType, actorId, OBJECT_MAPPER.writeValueAsString(operations));
} catch (JsonProcessingException e) {
e.printStackTrace();
return Mono.error(e);
}
return this.daprAsyncClient.saveStateTransactionally(actorType, actorId, payload);
}
}

View File

@ -0,0 +1,248 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/
package io.dapr.actors.runtime;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Assert;
import org.junit.Test;
import reactor.core.publisher.Mono;
import java.io.IOException;
import java.util.Objects;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.*;
/**
* Tests for the state store facade.
*/
public class DaprStateAsyncProviderTest {
private static final ActorStateSerializer SERIALIZER = new ActorStateSerializer();
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final double EPSILON = 1e-10;
/**
* Class used to test JSON serialization.
*/
public static final class Customer {
private int id;
private String name;
public int getId() {
return id;
}
public Customer setId(int id) {
this.id = id;
return this;
}
public String getName() {
return name;
}
public Customer setName(String name) {
this.name = name;
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Customer customer = (Customer) o;
return id == customer.id &&
Objects.equals(name, customer.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
@Test
public void happyCaseApply() {
AppToDaprAsyncClient daprAsyncClient = mock(AppToDaprAsyncClient.class);
when(daprAsyncClient
.saveStateTransactionally(
eq("MyActor"),
eq("123"),
argThat(s -> {
try {
JsonNode node = OBJECT_MAPPER.readTree(s);
if (node == null) {
return false;
}
if (node.size() != 3) {
return false;
}
boolean foundInsertName = false;
boolean foundUpdateZipcode = false;
boolean foundDeleteFlag = false;
for (JsonNode operation : node) {
if (operation.get("operation") == null) {
return false;
}
if (operation.get("request") == null) {
return false;
}
String opName = operation.get("operation").asText();
String key = operation.get("request").get("key").asText();
JsonNode valueNode = operation.get("request").get("value");
foundInsertName |= "upsert".equals(opName) &&
"name".equals(key) &&
"Jon Doe".equals(valueNode.asText());
foundUpdateZipcode |= "upsert".equals(opName) &&
"zipcode".equals(key) &&
"98011".equals(valueNode.asText());
foundDeleteFlag |= "delete".equals(opName) &&
"flag".equals(key) &&
(valueNode == null);
}
return foundInsertName && foundUpdateZipcode && foundDeleteFlag;
} catch (IOException e) {
e.printStackTrace();
return false;
}
})))
.thenReturn(Mono.empty());
DaprStateAsyncProvider provider = new DaprStateAsyncProvider(daprAsyncClient, SERIALIZER);
provider.apply("MyActor",
"123",
createInsertChange("name", "Jon Doe"),
createUpdateChange("zipcode", "98011"),
createDeleteChange("flag"))
.block();
verify(daprAsyncClient).saveStateTransactionally(eq("MyActor"), eq("123"), any());
}
@Test
public void happyCaseLoad() {
AppToDaprAsyncClient daprAsyncClient = mock(AppToDaprAsyncClient.class);
when(daprAsyncClient
.getState(any(), any(), eq("name")))
.thenReturn(Mono.just("Jon Doe"));
when(daprAsyncClient
.getState(any(), any(), eq("zipcode")))
.thenReturn(Mono.just("98021"));
when(daprAsyncClient
.getState(any(), any(), eq("goals")))
.thenReturn(Mono.just("98"));
when(daprAsyncClient
.getState(any(), any(), eq("balance")))
.thenReturn(Mono.just("46.55"));
when(daprAsyncClient
.getState(any(), any(), eq("active")))
.thenReturn(Mono.just("true"));
when(daprAsyncClient
.getState(any(), any(), eq("customer")))
.thenReturn(Mono.just("{ \"id\": 1000, \"name\": \"Roxane\"}"));
when(daprAsyncClient
.getState(any(), any(), eq("anotherCustomer")))
.thenReturn(Mono.just("{ \"id\": 2000, \"name\": \"Max\"}"));
when(daprAsyncClient
.getState(any(), any(), eq("nullCustomer")))
.thenReturn(Mono.just(""));
DaprStateAsyncProvider provider = new DaprStateAsyncProvider(daprAsyncClient, SERIALIZER);
Assert.assertEquals("Jon Doe",
provider.load("MyActor", "123", "name", String.class).block());
Assert.assertEquals("98021",
provider.load("MyActor", "123", "zipcode", String.class).block());
Assert.assertEquals(98,
(int) provider.load("MyActor", "123", "goals", int.class).block());
Assert.assertEquals(98,
(int) provider.load("MyActor", "123", "goals", int.class).block());
Assert.assertEquals(46.55,
(double) provider.load("MyActor", "123", "balance", double.class).block(),
EPSILON);
Assert.assertEquals(true,
(boolean) provider.load("MyActor", "123", "active", boolean.class).block());
Assert.assertEquals(new Customer().setId(1000).setName("Roxane"),
provider.load("MyActor", "123", "customer", Customer.class).block());
Assert.assertNotEquals(new Customer().setId(1000).setName("Roxane"),
provider.load("MyActor", "123", "anotherCustomer", Customer.class).block());
Assert.assertNull(provider.load("MyActor", "123", "nullCustomer", Customer.class).block());
}
@Test
public void happyCaseContains() {
AppToDaprAsyncClient daprAsyncClient = mock(AppToDaprAsyncClient.class);
// Keys that exists.
when(daprAsyncClient
.getState(any(), any(), eq("name")))
.thenReturn(Mono.just("Jon Doe"));
when(daprAsyncClient
.getState(any(), any(), eq("zipcode")))
.thenReturn(Mono.just("98021"));
when(daprAsyncClient
.getState(any(), any(), eq("goals")))
.thenReturn(Mono.just("98"));
when(daprAsyncClient
.getState(any(), any(), eq("balance")))
.thenReturn(Mono.just("46.55"));
when(daprAsyncClient
.getState(any(), any(), eq("active")))
.thenReturn(Mono.just("true"));
when(daprAsyncClient
.getState(any(), any(), eq("customer")))
.thenReturn(Mono.just("{ \"id\": \"3000\", \"name\": \"Ely\" }"));
// Keys that do not exist.
when(daprAsyncClient
.getState(any(), any(), eq("Does not exist")))
.thenReturn(Mono.just(""));
when(daprAsyncClient
.getState(any(), any(), eq("NAME")))
.thenReturn(Mono.just(""));
when(daprAsyncClient
.getState(any(), any(), eq(null)))
.thenReturn(Mono.just(""));
DaprStateAsyncProvider provider = new DaprStateAsyncProvider(daprAsyncClient, SERIALIZER);
Assert.assertTrue(provider.contains("MyActor", "123", "name").block());
Assert.assertFalse(provider.contains("MyActor", "123", "NAME").block());
Assert.assertTrue(provider.contains("MyActor", "123", "zipcode").block());
Assert.assertTrue(provider.contains("MyActor", "123", "goals").block());
Assert.assertTrue(provider.contains("MyActor", "123", "balance").block());
Assert.assertTrue(provider.contains("MyActor", "123", "active").block());
Assert.assertTrue(provider.contains("MyActor", "123", "customer").block());
Assert.assertFalse(provider.contains("MyActor", "123", "Does not exist").block());
Assert.assertFalse(provider.contains("MyActor", "123", null).block());
}
private final <T> ActorStateChange<T> createInsertChange(String name, T value) {
return new ActorStateChange(name, value, ActorStateChangeKind.ADD);
}
private final <T> ActorStateChange<T> createUpdateChange(String name, T value) {
return new ActorStateChange(name, value, ActorStateChangeKind.UPDATE);
}
private final <T> ActorStateChange<T> createDeleteChange(String name) {
return new ActorStateChange(name, null, ActorStateChangeKind.REMOVE);
}
}